import { identity as linear, noop } from './utils.js'; import { now } from './environment.js'; import { loop } from './loop.js'; import { create_rule, delete_rule } from './style_manager.js'; /** * @param {Element & ElementCSSInlineStyle} node * @param {import('./private.js').PositionRect} from * @param {import('./private.js').AnimationFn} fn */ export function create_animation(node, from, fn, params) { if (!from) return noop; const to = node.getBoundingClientRect(); if ( from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom ) return noop; const { delay = 0, duration = 300, easing = linear, // @ts-ignore todo: should this be separated from destructuring? Or start/end added to public api and documentation? start: start_time = now() + delay, // @ts-ignore todo: end = start_time + duration, tick = noop, css } = fn(node, { from, to }, params); let running = true; let started = false; let name; /** @returns {void} */ function start() { if (css) { name = create_rule(node, 0, 1, duration, delay, easing, css); } if (!delay) { started = true; } } /** @returns {void} */ function stop() { if (css) delete_rule(node, name); running = false; } loop((now) => { if (!started && now >= start_time) { started = true; } if (started && now >= end) { tick(1, 0); stop(); } if (!running) { return false; } if (started) { const p = now - start_time; const t = 0 + 1 * easing(p / duration); tick(t, 1 - t); } return true; }); start(); tick(0, 1); return stop; } /** * @param {Element & ElementCSSInlineStyle} node * @returns {void} */ export function fix_position(node) { const style = getComputedStyle(node); if (style.position !== 'absolute' && style.position !== 'fixed') { const { width, height } = style; const a = node.getBoundingClientRect(); node.style.position = 'absolute'; node.style.width = width; node.style.height = height; add_transform(node, a); } } /** * @param {Element & ElementCSSInlineStyle} node * @param {import('./private.js').PositionRect} a * @returns {void} */ export function add_transform(node, a) { const b = node.getBoundingClientRect(); if (a.left !== b.left || a.top !== b.top) { const style = getComputedStyle(node); const transform = style.transform === 'none' ? '' : style.transform; node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`; } }