this repo has no description
1import { cubicOut, cubicInOut, linear } from '../easing/index.js';
2import { assign, split_css_unit, is_function } from '../internal/index.js';
3
4/**
5 * Animates a `blur` filter alongside an element's opacity.
6 *
7 * https://svelte.dev/docs/svelte-transition#blur
8 * @param {Element} node
9 * @param {import('./public').BlurParams} [params]
10 * @returns {import('./public').TransitionConfig}
11 */
12export function blur(
13 node,
14 { delay = 0, duration = 400, easing = cubicInOut, amount = 5, opacity = 0 } = {}
15) {
16 const style = getComputedStyle(node);
17 const target_opacity = +style.opacity;
18 const f = style.filter === 'none' ? '' : style.filter;
19 const od = target_opacity * (1 - opacity);
20 const [value, unit] = split_css_unit(amount);
21 return {
22 delay,
23 duration,
24 easing,
25 css: (_t, u) => `opacity: ${target_opacity - od * u}; filter: ${f} blur(${u * value}${unit});`
26 };
27}
28
29/**
30 * 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.
31 *
32 * https://svelte.dev/docs/svelte-transition#fade
33 * @param {Element} node
34 * @param {import('./public').FadeParams} [params]
35 * @returns {import('./public').TransitionConfig}
36 */
37export function fade(node, { delay = 0, duration = 400, easing = linear } = {}) {
38 const o = +getComputedStyle(node).opacity;
39 return {
40 delay,
41 duration,
42 easing,
43 css: (t) => `opacity: ${t * o}`
44 };
45}
46
47/**
48 * 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.
49 *
50 * https://svelte.dev/docs/svelte-transition#fly
51 * @param {Element} node
52 * @param {import('./public').FlyParams} [params]
53 * @returns {import('./public').TransitionConfig}
54 */
55export function fly(
56 node,
57 { delay = 0, duration = 400, easing = cubicOut, x = 0, y = 0, opacity = 0 } = {}
58) {
59 const style = getComputedStyle(node);
60 const target_opacity = +style.opacity;
61 const transform = style.transform === 'none' ? '' : style.transform;
62 const od = target_opacity * (1 - opacity);
63 const [xValue, xUnit] = split_css_unit(x);
64 const [yValue, yUnit] = split_css_unit(y);
65 return {
66 delay,
67 duration,
68 easing,
69 css: (t, u) => `
70 transform: ${transform} translate(${(1 - t) * xValue}${xUnit}, ${(1 - t) * yValue}${yUnit});
71 opacity: ${target_opacity - od * u}`
72 };
73}
74
75/**
76 * Slides an element in and out.
77 *
78 * https://svelte.dev/docs/svelte-transition#slide
79 * @param {Element} node
80 * @param {import('./public').SlideParams} [params]
81 * @returns {import('./public').TransitionConfig}
82 */
83export function slide(node, { delay = 0, duration = 400, easing = cubicOut, axis = 'y' } = {}) {
84 const style = getComputedStyle(node);
85 const opacity = +style.opacity;
86 const primary_property = axis === 'y' ? 'height' : 'width';
87 const primary_property_value = parseFloat(style[primary_property]);
88 const secondary_properties = axis === 'y' ? ['top', 'bottom'] : ['left', 'right'];
89 const capitalized_secondary_properties = secondary_properties.map(
90 (e) => `${e[0].toUpperCase()}${e.slice(1)}`
91 );
92 const padding_start_value = parseFloat(style[`padding${capitalized_secondary_properties[0]}`]);
93 const padding_end_value = parseFloat(style[`padding${capitalized_secondary_properties[1]}`]);
94 const margin_start_value = parseFloat(style[`margin${capitalized_secondary_properties[0]}`]);
95 const margin_end_value = parseFloat(style[`margin${capitalized_secondary_properties[1]}`]);
96 const border_width_start_value = parseFloat(
97 style[`border${capitalized_secondary_properties[0]}Width`]
98 );
99 const border_width_end_value = parseFloat(
100 style[`border${capitalized_secondary_properties[1]}Width`]
101 );
102 return {
103 delay,
104 duration,
105 easing,
106 css: (t) =>
107 'overflow: hidden;' +
108 `opacity: ${Math.min(t * 20, 1) * opacity};` +
109 `${primary_property}: ${t * primary_property_value}px;` +
110 `padding-${secondary_properties[0]}: ${t * padding_start_value}px;` +
111 `padding-${secondary_properties[1]}: ${t * padding_end_value}px;` +
112 `margin-${secondary_properties[0]}: ${t * margin_start_value}px;` +
113 `margin-${secondary_properties[1]}: ${t * margin_end_value}px;` +
114 `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` +
115 `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;`
116 };
117}
118
119/**
120 * 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.
121 *
122 * https://svelte.dev/docs/svelte-transition#scale
123 * @param {Element} node
124 * @param {import('./public').ScaleParams} [params]
125 * @returns {import('./public').TransitionConfig}
126 */
127export function scale(
128 node,
129 { delay = 0, duration = 400, easing = cubicOut, start = 0, opacity = 0 } = {}
130) {
131 const style = getComputedStyle(node);
132 const target_opacity = +style.opacity;
133 const transform = style.transform === 'none' ? '' : style.transform;
134 const sd = 1 - start;
135 const od = target_opacity * (1 - opacity);
136 return {
137 delay,
138 duration,
139 easing,
140 css: (_t, u) => `
141 transform: ${transform} scale(${1 - sd * u});
142 opacity: ${target_opacity - od * u}
143 `
144 };
145}
146
147/**
148 * 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 `<path>` and `<polyline>`.
149 *
150 * https://svelte.dev/docs/svelte-transition#draw
151 * @param {SVGElement & { getTotalLength(): number }} node
152 * @param {import('./public').DrawParams} [params]
153 * @returns {import('./public').TransitionConfig}
154 */
155export function draw(node, { delay = 0, speed, duration, easing = cubicInOut } = {}) {
156 let len = node.getTotalLength();
157 const style = getComputedStyle(node);
158 if (style.strokeLinecap !== 'butt') {
159 len += parseInt(style.strokeWidth);
160 }
161 if (duration === undefined) {
162 if (speed === undefined) {
163 duration = 800;
164 } else {
165 duration = len / speed;
166 }
167 } else if (typeof duration === 'function') {
168 duration = duration(len);
169 }
170 return {
171 delay,
172 duration,
173 easing,
174 css: (_, u) => `
175 stroke-dasharray: ${len};
176 stroke-dashoffset: ${u * len};
177 `
178 };
179}
180
181/**
182 * The `crossfade` function creates a pair of [transitions](https://svelte.dev/docs#template-syntax-element-directives-transition-fn) called `send` and `receive`. When an element is 'sent', it looks for a corresponding element being 'received', and generates a transition that transforms the element to its counterpart's position and fades it out. When an element is 'received', the reverse happens. If there is no counterpart, the `fallback` transition is used.
183 *
184 * https://svelte.dev/docs/svelte-transition#crossfade
185 * @param {import('./public').CrossfadeParams & {
186 * fallback?: (node: Element, params: import('./public').CrossfadeParams, intro: boolean) => import('./public').TransitionConfig;
187 * }} params
188 * @returns {[(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig, (node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig]}
189 */
190export function crossfade({ fallback, ...defaults }) {
191 /** @type {Map<any, Element>} */
192 const to_receive = new Map();
193 /** @type {Map<any, Element>} */
194 const to_send = new Map();
195 /**
196 * @param {Element} from_node
197 * @param {Element} node
198 * @param {import('./public').CrossfadeParams} params
199 * @returns {import('./public').TransitionConfig}
200 */
201 function crossfade(from_node, node, params) {
202 const {
203 delay = 0,
204 duration = (d) => Math.sqrt(d) * 30,
205 easing = cubicOut
206 } = assign(assign({}, defaults), params);
207 const from = from_node.getBoundingClientRect();
208 const to = node.getBoundingClientRect();
209 const dx = from.left - to.left;
210 const dy = from.top - to.top;
211 const dw = from.width / to.width;
212 const dh = from.height / to.height;
213 const d = Math.sqrt(dx * dx + dy * dy);
214 const style = getComputedStyle(node);
215 const transform = style.transform === 'none' ? '' : style.transform;
216 const opacity = +style.opacity;
217 return {
218 delay,
219 duration: is_function(duration) ? duration(d) : duration,
220 easing,
221 css: (t, u) => `
222 opacity: ${t * opacity};
223 transform-origin: top left;
224 transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${
225 t + (1 - t) * dh
226 });
227 `
228 };
229 }
230
231 /**
232 * @param {Map<any, Element>} items
233 * @param {Map<any, Element>} counterparts
234 * @param {boolean} intro
235 * @returns {(node: any, params: import('./public').CrossfadeParams & { key: any; }) => () => import('./public').TransitionConfig}
236 */
237 function transition(items, counterparts, intro) {
238 return (node, params) => {
239 items.set(params.key, node);
240 return () => {
241 if (counterparts.has(params.key)) {
242 const other_node = counterparts.get(params.key);
243 counterparts.delete(params.key);
244 return crossfade(other_node, node, params);
245 }
246 // if the node is disappearing altogether
247 // (i.e. wasn't claimed by the other list)
248 // then we need to supply an outro
249 items.delete(params.key);
250 return fallback && fallback(node, params, intro);
251 };
252 };
253 }
254 return [transition(to_send, to_receive, false), transition(to_receive, to_send, true)];
255}