motion 10.1.3 → 10.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +1 -0
- package/dist/main.cjs.js +4 -0
- package/dist/main.es.js +2 -0
- package/dist/motion.min.js +1 -1
- package/dist/motion.umd.js +524 -189
- package/dist/size-animate-dom.js +1 -1
- package/dist/size-animate-style.js +1 -1
- package/dist/size-react.js +1 -1
- package/dist/size-spring.js +1 -1
- package/dist/size-timeline-dom.js +1 -1
- package/dist/size-webpack-animate.js +1 -1
- package/dist/targets/dom/animate-style.cjs.js +136 -111
- package/dist/targets/dom/animate-style.es.js +139 -114
- package/dist/targets/dom/animate.cjs.js +16 -3
- package/dist/targets/dom/animate.es.js +17 -4
- package/dist/targets/dom/data.cjs.js +4 -2
- package/dist/targets/dom/data.es.js +4 -2
- package/dist/targets/dom/style.cjs.js +1 -1
- package/dist/targets/dom/style.es.js +2 -2
- package/dist/targets/dom/timeline/index.cjs.js +17 -7
- package/dist/targets/dom/timeline/index.es.js +18 -8
- package/dist/targets/dom/timeline/utils/calc-time.cjs.js +3 -1
- package/dist/targets/dom/timeline/utils/calc-time.es.js +3 -1
- package/dist/targets/dom/utils/apply.cjs.js +4 -8
- package/dist/targets/dom/utils/apply.es.js +3 -7
- package/dist/targets/dom/utils/controls.cjs.js +6 -2
- package/dist/targets/dom/utils/controls.es.js +6 -2
- package/dist/targets/dom/utils/css-var.cjs.js +2 -2
- package/dist/targets/dom/utils/css-var.es.js +3 -3
- package/dist/targets/dom/utils/easing.cjs.js +7 -2
- package/dist/targets/dom/utils/easing.es.js +7 -3
- package/dist/targets/dom/utils/feature-detection.cjs.js +4 -4
- package/dist/targets/dom/utils/feature-detection.es.js +4 -4
- package/dist/targets/dom/utils/get-style-name.cjs.js +13 -0
- package/dist/targets/dom/utils/get-style-name.es.js +9 -0
- package/dist/targets/dom/utils/keyframes.cjs.js +2 -4
- package/dist/targets/dom/utils/keyframes.es.js +2 -4
- package/dist/targets/dom/utils/options.cjs.js +1 -1
- package/dist/targets/dom/utils/options.es.js +1 -1
- package/dist/targets/dom/utils/stop-animation.cjs.js +2 -0
- package/dist/targets/dom/utils/stop-animation.es.js +2 -0
- package/dist/targets/dom/utils/transforms.cjs.js +10 -7
- package/dist/targets/dom/utils/transforms.es.js +10 -7
- package/dist/targets/js/{animate-number.cjs.js → NumberAnimation.cjs.js} +42 -29
- package/dist/targets/js/{animate-number.es.js → NumberAnimation.es.js} +43 -29
- package/dist/targets/js/easing/glide/generator.cjs.js +99 -0
- package/dist/targets/js/easing/glide/generator.es.js +95 -0
- package/dist/targets/js/easing/glide/index.cjs.js +10 -0
- package/dist/targets/js/easing/glide/index.es.js +6 -0
- package/dist/targets/js/easing/spring/generator.cjs.js +64 -0
- package/dist/targets/js/easing/spring/generator.es.js +57 -0
- package/dist/targets/js/easing/spring/index.cjs.js +10 -0
- package/dist/targets/js/easing/spring/index.es.js +6 -0
- package/dist/targets/js/easing/utils/create-generator-easing.cjs.js +71 -0
- package/dist/targets/js/easing/utils/create-generator-easing.es.js +67 -0
- package/dist/targets/js/easing/{get-function.cjs.js → utils/get-function.cjs.js} +3 -3
- package/dist/targets/js/easing/{get-function.es.js → utils/get-function.es.js} +3 -3
- package/dist/targets/js/easing/utils/has-reached-target.cjs.js +10 -0
- package/dist/targets/js/easing/utils/has-reached-target.es.js +6 -0
- package/dist/targets/js/easing/utils/pregenerate-keyframes.cjs.js +34 -0
- package/dist/targets/js/easing/utils/pregenerate-keyframes.es.js +30 -0
- package/dist/targets/react/hooks/use-animation.cjs.js +5 -2
- package/dist/targets/react/hooks/use-animation.es.js +5 -2
- package/dist/targets/react/utils/keyframes.cjs.js +5 -3
- package/dist/targets/react/utils/keyframes.es.js +6 -4
- package/dist/utils/is-number.cjs.js +7 -0
- package/dist/utils/is-number.es.js +3 -0
- package/dist/utils/stagger.cjs.js +3 -2
- package/dist/utils/stagger.es.js +3 -2
- package/dist/utils/velocity-per-second.cjs.js +15 -0
- package/dist/utils/velocity-per-second.es.js +11 -0
- package/package.json +8 -14
- package/types/index.d.ts +2 -0
- package/types/targets/dom/animate-style.d.ts +2 -2
- package/types/targets/dom/data.d.ts +1 -7
- package/types/targets/dom/style.d.ts +1 -1
- package/types/targets/dom/types.d.ts +24 -18
- package/types/targets/dom/utils/apply.d.ts +3 -2
- package/types/targets/dom/utils/controls.d.ts +3 -3
- package/types/targets/dom/utils/easing.d.ts +2 -1
- package/types/targets/dom/utils/get-style-name.d.ts +1 -0
- package/types/targets/dom/utils/keyframes.d.ts +1 -1
- package/types/targets/dom/utils/stop-animation.d.ts +1 -1
- package/types/targets/dom/utils/transforms.d.ts +2 -2
- package/types/targets/js/{animate-number.d.ts → NumberAnimation.d.ts} +2 -3
- package/types/targets/js/easing/glide/generator.d.ts +5 -0
- package/types/targets/js/easing/glide/index.d.ts +2 -0
- package/types/targets/js/easing/glide/types.d.ts +14 -0
- package/types/targets/js/easing/spring/generator.d.ts +6 -0
- package/types/targets/js/easing/spring/index.d.ts +2 -0
- package/types/targets/js/easing/spring/types.d.ts +10 -0
- package/types/targets/js/easing/utils/create-generator-easing.d.ts +3 -0
- package/types/targets/js/easing/{get-function.d.ts → utils/get-function.d.ts} +2 -2
- package/types/targets/js/easing/utils/has-reached-target.d.ts +1 -0
- package/types/targets/js/easing/utils/pregenerate-keyframes.d.ts +7 -0
- package/types/targets/js/types.d.ts +11 -0
- package/types/utils/is-number.d.ts +1 -0
- package/types/generators/index.d.ts +0 -2
- package/types/generators/spring/create.d.ts +0 -13
- package/types/generators/spring/index.d.ts +0 -2
- package/types/generators/types.d.ts +0 -7
package/dist/motion.umd.js
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
function getAnimationData(element) {
|
|
9
9
|
if (!data.has(element)) {
|
|
10
10
|
data.set(element, {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
transforms: [],
|
|
12
|
+
animations: {},
|
|
13
|
+
generators: {},
|
|
14
|
+
prevGeneratorState: {},
|
|
13
15
|
});
|
|
14
16
|
}
|
|
15
17
|
return data.get(element);
|
|
@@ -60,7 +62,7 @@
|
|
|
60
62
|
},
|
|
61
63
|
skew: rotation,
|
|
62
64
|
};
|
|
63
|
-
const
|
|
65
|
+
const transformDefinitions = new Map();
|
|
64
66
|
const asTransformCssVar = (name) => `--motion-${name}`;
|
|
65
67
|
/**
|
|
66
68
|
* Generate a list of every possible transform key
|
|
@@ -69,7 +71,7 @@
|
|
|
69
71
|
order.forEach((name) => {
|
|
70
72
|
axes.forEach((axis) => {
|
|
71
73
|
transforms.push(name + axis);
|
|
72
|
-
|
|
74
|
+
transformDefinitions.set(asTransformCssVar(name + axis), baseTransformProperties[name]);
|
|
73
75
|
});
|
|
74
76
|
});
|
|
75
77
|
/**
|
|
@@ -82,11 +84,14 @@
|
|
|
82
84
|
const transformLookup = new Set(transforms);
|
|
83
85
|
const isTransform = (name) => transformLookup.has(name);
|
|
84
86
|
const addTransformToElement = (element, name) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
// Map x to translateX etc
|
|
88
|
+
if (transformAlias[name])
|
|
89
|
+
name = transformAlias[name];
|
|
90
|
+
const { transforms } = getAnimationData(element);
|
|
91
|
+
addUniqueItem(transforms, name);
|
|
92
|
+
element.style.transform = buildTransformTemplate(transforms);
|
|
88
93
|
};
|
|
89
|
-
const buildTransformTemplate = (
|
|
94
|
+
const buildTransformTemplate = (transforms) => transforms
|
|
90
95
|
.sort(compareTransformOrder)
|
|
91
96
|
.reduce(transformListToString, "")
|
|
92
97
|
.trim();
|
|
@@ -99,8 +104,8 @@
|
|
|
99
104
|
return;
|
|
100
105
|
registeredProperties.add(name);
|
|
101
106
|
try {
|
|
102
|
-
const { syntax, initialValue } =
|
|
103
|
-
?
|
|
107
|
+
const { syntax, initialValue } = transformDefinitions.has(name)
|
|
108
|
+
? transformDefinitions.get(name)
|
|
104
109
|
: {};
|
|
105
110
|
CSS.registerProperty({
|
|
106
111
|
name,
|
|
@@ -114,20 +119,12 @@
|
|
|
114
119
|
|
|
115
120
|
const ms = (seconds) => seconds * 1000;
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
// Suppress error thrown by WAAPI
|
|
119
|
-
try {
|
|
120
|
-
/**
|
|
121
|
-
* commitStyles has overhead so we only want to commit and cancel
|
|
122
|
-
*/
|
|
123
|
-
animation.playState !== "finished" && animation.commitStyles();
|
|
124
|
-
animation.cancel();
|
|
125
|
-
}
|
|
126
|
-
catch (e) { }
|
|
127
|
-
}
|
|
122
|
+
const isNumber = (value) => typeof value === "number";
|
|
128
123
|
|
|
129
|
-
const isCubicBezier = (easing) => Array.isArray(easing) &&
|
|
130
|
-
const isEasingList = (easing) => Array.isArray(easing) &&
|
|
124
|
+
const isCubicBezier = (easing) => Array.isArray(easing) && isNumber(easing[0]);
|
|
125
|
+
const isEasingList = (easing) => Array.isArray(easing) && !isNumber(easing[0]);
|
|
126
|
+
const isCustomEasing = (easing) => typeof easing === "object" &&
|
|
127
|
+
Boolean(easing.createAnimation);
|
|
131
128
|
const convertEasing = (easing) => isCubicBezier(easing) ? cubicBezierAsString(easing) : easing;
|
|
132
129
|
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
133
130
|
|
|
@@ -148,21 +145,17 @@
|
|
|
148
145
|
finished: () => Boolean(testAnimation({ opacity: [0, 1] }).finished),
|
|
149
146
|
};
|
|
150
147
|
const results = {};
|
|
151
|
-
const supports =
|
|
152
|
-
|
|
148
|
+
const supports = {};
|
|
149
|
+
for (const key in featureTests) {
|
|
150
|
+
supports[key] = () => {
|
|
153
151
|
if (results[key] === undefined)
|
|
154
152
|
results[key] = featureTests[key]();
|
|
155
153
|
return results[key];
|
|
156
154
|
};
|
|
157
|
-
|
|
158
|
-
}, {});
|
|
155
|
+
}
|
|
159
156
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
};
|
|
163
|
-
const createStyleRenderer = (element, name) => {
|
|
164
|
-
return (latest) => (element.style[name] = latest);
|
|
165
|
-
};
|
|
157
|
+
const cssVariableRenderer = (element, name) => (latest) => element.style.setProperty(name, latest);
|
|
158
|
+
const styleRenderer = (element, name) => (latest) => (element.style[name] = latest);
|
|
166
159
|
|
|
167
160
|
const defaults = {
|
|
168
161
|
duration: 0.3,
|
|
@@ -346,10 +339,8 @@
|
|
|
346
339
|
};
|
|
347
340
|
}
|
|
348
341
|
|
|
349
|
-
class
|
|
350
|
-
constructor(output, keyframes,
|
|
351
|
-
// TODO Merge in defaults
|
|
352
|
-
{ easing = defaults.easing, duration = defaults.duration, delay = defaults.delay, endDelay = defaults.endDelay, offset, repeat = defaults.repeat, direction = "normal", }) {
|
|
342
|
+
class NumberAnimation {
|
|
343
|
+
constructor(output, keyframes = [0, 1], { easing = defaults.easing, duration = defaults.duration, delay = defaults.delay, endDelay = defaults.endDelay, repeat = defaults.repeat, offset, direction = "normal", } = {}) {
|
|
353
344
|
this.startTime = 0;
|
|
354
345
|
this.rate = 1;
|
|
355
346
|
this.t = 0;
|
|
@@ -360,50 +351,69 @@
|
|
|
360
351
|
this.reject = reject;
|
|
361
352
|
});
|
|
362
353
|
const totalDuration = duration * (repeat + 1);
|
|
354
|
+
/**
|
|
355
|
+
* We don't currently support custom easing (spring, glide etc) in NumberAnimation
|
|
356
|
+
* (although this is completely possible), so this will have been hydrated by
|
|
357
|
+
* animateStyle.
|
|
358
|
+
*/
|
|
359
|
+
if (isCustomEasing(easing))
|
|
360
|
+
easing = "ease";
|
|
363
361
|
const interpolate = slowInterpolateNumbers(keyframes, offset, isEasingList(easing)
|
|
364
362
|
? easing.map(getEasingFunction)
|
|
365
363
|
: getEasingFunction(easing));
|
|
366
364
|
this.tick = (timestamp) => {
|
|
367
|
-
if (this.
|
|
368
|
-
const latest = interpolate(1);
|
|
369
|
-
output(latest);
|
|
370
|
-
this.resolve(latest);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
if (this.pauseTime) {
|
|
365
|
+
if (this.pauseTime)
|
|
374
366
|
timestamp = this.pauseTime;
|
|
375
|
-
}
|
|
376
367
|
let t = (timestamp - this.startTime) * this.rate;
|
|
377
368
|
this.t = t;
|
|
378
369
|
// Convert to seconds
|
|
379
370
|
t /= 1000;
|
|
380
371
|
// Rebase on delay
|
|
381
372
|
t = Math.max(t - delay, 0);
|
|
373
|
+
/**
|
|
374
|
+
* If this animation has finished, set the current time
|
|
375
|
+
* to the total duration.
|
|
376
|
+
*/
|
|
377
|
+
if (this.playState === "finished")
|
|
378
|
+
t = totalDuration;
|
|
379
|
+
/**
|
|
380
|
+
* Get the current progress (0-1) of the animation. If t is >
|
|
381
|
+
* than duration we'll get values like 2.5 (midway through the
|
|
382
|
+
* third iteration)
|
|
383
|
+
*/
|
|
382
384
|
const progress = t / duration;
|
|
383
385
|
// TODO progress += iterationStart
|
|
386
|
+
/**
|
|
387
|
+
* Get the current iteration (0 indexed). For instance the floor of
|
|
388
|
+
* 2.5 is 2.
|
|
389
|
+
*/
|
|
384
390
|
let currentIteration = Math.floor(progress);
|
|
391
|
+
/**
|
|
392
|
+
* Get the current progress of the iteration by taking the remainder
|
|
393
|
+
* so 2.5 is 0.5 through iteration 2
|
|
394
|
+
*/
|
|
385
395
|
let iterationProgress = progress % 1.0;
|
|
386
396
|
if (!iterationProgress && progress >= 1) {
|
|
387
397
|
iterationProgress = 1;
|
|
388
398
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
399
|
+
/**
|
|
400
|
+
* If iteration progress is 1 we count that as the end
|
|
401
|
+
* of the previous iteration.
|
|
402
|
+
*/
|
|
403
|
+
iterationProgress === 1 && currentIteration--;
|
|
404
|
+
/**
|
|
405
|
+
* Reverse progress if we're not running in "normal" direction
|
|
406
|
+
*/
|
|
393
407
|
const iterationIsOdd = currentIteration % 2;
|
|
394
408
|
if (direction === "reverse" ||
|
|
395
409
|
(direction === "alternate" && iterationIsOdd) ||
|
|
396
410
|
(direction === "alternate-reverse" && !iterationIsOdd)) {
|
|
397
411
|
iterationProgress = 1 - iterationProgress;
|
|
398
412
|
}
|
|
399
|
-
const
|
|
400
|
-
const interpolationProgress = interpolationIsFinished
|
|
401
|
-
? 1
|
|
402
|
-
: Math.min(iterationProgress, 1);
|
|
403
|
-
const latest = interpolate(interpolationProgress);
|
|
413
|
+
const latest = interpolate(t >= totalDuration ? 1 : Math.min(iterationProgress, 1));
|
|
404
414
|
output(latest);
|
|
405
|
-
const
|
|
406
|
-
if (
|
|
415
|
+
const isAnimationFinished = this.playState === "finished" || t >= totalDuration + endDelay;
|
|
416
|
+
if (isAnimationFinished) {
|
|
407
417
|
this.playState = "finished";
|
|
408
418
|
this.resolve(latest);
|
|
409
419
|
}
|
|
@@ -462,9 +472,16 @@
|
|
|
462
472
|
this.rate = rate;
|
|
463
473
|
}
|
|
464
474
|
}
|
|
465
|
-
|
|
466
|
-
|
|
475
|
+
|
|
476
|
+
function hydrateKeyframes(keyframes, readInitialValue) {
|
|
477
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
478
|
+
if (keyframes[i] === null) {
|
|
479
|
+
keyframes[i] = i ? keyframes[i - 1] : readInitialValue();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return keyframes;
|
|
467
483
|
}
|
|
484
|
+
const keyframesList = (keyframes) => Array.isArray(keyframes) ? keyframes : [keyframes];
|
|
468
485
|
|
|
469
486
|
const style = {
|
|
470
487
|
get: (element, name) => {
|
|
@@ -472,7 +489,7 @@
|
|
|
472
489
|
? element.style.getPropertyValue(name)
|
|
473
490
|
: getComputedStyle(element)[name];
|
|
474
491
|
if (!value && value !== 0) {
|
|
475
|
-
const definition =
|
|
492
|
+
const definition = transformDefinitions.get(name);
|
|
476
493
|
if (definition)
|
|
477
494
|
value = definition.initialValue;
|
|
478
495
|
}
|
|
@@ -480,155 +497,188 @@
|
|
|
480
497
|
},
|
|
481
498
|
};
|
|
482
499
|
|
|
483
|
-
function
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
500
|
+
function getStyleName(key) {
|
|
501
|
+
if (transformAlias[key])
|
|
502
|
+
key = transformAlias[key];
|
|
503
|
+
return isTransform(key) ? asTransformCssVar(key) : key;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function stopAnimation(animation) {
|
|
507
|
+
if (!animation)
|
|
508
|
+
return;
|
|
509
|
+
// Suppress error thrown by WAAPI
|
|
510
|
+
try {
|
|
511
|
+
/**
|
|
512
|
+
* commitStyles has overhead so we only want to commit and cancel
|
|
513
|
+
*/
|
|
514
|
+
animation.playState !== "finished" && animation.commitStyles();
|
|
515
|
+
animation.cancel();
|
|
488
516
|
}
|
|
489
|
-
|
|
517
|
+
catch (e) { }
|
|
490
518
|
}
|
|
491
|
-
const keyframesList = (keyframes) => Array.isArray(keyframes) ? keyframes : [keyframes];
|
|
492
519
|
|
|
493
|
-
function animateStyle(element,
|
|
520
|
+
function animateStyle(element, key, keyframesDefinition, options = {}) {
|
|
521
|
+
let animation;
|
|
494
522
|
let { duration = defaults.duration, delay = defaults.delay, endDelay = defaults.endDelay, repeat = defaults.repeat, easing = defaults.easing, direction, offset, allowWebkitAcceleration = false, } = options;
|
|
495
523
|
const data = getAnimationData(element);
|
|
496
524
|
let canAnimateNatively = supports.waapi();
|
|
497
525
|
let render = noop;
|
|
498
|
-
const valueIsTransform = isTransform(
|
|
526
|
+
const valueIsTransform = isTransform(key);
|
|
499
527
|
/**
|
|
500
528
|
* If this is an individual transform, we need to map its
|
|
501
529
|
* key to a CSS variable and update the element's transform style
|
|
502
530
|
*/
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
name = transformAlias[name];
|
|
506
|
-
addTransformToElement(element, name);
|
|
507
|
-
name = asTransformCssVar(name);
|
|
508
|
-
}
|
|
531
|
+
valueIsTransform && addTransformToElement(element, key);
|
|
532
|
+
const name = getStyleName(key);
|
|
509
533
|
/**
|
|
510
534
|
* Get definition of value, this will be used to convert numerical
|
|
511
535
|
* keyframes into the default value type.
|
|
512
536
|
*/
|
|
513
|
-
const definition =
|
|
537
|
+
const definition = transformDefinitions.get(name);
|
|
514
538
|
/**
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
*
|
|
519
|
-
* check so it can correctly read the underlying value.
|
|
520
|
-
* Should make a test for this.
|
|
539
|
+
* Stop the current animation, if any. Because this will trigger
|
|
540
|
+
* commitStyles (DOM writes) and we might later trigger DOM reads,
|
|
541
|
+
* this is fired now and we return a factory function to create
|
|
542
|
+
* the actual animation that can get called in batch,
|
|
521
543
|
*/
|
|
522
|
-
|
|
523
|
-
stopCurrentAnimation(data, name);
|
|
524
|
-
/**
|
|
525
|
-
* If this is a CSS variable we need to register it with the browser
|
|
526
|
-
* before it can be animated natively. We also set it with setProperty
|
|
527
|
-
* rather than directly onto the element.style object.
|
|
528
|
-
*/
|
|
529
|
-
if (isCssVar(name)) {
|
|
530
|
-
render = createCssVariableRenderer(element, name);
|
|
531
|
-
if (supports.cssRegisterProperty()) {
|
|
532
|
-
registerCssVariable(name);
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
canAnimateNatively = false;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
render = createStyleRenderer(element, name);
|
|
540
|
-
}
|
|
541
|
-
let animation;
|
|
544
|
+
stopAnimation(data.animations[name]);
|
|
542
545
|
/**
|
|
543
|
-
*
|
|
544
|
-
* feature detects CSS.registerProperty but could check WAAPI too.
|
|
546
|
+
* Batchable factory function containing all DOM reads.
|
|
545
547
|
*/
|
|
546
|
-
|
|
548
|
+
return () => {
|
|
549
|
+
const readInitialValue = () => { var _a, _b; return (_b = (_a = style.get(element, name)) !== null && _a !== void 0 ? _a : definition === null || definition === void 0 ? void 0 : definition.initialValue) !== null && _b !== void 0 ? _b : 0; };
|
|
547
550
|
/**
|
|
548
|
-
*
|
|
549
|
-
*
|
|
551
|
+
* Replace null values with the previous keyframe value, or read
|
|
552
|
+
* it from the DOM if it's the first keyframe.
|
|
550
553
|
*/
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
554
|
+
let keyframes = hydrateKeyframes(keyframesList(keyframesDefinition), readInitialValue);
|
|
555
|
+
if (isCustomEasing(easing)) {
|
|
556
|
+
const custom = easing.createAnimation(keyframes, readInitialValue, valueIsTransform, name, data);
|
|
557
|
+
easing = custom.easing;
|
|
558
|
+
if (custom.keyframes !== undefined)
|
|
559
|
+
keyframes = custom.keyframes;
|
|
560
|
+
if (custom.duration !== undefined)
|
|
561
|
+
duration = custom.duration;
|
|
556
562
|
}
|
|
557
|
-
const animationOptions = {
|
|
558
|
-
delay: ms(delay),
|
|
559
|
-
duration: ms(duration),
|
|
560
|
-
endDelay: ms(endDelay),
|
|
561
|
-
easing: !isEasingList(easing) ? convertEasing(easing) : undefined,
|
|
562
|
-
direction,
|
|
563
|
-
iterations: repeat + 1,
|
|
564
|
-
fill: "both",
|
|
565
|
-
};
|
|
566
|
-
animation = element.animate({
|
|
567
|
-
[name]: keyframes,
|
|
568
|
-
offset,
|
|
569
|
-
easing: isEasingList(easing) ? easing.map(convertEasing) : undefined,
|
|
570
|
-
}, animationOptions);
|
|
571
563
|
/**
|
|
572
|
-
*
|
|
564
|
+
* If this is a CSS variable we need to register it with the browser
|
|
565
|
+
* before it can be animated natively. We also set it with setProperty
|
|
566
|
+
* rather than directly onto the element.style object.
|
|
573
567
|
*/
|
|
574
|
-
if (
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}
|
|
568
|
+
if (isCssVar(name)) {
|
|
569
|
+
render = cssVariableRenderer(element, name);
|
|
570
|
+
if (supports.cssRegisterProperty()) {
|
|
571
|
+
registerCssVariable(name);
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
canAnimateNatively = false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
render = styleRenderer(element, name);
|
|
579
579
|
}
|
|
580
|
-
const target = keyframes[keyframes.length - 1];
|
|
581
|
-
animation.finished.then(() => render(target)).catch(noop);
|
|
582
580
|
/**
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp?rev=281238#L1099
|
|
586
|
-
*
|
|
587
|
-
* This fixes Webkit's timing bugs, like accelerated animations falling
|
|
588
|
-
* out of sync with main thread animations and massive delays in starting
|
|
589
|
-
* accelerated animations in WKWebView.
|
|
581
|
+
* If we can animate this value with WAAPI, do so. Currently this only
|
|
582
|
+
* feature detects CSS.registerProperty but could check WAAPI too.
|
|
590
583
|
*/
|
|
591
|
-
if (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
584
|
+
if (canAnimateNatively) {
|
|
585
|
+
/**
|
|
586
|
+
* Convert numbers to default value types. Currently this only supports
|
|
587
|
+
* transforms but it could also support other value types.
|
|
588
|
+
*/
|
|
589
|
+
if (definition) {
|
|
590
|
+
keyframes = keyframes.map((value) => isNumber(value) ? definition.toDefaultUnit(value) : value);
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* If this browser doesn't support partial/implicit keyframes we need to
|
|
594
|
+
* explicitly provide one.
|
|
595
|
+
*/
|
|
596
|
+
if (!supports.partialKeyframes() && keyframes.length === 1) {
|
|
597
|
+
keyframes.unshift(readInitialValue());
|
|
598
|
+
}
|
|
599
|
+
const animationOptions = {
|
|
600
|
+
delay: ms(delay),
|
|
601
|
+
duration: ms(duration),
|
|
602
|
+
endDelay: ms(endDelay),
|
|
603
|
+
easing: !isEasingList(easing) ? convertEasing(easing) : undefined,
|
|
604
|
+
direction,
|
|
605
|
+
iterations: repeat + 1,
|
|
606
|
+
fill: "both",
|
|
607
|
+
};
|
|
608
|
+
animation = element.animate({
|
|
609
|
+
[name]: keyframes,
|
|
610
|
+
offset,
|
|
611
|
+
easing: isEasingList(easing) ? easing.map(convertEasing) : undefined,
|
|
612
|
+
}, animationOptions);
|
|
613
|
+
/**
|
|
614
|
+
* Polyfill finished Promise in browsers that don't support it
|
|
615
|
+
*/
|
|
616
|
+
if (!animation.finished) {
|
|
617
|
+
animation.finished = new Promise((resolve, reject) => {
|
|
618
|
+
animation.onfinish = resolve;
|
|
619
|
+
animation.oncancel = reject;
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
const target = keyframes[keyframes.length - 1];
|
|
623
|
+
animation.finished
|
|
624
|
+
.then(() => {
|
|
625
|
+
// Apply styles to target
|
|
626
|
+
render(target);
|
|
627
|
+
// Ensure fill modes don't persist
|
|
628
|
+
animation.cancel();
|
|
629
|
+
})
|
|
630
|
+
.catch(noop);
|
|
631
|
+
/**
|
|
632
|
+
* This forces Webkit to run animations on the main thread by exploiting
|
|
633
|
+
* this condition:
|
|
634
|
+
* https://trac.webkit.org/browser/webkit/trunk/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp?rev=281238#L1099
|
|
635
|
+
*
|
|
636
|
+
* This fixes Webkit's timing bugs, like accelerated animations falling
|
|
637
|
+
* out of sync with main thread animations and massive delays in starting
|
|
638
|
+
* accelerated animations in WKWebView.
|
|
639
|
+
*/
|
|
640
|
+
if (!allowWebkitAcceleration)
|
|
641
|
+
animation.playbackRate = 1.000001;
|
|
642
|
+
/**
|
|
643
|
+
* If we can't animate the value natively then we can fallback to the numbers-only
|
|
644
|
+
* polyfill for transforms. All keyframes must be numerical.
|
|
645
|
+
*/
|
|
597
646
|
}
|
|
647
|
+
else if (valueIsTransform && keyframes.every(isNumber)) {
|
|
648
|
+
/**
|
|
649
|
+
* If we only have a single keyframe, we need to create an initial keyframe by reading
|
|
650
|
+
* the current value from the DOM.
|
|
651
|
+
*/
|
|
652
|
+
if (keyframes.length === 1) {
|
|
653
|
+
keyframes.unshift(parseFloat(readInitialValue()));
|
|
654
|
+
}
|
|
655
|
+
if (definition) {
|
|
656
|
+
const applyStyle = render;
|
|
657
|
+
render = (v) => applyStyle(definition.toDefaultUnit(v));
|
|
658
|
+
}
|
|
659
|
+
animation = new NumberAnimation(render, keyframes, Object.assign(Object.assign({}, options), { duration,
|
|
660
|
+
easing }));
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
const target = keyframes[keyframes.length - 1];
|
|
664
|
+
render(definition && isNumber(target)
|
|
665
|
+
? definition.toDefaultUnit(target)
|
|
666
|
+
: target);
|
|
667
|
+
}
|
|
668
|
+
data.animations[name] = animation;
|
|
598
669
|
/**
|
|
599
|
-
*
|
|
600
|
-
* their default value type, so here we loop through and map
|
|
601
|
-
* them to numbers.
|
|
670
|
+
* When an animation finishes, delete the reference to the previous animation.
|
|
602
671
|
*/
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
animation
|
|
609
|
-
}
|
|
610
|
-
else {
|
|
611
|
-
const target = keyframes[keyframes.length - 1];
|
|
612
|
-
render(definition && typeof target === "number"
|
|
613
|
-
? definition.toDefaultUnit(target)
|
|
614
|
-
: target);
|
|
615
|
-
}
|
|
616
|
-
data.activeAnimations[name] = animation;
|
|
617
|
-
/**
|
|
618
|
-
* When an animation finishes, delete the reference to the previous animation.
|
|
619
|
-
*/
|
|
620
|
-
animation === null || animation === void 0 ? void 0 : animation.finished.then(() => (data.activeAnimations[name] = undefined)).catch(noop);
|
|
621
|
-
return animation;
|
|
622
|
-
}
|
|
623
|
-
function stopCurrentAnimation(data, name) {
|
|
624
|
-
if (data.activeAnimations[name]) {
|
|
625
|
-
stopAnimation(data.activeAnimations[name]);
|
|
626
|
-
data.activeAnimations[name] = undefined;
|
|
627
|
-
}
|
|
672
|
+
animation === null || animation === void 0 ? void 0 : animation.finished.then(() => {
|
|
673
|
+
data.animations[name] = undefined;
|
|
674
|
+
data.generators[name] = undefined;
|
|
675
|
+
data.prevGeneratorState[name] = undefined;
|
|
676
|
+
}).catch(noop);
|
|
677
|
+
return animation;
|
|
678
|
+
};
|
|
628
679
|
}
|
|
629
|
-
const isNumber = (value) => typeof value === "number";
|
|
630
680
|
|
|
631
|
-
const getOptions = (options, key) => options[key] ? Object.assign(Object.assign({}, options), options[key]) :
|
|
681
|
+
const getOptions = (options, key) => options[key] ? Object.assign(Object.assign({}, options), options[key]) : options;
|
|
632
682
|
|
|
633
683
|
function resolveElements(elements, selectorCache) {
|
|
634
684
|
var _a;
|
|
@@ -647,7 +697,11 @@
|
|
|
647
697
|
return Array.from(elements);
|
|
648
698
|
}
|
|
649
699
|
|
|
650
|
-
const
|
|
700
|
+
const createAnimation = (factory) => factory();
|
|
701
|
+
const createAnimations = (animationFactory, duration) => new Proxy({
|
|
702
|
+
animations: animationFactory.map(createAnimation).filter(Boolean),
|
|
703
|
+
duration,
|
|
704
|
+
}, controls);
|
|
651
705
|
/**
|
|
652
706
|
* TODO:
|
|
653
707
|
* Currently this returns the first animation, ideally it would return
|
|
@@ -694,7 +748,7 @@
|
|
|
694
748
|
|
|
695
749
|
function stagger(duration = 0.1, { start = 0, from = 0, easing } = {}) {
|
|
696
750
|
return (i, total) => {
|
|
697
|
-
const fromIndex =
|
|
751
|
+
const fromIndex = isNumber(from) ? from : getFromIndex(from, total);
|
|
698
752
|
const distance = Math.abs(fromIndex - i);
|
|
699
753
|
let delay = duration * distance;
|
|
700
754
|
if (easing) {
|
|
@@ -723,18 +777,31 @@
|
|
|
723
777
|
function animate(elements, keyframes, options = {}) {
|
|
724
778
|
var _a;
|
|
725
779
|
elements = resolveElements(elements);
|
|
726
|
-
const animations = [];
|
|
727
780
|
const numElements = elements.length;
|
|
781
|
+
/**
|
|
782
|
+
* Create and start new animations
|
|
783
|
+
*/
|
|
784
|
+
const animationFactories = [];
|
|
728
785
|
for (let i = 0; i < numElements; i++) {
|
|
729
786
|
const element = elements[i];
|
|
730
787
|
for (const key in keyframes) {
|
|
731
788
|
const valueOptions = getOptions(options, key);
|
|
732
789
|
valueOptions.delay = resolveOption(valueOptions.delay, i, numElements);
|
|
733
790
|
const animation = animateStyle(element, key, keyframes[key], valueOptions);
|
|
734
|
-
|
|
791
|
+
animationFactories.push(animation);
|
|
735
792
|
}
|
|
736
793
|
}
|
|
737
|
-
return
|
|
794
|
+
return createAnimations(animationFactories,
|
|
795
|
+
/**
|
|
796
|
+
* TODO:
|
|
797
|
+
* If easing is set to spring or glide, duration will be dynamically
|
|
798
|
+
* generated. Ideally we would dynamically generate this from
|
|
799
|
+
* animation.effect.getComputedTiming().duration but this isn't
|
|
800
|
+
* supported in iOS13 or our number polyfill. Perhaps it's possible
|
|
801
|
+
* to Proxy animations returned from animateStyle that has duration
|
|
802
|
+
* as a getter.
|
|
803
|
+
*/
|
|
804
|
+
(_a = options.duration) !== null && _a !== void 0 ? _a : defaults.duration);
|
|
738
805
|
}
|
|
739
806
|
|
|
740
807
|
/*! *****************************************************************************
|
|
@@ -766,7 +833,7 @@
|
|
|
766
833
|
|
|
767
834
|
function calcNextTime(current, next, prev, labels) {
|
|
768
835
|
var _a;
|
|
769
|
-
if (
|
|
836
|
+
if (isNumber(next)) {
|
|
770
837
|
return next;
|
|
771
838
|
}
|
|
772
839
|
else if (next.startsWith("-") || next.startsWith("+")) {
|
|
@@ -817,13 +884,14 @@
|
|
|
817
884
|
|
|
818
885
|
function timeline(definition, options = {}) {
|
|
819
886
|
var _a, _b;
|
|
820
|
-
const animations = [];
|
|
821
887
|
const animationDefinitions = createAnimationsFromTimeline(definition, options);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
888
|
+
/**
|
|
889
|
+
* Create and start animations
|
|
890
|
+
*/
|
|
891
|
+
const animationFactories = animationDefinitions
|
|
892
|
+
.map((definition) => animateStyle(...definition))
|
|
893
|
+
.filter(Boolean);
|
|
894
|
+
return createAnimations(animationFactories,
|
|
827
895
|
// Get the duration from the first animation definition
|
|
828
896
|
(_b = (_a = animationDefinitions[0]) === null || _a === void 0 ? void 0 : _a[3].duration) !== null && _b !== void 0 ? _b : defaults.duration);
|
|
829
897
|
}
|
|
@@ -868,10 +936,19 @@
|
|
|
868
936
|
const valueSequence = getValueSequence(key, elementSequence);
|
|
869
937
|
const valueKeyframes = keyframesList(keyframes[key]);
|
|
870
938
|
const valueOptions = getOptions(options, key);
|
|
871
|
-
|
|
939
|
+
let { duration = defaultOptions.duration || defaults.duration, easing = defaultOptions.easing || defaults.easing, } = valueOptions;
|
|
872
940
|
const delay = resolveOption(options.delay, elementIndex, numElements) || 0;
|
|
873
941
|
const startTime = currentTime + delay;
|
|
874
942
|
const targetTime = startTime + duration;
|
|
943
|
+
/**
|
|
944
|
+
*
|
|
945
|
+
*/
|
|
946
|
+
let { offset = defaultOffset(valueKeyframes.length) } = valueOptions;
|
|
947
|
+
/**
|
|
948
|
+
* If there's only one offset of 0, fill in a second with length 1
|
|
949
|
+
*
|
|
950
|
+
* TODO: Ensure there's a test that covers this removal
|
|
951
|
+
*/
|
|
875
952
|
if (offset.length === 1 && offset[0] === 0) {
|
|
876
953
|
offset[1] = 1;
|
|
877
954
|
}
|
|
@@ -959,8 +1036,266 @@
|
|
|
959
1036
|
return sequences[name];
|
|
960
1037
|
}
|
|
961
1038
|
|
|
1039
|
+
/*
|
|
1040
|
+
Convert velocity into velocity per second
|
|
1041
|
+
|
|
1042
|
+
@param [number]: Unit per frame
|
|
1043
|
+
@param [number]: Frame duration in ms
|
|
1044
|
+
*/
|
|
1045
|
+
function velocityPerSecond(velocity, frameDuration) {
|
|
1046
|
+
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
function hasReachedTarget(origin, target, current) {
|
|
1050
|
+
return ((origin < target && current >= target) ||
|
|
1051
|
+
(origin > target && current <= target));
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const defaultStiffness = 100.0;
|
|
1055
|
+
const defaultDamping = 10.0;
|
|
1056
|
+
const defaultMass = 1.0;
|
|
1057
|
+
const calcDampingRatio = (stiffness = defaultStiffness, damping = defaultDamping, mass = defaultMass) => damping / (2 * Math.sqrt(stiffness * mass));
|
|
1058
|
+
const calcAngularFreq = (undampedFreq, dampingRatio) => undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
1059
|
+
const createSpringGenerator = ({ stiffness = defaultStiffness, damping = defaultDamping, mass = defaultMass, from = 0, to = 1, velocity = 0.0, restSpeed = 2, restDistance = 0.5, } = {}) => {
|
|
1060
|
+
velocity = velocity ? velocity / 1000 : 0.0;
|
|
1061
|
+
const state = {
|
|
1062
|
+
done: false,
|
|
1063
|
+
value: from,
|
|
1064
|
+
target: to,
|
|
1065
|
+
velocity,
|
|
1066
|
+
hasReachedTarget: false,
|
|
1067
|
+
};
|
|
1068
|
+
const dampingRatio = calcDampingRatio(stiffness, damping, mass);
|
|
1069
|
+
const initialDelta = to - from;
|
|
1070
|
+
const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
|
|
1071
|
+
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
1072
|
+
let resolveSpring;
|
|
1073
|
+
if (dampingRatio < 1) {
|
|
1074
|
+
// Underdamped spring (bouncy)
|
|
1075
|
+
resolveSpring = (t) => to -
|
|
1076
|
+
Math.exp(-dampingRatio * undampedAngularFreq * t) *
|
|
1077
|
+
(((-velocity + dampingRatio * undampedAngularFreq * initialDelta) /
|
|
1078
|
+
angularFreq) *
|
|
1079
|
+
Math.sin(angularFreq * t) +
|
|
1080
|
+
initialDelta * Math.cos(angularFreq * t));
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
// Critically damped spring
|
|
1084
|
+
resolveSpring = (t) => to -
|
|
1085
|
+
Math.exp(-undampedAngularFreq * t) *
|
|
1086
|
+
(initialDelta + (velocity + undampedAngularFreq * initialDelta) * t);
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
next: (t) => {
|
|
1090
|
+
state.value = resolveSpring(t);
|
|
1091
|
+
state.velocity =
|
|
1092
|
+
t === 0 ? velocity : calcVelocity(resolveSpring, t, state.value);
|
|
1093
|
+
const isBelowVelocityThreshold = Math.abs(state.velocity) <= restSpeed;
|
|
1094
|
+
const isBelowDisplacementThreshold = Math.abs(to - state.value) <= restDistance;
|
|
1095
|
+
state.done = isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
|
1096
|
+
state.hasReachedTarget = hasReachedTarget(from, to, state.value);
|
|
1097
|
+
return state;
|
|
1098
|
+
},
|
|
1099
|
+
};
|
|
1100
|
+
};
|
|
1101
|
+
const sampleT = 5; // ms
|
|
1102
|
+
function calcVelocity(resolveValue, t, current) {
|
|
1103
|
+
const prevT = Math.max(t - sampleT, 0);
|
|
1104
|
+
return velocityPerSecond(current - resolveValue(prevT), 5);
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const timeStep = 10;
|
|
1108
|
+
const maxDuration = 10000;
|
|
1109
|
+
function pregenerateKeyframes(generator) {
|
|
1110
|
+
let overshootDuration = undefined;
|
|
1111
|
+
let timestamp = timeStep;
|
|
1112
|
+
let state = generator.next(0);
|
|
1113
|
+
const keyframes = [state.value];
|
|
1114
|
+
while (!state.done && timestamp < maxDuration) {
|
|
1115
|
+
state = generator.next(timestamp);
|
|
1116
|
+
keyframes.push(state.done ? state.target : state.value);
|
|
1117
|
+
if (overshootDuration === undefined && state.hasReachedTarget) {
|
|
1118
|
+
overshootDuration = timestamp;
|
|
1119
|
+
}
|
|
1120
|
+
timestamp += timeStep;
|
|
1121
|
+
}
|
|
1122
|
+
const duration = timestamp - timeStep;
|
|
1123
|
+
/**
|
|
1124
|
+
* If generating an animation that didn't actually move,
|
|
1125
|
+
* generate a second keyframe so we have an origin and target.
|
|
1126
|
+
*/
|
|
1127
|
+
if (keyframes.length === 1)
|
|
1128
|
+
keyframes.push(state.value);
|
|
1129
|
+
return {
|
|
1130
|
+
keyframes,
|
|
1131
|
+
duration: duration / 1000,
|
|
1132
|
+
overshootDuration: (overshootDuration !== null && overshootDuration !== void 0 ? overshootDuration : duration) / 1000,
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
function createGeneratorEasing(createGenerator) {
|
|
1137
|
+
const keyframesCache = new WeakMap();
|
|
1138
|
+
return (options = {}) => {
|
|
1139
|
+
const generatorCache = new Map();
|
|
1140
|
+
const getGenerator = (from = 0, to = 100, velocity = 0, isScale = false) => {
|
|
1141
|
+
const key = `${from}-${to}-${velocity}-${isScale}`;
|
|
1142
|
+
if (!generatorCache.has(key)) {
|
|
1143
|
+
generatorCache.set(key, createGenerator(Object.assign({ from,
|
|
1144
|
+
to,
|
|
1145
|
+
velocity, restSpeed: isScale ? 0.05 : 2, restDistance: isScale ? 0.01 : 0.5 }, options)));
|
|
1146
|
+
}
|
|
1147
|
+
return generatorCache.get(key);
|
|
1148
|
+
};
|
|
1149
|
+
const getKeyframes = (generator) => {
|
|
1150
|
+
if (!keyframesCache.has(generator)) {
|
|
1151
|
+
keyframesCache.set(generator, pregenerateKeyframes(generator));
|
|
1152
|
+
}
|
|
1153
|
+
return keyframesCache.get(generator);
|
|
1154
|
+
};
|
|
1155
|
+
return {
|
|
1156
|
+
createAnimation: (keyframes, getOrigin, canUseGenerator, name, data) => {
|
|
1157
|
+
let settings;
|
|
1158
|
+
let generator;
|
|
1159
|
+
const numKeyframes = keyframes.length;
|
|
1160
|
+
let shouldUseGenerator = canUseGenerator &&
|
|
1161
|
+
numKeyframes <= 2 &&
|
|
1162
|
+
keyframes.every(isNumberOrNull);
|
|
1163
|
+
if (shouldUseGenerator) {
|
|
1164
|
+
const prevAnimationState = name && data && data.prevGeneratorState[name];
|
|
1165
|
+
const velocity = prevAnimationState &&
|
|
1166
|
+
(numKeyframes === 1 ||
|
|
1167
|
+
(numKeyframes === 2 && keyframes[0] === null))
|
|
1168
|
+
? prevAnimationState.velocity
|
|
1169
|
+
: 0;
|
|
1170
|
+
const target = keyframes[numKeyframes - 1];
|
|
1171
|
+
const unresolvedOrigin = numKeyframes === 1 ? null : keyframes[0];
|
|
1172
|
+
const origin = unresolvedOrigin === null
|
|
1173
|
+
? prevAnimationState
|
|
1174
|
+
? prevAnimationState.value
|
|
1175
|
+
: parseFloat(getOrigin())
|
|
1176
|
+
: unresolvedOrigin;
|
|
1177
|
+
generator = getGenerator(origin, target, velocity, name === null || name === void 0 ? void 0 : name.includes("scale"));
|
|
1178
|
+
const keyframesMetadata = getKeyframes(generator);
|
|
1179
|
+
settings = Object.assign(Object.assign({}, keyframesMetadata), { easing: "linear" });
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
generator = getGenerator(0, 100);
|
|
1183
|
+
const keyframesMetadata = getKeyframes(generator);
|
|
1184
|
+
settings = {
|
|
1185
|
+
easing: "ease",
|
|
1186
|
+
duration: keyframesMetadata.overshootDuration,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
// TODO Add test for this
|
|
1190
|
+
if (generator && data && name) {
|
|
1191
|
+
data.generators[name] = generator;
|
|
1192
|
+
}
|
|
1193
|
+
return settings;
|
|
1194
|
+
},
|
|
1195
|
+
};
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
const isNumberOrNull = (value) => typeof value !== "string";
|
|
1199
|
+
|
|
1200
|
+
const spring = createGeneratorEasing(createSpringGenerator);
|
|
1201
|
+
|
|
1202
|
+
const createGlideGenerator = ({ from = 0, velocity = 0.0, power = 0.8, decay = 0.325, bounceDamping, bounceStiffness, changeTarget, min, max, restDistance = 0.5, restSpeed, }) => {
|
|
1203
|
+
decay = ms(decay);
|
|
1204
|
+
const state = {
|
|
1205
|
+
value: from,
|
|
1206
|
+
target: from,
|
|
1207
|
+
velocity,
|
|
1208
|
+
hasReachedTarget: false,
|
|
1209
|
+
done: false,
|
|
1210
|
+
};
|
|
1211
|
+
const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
1212
|
+
const nearestBoundary = (v) => {
|
|
1213
|
+
if (min === undefined)
|
|
1214
|
+
return max;
|
|
1215
|
+
if (max === undefined)
|
|
1216
|
+
return min;
|
|
1217
|
+
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
1218
|
+
};
|
|
1219
|
+
let amplitude = power * velocity;
|
|
1220
|
+
const ideal = from + amplitude;
|
|
1221
|
+
const target = changeTarget === undefined ? ideal : changeTarget(ideal);
|
|
1222
|
+
state.target = target;
|
|
1223
|
+
/**
|
|
1224
|
+
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
1225
|
+
* the animation will start from the wrong position.
|
|
1226
|
+
*/
|
|
1227
|
+
if (target !== ideal)
|
|
1228
|
+
amplitude = target - from;
|
|
1229
|
+
const calcDelta = (t) => -amplitude * Math.exp(-t / decay);
|
|
1230
|
+
const calcLatest = (t) => target + calcDelta(t);
|
|
1231
|
+
const applyFriction = (t) => {
|
|
1232
|
+
const delta = calcDelta(t);
|
|
1233
|
+
const latest = calcLatest(t);
|
|
1234
|
+
state.done = Math.abs(delta) <= restDistance;
|
|
1235
|
+
state.value = state.done ? target : latest;
|
|
1236
|
+
state.velocity =
|
|
1237
|
+
t === 0 ? velocity : calcVelocity(calcLatest, t, state.value);
|
|
1238
|
+
};
|
|
1239
|
+
/**
|
|
1240
|
+
* Ideally this would resolve for t in a stateless way, we could
|
|
1241
|
+
* do that by always precalculating the animation but as we know
|
|
1242
|
+
* this will be done anyway we can assume that spring will
|
|
1243
|
+
* be discovered during that.
|
|
1244
|
+
*/
|
|
1245
|
+
let timeReachedBoundary;
|
|
1246
|
+
let spring;
|
|
1247
|
+
const checkCatchBoundary = (t) => {
|
|
1248
|
+
if (!isOutOfBounds(state.value))
|
|
1249
|
+
return;
|
|
1250
|
+
timeReachedBoundary = t;
|
|
1251
|
+
spring = createSpringGenerator({
|
|
1252
|
+
from: state.value,
|
|
1253
|
+
to: nearestBoundary(state.value),
|
|
1254
|
+
velocity: state.velocity,
|
|
1255
|
+
damping: bounceDamping,
|
|
1256
|
+
stiffness: bounceStiffness,
|
|
1257
|
+
restDistance,
|
|
1258
|
+
restSpeed,
|
|
1259
|
+
});
|
|
1260
|
+
};
|
|
1261
|
+
checkCatchBoundary(0);
|
|
1262
|
+
return {
|
|
1263
|
+
next: (t) => {
|
|
1264
|
+
/**
|
|
1265
|
+
* We need to resolve the friction to figure out if we need a
|
|
1266
|
+
* spring but we don't want to do this twice per frame. So here
|
|
1267
|
+
* we flag if we updated for this frame and later if we did
|
|
1268
|
+
* we can skip doing it again.
|
|
1269
|
+
*/
|
|
1270
|
+
let hasUpdatedFrame = false;
|
|
1271
|
+
if (!spring && timeReachedBoundary === undefined) {
|
|
1272
|
+
hasUpdatedFrame = true;
|
|
1273
|
+
applyFriction(t);
|
|
1274
|
+
checkCatchBoundary(t);
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* If we have a spring and the provided t is beyond the moment the friction
|
|
1278
|
+
* animation crossed the min/max boundary, use the spring.
|
|
1279
|
+
*/
|
|
1280
|
+
if (timeReachedBoundary !== undefined && t > timeReachedBoundary) {
|
|
1281
|
+
state.hasReachedTarget = true;
|
|
1282
|
+
return spring.next(t - timeReachedBoundary);
|
|
1283
|
+
}
|
|
1284
|
+
else {
|
|
1285
|
+
state.hasReachedTarget = false;
|
|
1286
|
+
!hasUpdatedFrame && applyFriction(t);
|
|
1287
|
+
return state;
|
|
1288
|
+
}
|
|
1289
|
+
},
|
|
1290
|
+
};
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
const glide = createGeneratorEasing(createGlideGenerator);
|
|
1294
|
+
|
|
962
1295
|
exports.animate = animate;
|
|
963
1296
|
exports.animateStyle = animateStyle;
|
|
1297
|
+
exports.glide = glide;
|
|
1298
|
+
exports.spring = spring;
|
|
964
1299
|
exports.stagger = stagger;
|
|
965
1300
|
exports.timeline = timeline;
|
|
966
1301
|
|