diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index 7b7485d31a..d66a421c07 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -7,6 +7,48 @@ function cubic_out(t) { return f * f * f + 1.0; } +/** + * https://svelte.dev/docs/svelte-easing + * @param {number} t + * @returns {number} + */ +export function cubic_in_out(t) { + return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0; +} + +/** @param {number | string} value + * @returns {[number, string]} + */ +export function split_css_unit(value) { + const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); + return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px']; +} + +/** + * Animates a `blur` filter alongside an element's opacity. + * + * https://svelte.dev/docs/svelte-transition#blur + * @param {Element} node + * @param {import('./public').BlurParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function blur( + node, + { delay = 0, duration = 400, easing = cubic_in_out, amount = 5, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const f = style.filter === 'none' ? '' : style.filter; + const od = target_opacity * (1 - opacity); + const [value, unit] = split_css_unit(amount); + return { + delay, + duration, + easing, + css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * value}${unit});` + }; +} + /** * Animates the opacity of an element from 0 to the current opacity for `in` transitions and from the current opacity to 0 for `out` transitions. * @@ -25,6 +67,34 @@ export function fade(node, { delay = 0, duration = 400, easing = linear } = {}) }; } +/** + * Animates the x and y positions and the opacity of an element. `in` transitions animate from the provided values, passed as parameters to the element's default values. `out` transitions animate from the element's default values to the provided values. + * + * https://svelte.dev/docs/svelte-transition#fly + * @param {Element} node + * @param {import('./public').FlyParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function fly( + node, + { delay = 0, duration = 400, easing = cubic_out, x = 0, y = 0, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const transform = style.transform === 'none' ? '' : style.transform; + const od = target_opacity * (1 - opacity); + const [xValue, xUnit] = split_css_unit(x); + const [yValue, yUnit] = split_css_unit(y); + return { + delay, + duration, + easing, + css: (t, u) => ` + transform: ${transform} translate(${(1 - t) * xValue}${xUnit}, ${(1 - t) * yValue}${yUnit}); + opacity: ${target_opacity - od * u}` + }; +} + /** * Slides an element in and out. * @@ -69,6 +139,68 @@ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axi }; } +/** + * Animates the opacity and scale of an element. `in` transitions animate from an element's current (default) values to the provided values, passed as parameters. `out` transitions animate from the provided values to an element's default values. + * + * https://svelte.dev/docs/svelte-transition#scale + * @param {Element} node + * @param {import('./public').ScaleParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function scale( + node, + { delay = 0, duration = 400, easing = cubic_out, start = 0, opacity = 0 } = {} +) { + const style = getComputedStyle(node); + const target_opacity = +style.opacity; + const transform = style.transform === 'none' ? '' : style.transform; + const sd = 1 - start; + const od = target_opacity * (1 - opacity); + return { + delay, + duration, + easing, + css: (_t, u) => ` + transform: ${transform} scale(${1 - sd * u}); + opacity: ${target_opacity - od * u} + ` + }; +} + +/** + * Animates the stroke of an SVG element, like a snake in a tube. `in` transitions begin with the path invisible and draw the path to the screen over time. `out` transitions start in a visible state and gradually erase the path. `draw` only works with elements that have a `getTotalLength` method, like `` and ``. + * + * https://svelte.dev/docs/svelte-transition#draw + * @param {SVGElement & { getTotalLength(): number }} node + * @param {import('./public').DrawParams} [params] + * @returns {import('./public').TransitionConfig} + */ +export function draw(node, { delay = 0, speed, duration, easing = cubic_in_out } = {}) { + let len = node.getTotalLength(); + const style = getComputedStyle(node); + if (style.strokeLinecap !== 'butt') { + len += parseInt(style.strokeWidth); + } + if (duration === undefined) { + if (speed === undefined) { + duration = 800; + } else { + duration = len / speed; + } + } else if (typeof duration === 'function') { + duration = duration(len); + } + return { + delay, + duration, + easing, + css: (_, u) => ` + stroke-dasharray: ${len}; + stroke-dashoffset: ${u * len}; + ` + }; +} + /** * @template T * @template S