framer-motion 10.2.3 → 10.2.4
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/dist/cjs/dom-entry.js +1 -1
- package/dist/cjs/index.js +18 -20
- package/dist/cjs/{wrap-27fda06a.js → wrap-62da7859.js} +456 -437
- package/dist/dom-entry.d.ts +485 -37
- package/dist/es/animation/GroupPlaybackControls.mjs +25 -0
- package/dist/es/animation/animate.mjs +2 -3
- package/dist/es/animation/create-instant-animation.mjs +13 -3
- package/dist/es/animation/generators/inertia.mjs +87 -0
- package/dist/es/animation/{legacy-popmotion → generators}/keyframes.mjs +8 -15
- package/dist/es/animation/{legacy-popmotion/find-spring.mjs → generators/spring/find.mjs} +6 -5
- package/dist/es/animation/generators/spring/index.mjs +129 -0
- package/dist/es/animation/generators/utils/velocity.mjs +9 -0
- package/dist/es/animation/index.mjs +2 -10
- package/dist/es/animation/js/driver-frameloop.mjs +12 -0
- package/dist/es/animation/js/index.mjs +206 -0
- package/dist/es/animation/optimized-appear/handoff.mjs +3 -1
- package/dist/es/animation/waapi/create-accelerated-animation.mjs +16 -10
- package/dist/es/frameloop/index.mjs +3 -4
- package/dist/es/gestures/pan/PanSession.mjs +2 -2
- package/dist/es/index.mjs +2 -3
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/utils/time-conversion.mjs +2 -1
- package/dist/es/value/index.mjs +3 -3
- package/dist/es/value/use-spring.mjs +1 -1
- package/dist/es/value/use-velocity.mjs +4 -6
- package/dist/framer-motion.dev.js +475 -458
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +175 -218
- package/dist/projection.dev.js +5849 -5830
- package/dist/three-entry.d.ts +62 -63
- package/package.json +7 -11
- package/dist/es/animation/legacy-popmotion/decay.mjs +0 -34
- package/dist/es/animation/legacy-popmotion/index.mjs +0 -163
- package/dist/es/animation/legacy-popmotion/inertia.mjs +0 -90
- package/dist/es/animation/legacy-popmotion/spring.mjs +0 -143
- package/dist/es/frameloop/on-next-frame.mjs +0 -12
|
@@ -7,8 +7,7 @@ import { isEasingArray, easingDefinitionToFunction } from '../utils/easing.mjs';
|
|
|
7
7
|
function defaultEasing(values, easing) {
|
|
8
8
|
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
9
9
|
}
|
|
10
|
-
function keyframes({ keyframes: keyframeValues,
|
|
11
|
-
keyframeValues = [...keyframeValues];
|
|
10
|
+
function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
|
|
12
11
|
/**
|
|
13
12
|
* Easing functions can be externally defined as strings. Here we convert them
|
|
14
13
|
* into actual functions.
|
|
@@ -33,24 +32,18 @@ function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duratio
|
|
|
33
32
|
times && times.length === keyframeValues.length
|
|
34
33
|
? times
|
|
35
34
|
: defaultOffset(keyframeValues), duration);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
let interpolator = createInterpolator();
|
|
35
|
+
const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
|
|
36
|
+
ease: Array.isArray(easingFunctions)
|
|
37
|
+
? easingFunctions
|
|
38
|
+
: defaultEasing(keyframeValues, easingFunctions),
|
|
39
|
+
});
|
|
44
40
|
return {
|
|
41
|
+
calculatedDuration: duration,
|
|
45
42
|
next: (t) => {
|
|
46
|
-
state.value =
|
|
43
|
+
state.value = mapTimeToKeyframe(t);
|
|
47
44
|
state.done = t >= duration;
|
|
48
45
|
return state;
|
|
49
46
|
},
|
|
50
|
-
flipTarget: () => {
|
|
51
|
-
keyframeValues.reverse();
|
|
52
|
-
interpolator = createInterpolator();
|
|
53
|
-
},
|
|
54
47
|
};
|
|
55
48
|
}
|
|
56
49
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { warning } from '
|
|
2
|
-
import { clamp } from '
|
|
1
|
+
import { warning } from '../../../utils/errors.mjs';
|
|
2
|
+
import { clamp } from '../../../utils/clamp.mjs';
|
|
3
|
+
import { secondsToMilliseconds, millisecondsToSeconds } from '../../../utils/time-conversion.mjs';
|
|
3
4
|
|
|
4
5
|
const safeMin = 0.001;
|
|
5
6
|
const minDuration = 0.01;
|
|
@@ -9,13 +10,13 @@ const maxDamping = 1;
|
|
|
9
10
|
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
10
11
|
let envelope;
|
|
11
12
|
let derivative;
|
|
12
|
-
warning(duration <= maxDuration
|
|
13
|
+
warning(duration <= secondsToMilliseconds(maxDuration), "Spring duration must be 10 seconds or less");
|
|
13
14
|
let dampingRatio = 1 - bounce;
|
|
14
15
|
/**
|
|
15
16
|
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
16
17
|
*/
|
|
17
18
|
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
18
|
-
duration = clamp(minDuration, maxDuration, duration
|
|
19
|
+
duration = clamp(minDuration, maxDuration, millisecondsToSeconds(duration));
|
|
19
20
|
if (dampingRatio < 1) {
|
|
20
21
|
/**
|
|
21
22
|
* Underdamped spring
|
|
@@ -56,7 +57,7 @@ function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, })
|
|
|
56
57
|
}
|
|
57
58
|
const initialGuess = 5 / duration;
|
|
58
59
|
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
59
|
-
duration = duration
|
|
60
|
+
duration = secondsToMilliseconds(duration);
|
|
60
61
|
if (isNaN(undampedFreq)) {
|
|
61
62
|
return {
|
|
62
63
|
stiffness: 100,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { millisecondsToSeconds } from '../../../utils/time-conversion.mjs';
|
|
2
|
+
import { calcGeneratorVelocity } from '../utils/velocity.mjs';
|
|
3
|
+
import { findSpring, calcAngularFreq } from './find.mjs';
|
|
4
|
+
|
|
5
|
+
const durationKeys = ["duration", "bounce"];
|
|
6
|
+
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
7
|
+
function isSpringType(options, keys) {
|
|
8
|
+
return keys.some((key) => options[key] !== undefined);
|
|
9
|
+
}
|
|
10
|
+
function getSpringOptions(options) {
|
|
11
|
+
let springOptions = {
|
|
12
|
+
velocity: 0.0,
|
|
13
|
+
stiffness: 100,
|
|
14
|
+
damping: 10,
|
|
15
|
+
mass: 1.0,
|
|
16
|
+
isResolvedFromDuration: false,
|
|
17
|
+
...options,
|
|
18
|
+
};
|
|
19
|
+
// stiffness/damping/mass overrides duration/bounce
|
|
20
|
+
if (!isSpringType(options, physicsKeys) &&
|
|
21
|
+
isSpringType(options, durationKeys)) {
|
|
22
|
+
const derived = findSpring(options);
|
|
23
|
+
springOptions = {
|
|
24
|
+
...springOptions,
|
|
25
|
+
...derived,
|
|
26
|
+
velocity: 0.0,
|
|
27
|
+
mass: 1.0,
|
|
28
|
+
};
|
|
29
|
+
springOptions.isResolvedFromDuration = true;
|
|
30
|
+
}
|
|
31
|
+
return springOptions;
|
|
32
|
+
}
|
|
33
|
+
function spring({ keyframes, restDelta, restSpeed, ...options }) {
|
|
34
|
+
const origin = keyframes[0];
|
|
35
|
+
const target = keyframes[keyframes.length - 1];
|
|
36
|
+
/**
|
|
37
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
38
|
+
* to reduce GC during animation.
|
|
39
|
+
*/
|
|
40
|
+
const state = { done: false, value: origin };
|
|
41
|
+
const { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
|
|
42
|
+
const initialVelocity = velocity ? -millisecondsToSeconds(velocity) : 0.0;
|
|
43
|
+
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
44
|
+
const initialDelta = target - origin;
|
|
45
|
+
const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
|
|
46
|
+
/**
|
|
47
|
+
* If we're working on a granular scale, use smaller defaults for determining
|
|
48
|
+
* when the spring is finished.
|
|
49
|
+
*
|
|
50
|
+
* These defaults have been selected emprically based on what strikes a good
|
|
51
|
+
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
|
52
|
+
*/
|
|
53
|
+
const isGranularScale = Math.abs(initialDelta) < 5;
|
|
54
|
+
restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
|
|
55
|
+
restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
|
|
56
|
+
let resolveSpring;
|
|
57
|
+
if (dampingRatio < 1) {
|
|
58
|
+
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
59
|
+
// Underdamped spring
|
|
60
|
+
resolveSpring = (t) => {
|
|
61
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
62
|
+
return (target -
|
|
63
|
+
envelope *
|
|
64
|
+
(((initialVelocity +
|
|
65
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
66
|
+
angularFreq) *
|
|
67
|
+
Math.sin(angularFreq * t) +
|
|
68
|
+
initialDelta * Math.cos(angularFreq * t)));
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
else if (dampingRatio === 1) {
|
|
72
|
+
// Critically damped spring
|
|
73
|
+
resolveSpring = (t) => target -
|
|
74
|
+
Math.exp(-undampedAngularFreq * t) *
|
|
75
|
+
(initialDelta +
|
|
76
|
+
(initialVelocity + undampedAngularFreq * initialDelta) * t);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Overdamped spring
|
|
80
|
+
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
81
|
+
resolveSpring = (t) => {
|
|
82
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
83
|
+
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
84
|
+
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
85
|
+
return (target -
|
|
86
|
+
(envelope *
|
|
87
|
+
((initialVelocity +
|
|
88
|
+
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
89
|
+
Math.sinh(freqForT) +
|
|
90
|
+
dampedAngularFreq *
|
|
91
|
+
initialDelta *
|
|
92
|
+
Math.cosh(freqForT))) /
|
|
93
|
+
dampedAngularFreq);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
calculatedDuration: isResolvedFromDuration ? duration || null : null,
|
|
98
|
+
next: (t) => {
|
|
99
|
+
const current = resolveSpring(t);
|
|
100
|
+
if (!isResolvedFromDuration) {
|
|
101
|
+
let currentVelocity = initialVelocity;
|
|
102
|
+
if (t !== 0) {
|
|
103
|
+
/**
|
|
104
|
+
* We only need to calculate velocity for under-damped springs
|
|
105
|
+
* as over- and critically-damped springs can't overshoot, so
|
|
106
|
+
* checking only for displacement is enough.
|
|
107
|
+
*/
|
|
108
|
+
if (dampingRatio < 1) {
|
|
109
|
+
currentVelocity = calcGeneratorVelocity(resolveSpring, t, current);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
currentVelocity = 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
|
116
|
+
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
|
117
|
+
state.done =
|
|
118
|
+
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
state.done = t >= duration;
|
|
122
|
+
}
|
|
123
|
+
state.value = state.done ? target : current;
|
|
124
|
+
return state;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { spring };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { velocityPerSecond } from '../../../utils/velocity-per-second.mjs';
|
|
2
|
+
|
|
3
|
+
const velocitySampleDuration = 5; // ms
|
|
4
|
+
function calcGeneratorVelocity(resolveValue, t, current) {
|
|
5
|
+
const prevT = Math.max(t - velocitySampleDuration, 0);
|
|
6
|
+
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { calcGeneratorVelocity };
|
|
@@ -3,12 +3,11 @@ import { secondsToMilliseconds } from '../utils/time-conversion.mjs';
|
|
|
3
3
|
import { instantAnimationState } from '../utils/use-instant-transition-state.mjs';
|
|
4
4
|
import { createAcceleratedAnimation } from './waapi/create-accelerated-animation.mjs';
|
|
5
5
|
import { createInstantAnimation } from './create-instant-animation.mjs';
|
|
6
|
-
import { animateValue } from './legacy-popmotion/index.mjs';
|
|
7
|
-
import { inertia } from './legacy-popmotion/inertia.mjs';
|
|
8
6
|
import { getDefaultTransition } from './utils/default-transitions.mjs';
|
|
9
7
|
import { isAnimatable } from './utils/is-animatable.mjs';
|
|
10
8
|
import { getKeyframes } from './utils/keyframes.mjs';
|
|
11
9
|
import { getValueTransition, isTransitionDefined } from './utils/transitions.mjs';
|
|
10
|
+
import { animateValue } from './js/index.mjs';
|
|
12
11
|
|
|
13
12
|
const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
|
|
14
13
|
return (onComplete) => {
|
|
@@ -40,7 +39,7 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
|
|
|
40
39
|
keyframes,
|
|
41
40
|
velocity: value.getVelocity(),
|
|
42
41
|
...valueTransition,
|
|
43
|
-
elapsed,
|
|
42
|
+
delay: -elapsed,
|
|
44
43
|
onUpdate: (v) => {
|
|
45
44
|
value.set(v);
|
|
46
45
|
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
@@ -60,13 +59,6 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
|
|
|
60
59
|
*/
|
|
61
60
|
return createInstantAnimation(options);
|
|
62
61
|
}
|
|
63
|
-
else if (valueTransition.type === "inertia") {
|
|
64
|
-
/**
|
|
65
|
-
* If this is an inertia animation, we currently don't support pre-generating
|
|
66
|
-
* keyframes for this as such it must always run on the main thread.
|
|
67
|
-
*/
|
|
68
|
-
return inertia(options);
|
|
69
|
-
}
|
|
70
62
|
/**
|
|
71
63
|
* If there's no transition defined for this value, we can generate
|
|
72
64
|
* unqiue transition settings for this value.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { sync, cancelSync } from '../../frameloop/index.mjs';
|
|
2
|
+
|
|
3
|
+
const frameloopDriver = (update) => {
|
|
4
|
+
const passTimestamp = ({ timestamp }) => update(timestamp);
|
|
5
|
+
return {
|
|
6
|
+
start: () => sync.update(passTimestamp, true),
|
|
7
|
+
stop: () => cancelSync.update(passTimestamp),
|
|
8
|
+
now: () => performance.now(),
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { frameloopDriver };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { keyframes } from '../generators/keyframes.mjs';
|
|
2
|
+
import { spring } from '../generators/spring/index.mjs';
|
|
3
|
+
import { inertia } from '../generators/inertia.mjs';
|
|
4
|
+
import { frameloopDriver } from './driver-frameloop.mjs';
|
|
5
|
+
import { interpolate } from '../../utils/interpolate.mjs';
|
|
6
|
+
import { clamp } from '../../utils/clamp.mjs';
|
|
7
|
+
import { millisecondsToSeconds, secondsToMilliseconds } from '../../utils/time-conversion.mjs';
|
|
8
|
+
|
|
9
|
+
const types = {
|
|
10
|
+
decay: inertia,
|
|
11
|
+
inertia,
|
|
12
|
+
tween: keyframes,
|
|
13
|
+
keyframes: keyframes,
|
|
14
|
+
spring,
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Implement a practical max duration for keyframe generation
|
|
18
|
+
* to prevent infinite loops
|
|
19
|
+
*/
|
|
20
|
+
const maxDuration = 20000;
|
|
21
|
+
function calculateDuration(generator) {
|
|
22
|
+
let duration = 0;
|
|
23
|
+
const timeStep = 50;
|
|
24
|
+
let state = generator.next(duration);
|
|
25
|
+
while (!state.done && duration < maxDuration) {
|
|
26
|
+
duration += timeStep;
|
|
27
|
+
state = generator.next(duration);
|
|
28
|
+
}
|
|
29
|
+
return duration;
|
|
30
|
+
}
|
|
31
|
+
function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, keyframes: keyframes$1, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", onPlay, onStop, onComplete, onUpdate, ...options }) {
|
|
32
|
+
let animationDriver;
|
|
33
|
+
const generatorFactory = types[type] || keyframes;
|
|
34
|
+
/**
|
|
35
|
+
* If this isn't the keyframes generator and we've been provided
|
|
36
|
+
* strings as keyframes, we need to interpolate these.
|
|
37
|
+
* TODO: Support velocity for units and complex value types/
|
|
38
|
+
*/
|
|
39
|
+
let mapNumbersToKeyframes;
|
|
40
|
+
if (generatorFactory !== keyframes &&
|
|
41
|
+
typeof keyframes$1[0] !== "number") {
|
|
42
|
+
mapNumbersToKeyframes = interpolate([0, 100], keyframes$1, {
|
|
43
|
+
clamp: false,
|
|
44
|
+
});
|
|
45
|
+
keyframes$1 = [0, 100];
|
|
46
|
+
}
|
|
47
|
+
const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
|
|
48
|
+
let mirroredGenerator;
|
|
49
|
+
if (repeatType === "mirror") {
|
|
50
|
+
mirroredGenerator = generatorFactory({
|
|
51
|
+
...options,
|
|
52
|
+
keyframes: [...keyframes$1].reverse(),
|
|
53
|
+
velocity: -(options.velocity || 0),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
let playState = "idle";
|
|
57
|
+
let holdTime = null;
|
|
58
|
+
let startTime = null;
|
|
59
|
+
/**
|
|
60
|
+
* If duration is undefined and we have repeat options,
|
|
61
|
+
* we need to calculate a duration from the generator.
|
|
62
|
+
*
|
|
63
|
+
* We set it to the generator itself to cache the duration.
|
|
64
|
+
* Any timeline resolver will need to have already precalculated
|
|
65
|
+
* the duration by this step.
|
|
66
|
+
*/
|
|
67
|
+
if (generator.calculatedDuration === null && repeat) {
|
|
68
|
+
generator.calculatedDuration = calculateDuration(generator);
|
|
69
|
+
}
|
|
70
|
+
const { calculatedDuration } = generator;
|
|
71
|
+
let resolvedDuration = Infinity;
|
|
72
|
+
let totalDuration = Infinity;
|
|
73
|
+
if (calculatedDuration) {
|
|
74
|
+
resolvedDuration = calculatedDuration + repeatDelay;
|
|
75
|
+
totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
|
|
76
|
+
}
|
|
77
|
+
let currentTime = 0;
|
|
78
|
+
const tick = (timestamp) => {
|
|
79
|
+
if (startTime === null)
|
|
80
|
+
return;
|
|
81
|
+
if (holdTime !== null) {
|
|
82
|
+
currentTime = holdTime;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
currentTime = timestamp - startTime;
|
|
86
|
+
}
|
|
87
|
+
// Rebase on delay
|
|
88
|
+
currentTime = Math.max(currentTime - delay, 0);
|
|
89
|
+
/**
|
|
90
|
+
* If this animation has finished, set the current time
|
|
91
|
+
* to the total duration.
|
|
92
|
+
*/
|
|
93
|
+
if (playState === "finished" && holdTime === null) {
|
|
94
|
+
currentTime = totalDuration;
|
|
95
|
+
}
|
|
96
|
+
let elapsed = currentTime;
|
|
97
|
+
let frameGenerator = generator;
|
|
98
|
+
if (repeat) {
|
|
99
|
+
/**
|
|
100
|
+
* Get the current progress (0-1) of the animation. If t is >
|
|
101
|
+
* than duration we'll get values like 2.5 (midway through the
|
|
102
|
+
* third iteration)
|
|
103
|
+
*/
|
|
104
|
+
const progress = currentTime / resolvedDuration;
|
|
105
|
+
/**
|
|
106
|
+
* Get the current iteration (0 indexed). For instance the floor of
|
|
107
|
+
* 2.5 is 2.
|
|
108
|
+
*/
|
|
109
|
+
let currentIteration = Math.floor(progress);
|
|
110
|
+
/**
|
|
111
|
+
* Get the current progress of the iteration by taking the remainder
|
|
112
|
+
* so 2.5 is 0.5 through iteration 2
|
|
113
|
+
*/
|
|
114
|
+
let iterationProgress = progress % 1.0;
|
|
115
|
+
/**
|
|
116
|
+
* If iteration progress is 1 we count that as the end
|
|
117
|
+
* of the previous iteration.
|
|
118
|
+
*/
|
|
119
|
+
if (!iterationProgress && progress >= 1) {
|
|
120
|
+
iterationProgress = 1;
|
|
121
|
+
}
|
|
122
|
+
iterationProgress === 1 && currentIteration--;
|
|
123
|
+
/**
|
|
124
|
+
* Reverse progress if we're not running in "normal" direction
|
|
125
|
+
*/
|
|
126
|
+
const iterationIsOdd = currentIteration % 2;
|
|
127
|
+
if (iterationIsOdd) {
|
|
128
|
+
if (repeatType === "reverse") {
|
|
129
|
+
iterationProgress = 1 - iterationProgress;
|
|
130
|
+
if (repeatDelay) {
|
|
131
|
+
iterationProgress -= repeatDelay / resolvedDuration;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (repeatType === "mirror") {
|
|
135
|
+
frameGenerator = mirroredGenerator;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const p = currentTime >= totalDuration
|
|
139
|
+
? repeatType === "reverse" && iterationIsOdd
|
|
140
|
+
? 0
|
|
141
|
+
: 1
|
|
142
|
+
: clamp(0, 1, iterationProgress);
|
|
143
|
+
elapsed = p * resolvedDuration;
|
|
144
|
+
}
|
|
145
|
+
const state = frameGenerator.next(elapsed);
|
|
146
|
+
let { value, done } = state;
|
|
147
|
+
if (onUpdate) {
|
|
148
|
+
onUpdate(mapNumbersToKeyframes ? mapNumbersToKeyframes(value) : value);
|
|
149
|
+
}
|
|
150
|
+
if (calculatedDuration !== null) {
|
|
151
|
+
done = currentTime >= totalDuration;
|
|
152
|
+
}
|
|
153
|
+
const isAnimationFinished = holdTime === null &&
|
|
154
|
+
(playState === "finished" || (playState === "running" && done));
|
|
155
|
+
if (isAnimationFinished) {
|
|
156
|
+
playState = "finished";
|
|
157
|
+
onComplete && onComplete();
|
|
158
|
+
animationDriver && animationDriver.stop();
|
|
159
|
+
}
|
|
160
|
+
return state;
|
|
161
|
+
};
|
|
162
|
+
const play = () => {
|
|
163
|
+
animationDriver = driver(tick);
|
|
164
|
+
const now = animationDriver.now();
|
|
165
|
+
onPlay && onPlay();
|
|
166
|
+
playState = "running";
|
|
167
|
+
if (holdTime !== null) {
|
|
168
|
+
startTime = now - holdTime;
|
|
169
|
+
}
|
|
170
|
+
else if (!startTime) {
|
|
171
|
+
// TODO When implementing play/pause, check WAAPI
|
|
172
|
+
// logic around finished animations
|
|
173
|
+
startTime = now;
|
|
174
|
+
}
|
|
175
|
+
holdTime = null;
|
|
176
|
+
animationDriver.start();
|
|
177
|
+
};
|
|
178
|
+
if (autoplay) {
|
|
179
|
+
play();
|
|
180
|
+
}
|
|
181
|
+
const controls = {
|
|
182
|
+
get currentTime() {
|
|
183
|
+
return millisecondsToSeconds(currentTime);
|
|
184
|
+
},
|
|
185
|
+
set currentTime(newTime) {
|
|
186
|
+
if (holdTime !== null || !animationDriver) {
|
|
187
|
+
holdTime = 0;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
startTime =
|
|
191
|
+
animationDriver.now() - secondsToMilliseconds(newTime);
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
stop: () => {
|
|
195
|
+
onStop && onStop();
|
|
196
|
+
animationDriver && animationDriver.stop();
|
|
197
|
+
},
|
|
198
|
+
sample: (elapsed) => {
|
|
199
|
+
startTime = 0;
|
|
200
|
+
return tick(elapsed);
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
return controls;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export { animateValue };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { transformProps } from '../../render/html/utils/transform.mjs';
|
|
2
|
+
import { millisecondsToSeconds } from '../../utils/time-conversion.mjs';
|
|
2
3
|
import { appearAnimationStore } from './store.mjs';
|
|
3
4
|
import { appearStoreId } from './store-id.mjs';
|
|
4
5
|
|
|
@@ -41,7 +42,8 @@ sync) {
|
|
|
41
42
|
*/
|
|
42
43
|
sync.update(() => {
|
|
43
44
|
if (value.animation) {
|
|
44
|
-
value.animation.currentTime =
|
|
45
|
+
value.animation.currentTime =
|
|
46
|
+
performance.now() - millisecondsToSeconds(sampledTime);
|
|
45
47
|
}
|
|
46
48
|
});
|
|
47
49
|
/**
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { sync } from '../../frameloop/index.mjs';
|
|
2
|
-
import { animateValue } from '../legacy-popmotion/index.mjs';
|
|
3
2
|
import { animateStyle } from './index.mjs';
|
|
4
3
|
import { isWaapiSupportedEasing } from './easing.mjs';
|
|
5
4
|
import { supports } from './supports.mjs';
|
|
6
5
|
import { getFinalKeyframe } from './utils/get-final-keyframe.mjs';
|
|
6
|
+
import { animateValue } from '../js/index.mjs';
|
|
7
|
+
import { millisecondsToSeconds, secondsToMilliseconds } from '../../utils/time-conversion.mjs';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A list of values that can be hardware-accelerated.
|
|
@@ -21,6 +22,11 @@ const acceleratedValues = new Set([
|
|
|
21
22
|
* keyframe quantity.
|
|
22
23
|
*/
|
|
23
24
|
const sampleDelta = 10; //ms
|
|
25
|
+
/**
|
|
26
|
+
* Implement a practical max duration for keyframe generation
|
|
27
|
+
* to prevent infinite loops
|
|
28
|
+
*/
|
|
29
|
+
const maxDuration = 20000;
|
|
24
30
|
const requiresPregeneratedKeyframes = (valueName, options) => options.type === "spring" ||
|
|
25
31
|
valueName === "backgroundColor" ||
|
|
26
32
|
!isWaapiSupportedEasing(options.ease);
|
|
@@ -29,10 +35,11 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
|
|
|
29
35
|
acceleratedValues.has(valueName) &&
|
|
30
36
|
!options.repeatDelay &&
|
|
31
37
|
options.repeatType !== "mirror" &&
|
|
32
|
-
options.damping !== 0
|
|
38
|
+
options.damping !== 0 &&
|
|
39
|
+
options.type !== "inertia";
|
|
33
40
|
if (!canAccelerateAnimation)
|
|
34
41
|
return false;
|
|
35
|
-
let { keyframes, duration = 300,
|
|
42
|
+
let { keyframes, duration = 300, ease } = options;
|
|
36
43
|
/**
|
|
37
44
|
* If this animation needs pre-generated keyframes then generate.
|
|
38
45
|
*/
|
|
@@ -40,7 +47,7 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
|
|
|
40
47
|
const sampleAnimation = animateValue({
|
|
41
48
|
...options,
|
|
42
49
|
repeat: 0,
|
|
43
|
-
|
|
50
|
+
delay: 0,
|
|
44
51
|
});
|
|
45
52
|
let state = { done: false, value: keyframes[0] };
|
|
46
53
|
const pregeneratedKeyframes = [];
|
|
@@ -49,8 +56,8 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
|
|
|
49
56
|
* we're heading for an infinite loop.
|
|
50
57
|
*/
|
|
51
58
|
let t = 0;
|
|
52
|
-
while (!state.done && t <
|
|
53
|
-
state = sampleAnimation.sample(t
|
|
59
|
+
while (!state.done && t < maxDuration) {
|
|
60
|
+
state = sampleAnimation.sample(t);
|
|
54
61
|
pregeneratedKeyframes.push(state.value);
|
|
55
62
|
t += sampleDelta;
|
|
56
63
|
}
|
|
@@ -60,7 +67,6 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
|
|
|
60
67
|
}
|
|
61
68
|
const animation = animateStyle(value.owner.current, valueName, keyframes, {
|
|
62
69
|
...options,
|
|
63
|
-
delay: -elapsed,
|
|
64
70
|
duration,
|
|
65
71
|
/**
|
|
66
72
|
* This function is currently not called if ease is provided
|
|
@@ -90,10 +96,10 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
|
|
|
90
96
|
*/
|
|
91
97
|
return {
|
|
92
98
|
get currentTime() {
|
|
93
|
-
return animation.currentTime || 0;
|
|
99
|
+
return millisecondsToSeconds(animation.currentTime || 0);
|
|
94
100
|
},
|
|
95
|
-
set currentTime(
|
|
96
|
-
animation.currentTime =
|
|
101
|
+
set currentTime(newTime) {
|
|
102
|
+
animation.currentTime = secondsToMilliseconds(newTime);
|
|
97
103
|
},
|
|
98
104
|
stop: () => {
|
|
99
105
|
/**
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { onNextFrame, defaultTimestep } from './on-next-frame.mjs';
|
|
2
1
|
import { createRenderStep } from './create-render-step.mjs';
|
|
3
2
|
import { frameData } from './data.mjs';
|
|
4
3
|
|
|
@@ -38,7 +37,7 @@ const processStep = (stepId) => steps[stepId].process(frameData);
|
|
|
38
37
|
const processFrame = (timestamp) => {
|
|
39
38
|
runNextFrame = false;
|
|
40
39
|
frameData.delta = useDefaultElapsed
|
|
41
|
-
?
|
|
40
|
+
? 1000 / 60
|
|
42
41
|
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed), 1);
|
|
43
42
|
frameData.timestamp = timestamp;
|
|
44
43
|
isProcessing = true;
|
|
@@ -46,14 +45,14 @@ const processFrame = (timestamp) => {
|
|
|
46
45
|
isProcessing = false;
|
|
47
46
|
if (runNextFrame) {
|
|
48
47
|
useDefaultElapsed = false;
|
|
49
|
-
|
|
48
|
+
requestAnimationFrame(processFrame);
|
|
50
49
|
}
|
|
51
50
|
};
|
|
52
51
|
const startLoop = () => {
|
|
53
52
|
runNextFrame = true;
|
|
54
53
|
useDefaultElapsed = true;
|
|
55
54
|
if (!isProcessing)
|
|
56
|
-
|
|
55
|
+
requestAnimationFrame(processFrame);
|
|
57
56
|
};
|
|
58
57
|
|
|
59
58
|
export { cancelSync, flushSync, sync };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { extractEventInfo } from '../../events/event-info.mjs';
|
|
2
2
|
import { sync, cancelSync } from '../../frameloop/index.mjs';
|
|
3
|
-
import { secondsToMilliseconds } from '../../utils/time-conversion.mjs';
|
|
3
|
+
import { secondsToMilliseconds, millisecondsToSeconds } from '../../utils/time-conversion.mjs';
|
|
4
4
|
import { addPointerEvent } from '../../events/add-pointer-event.mjs';
|
|
5
5
|
import { pipe } from '../../utils/pipe.mjs';
|
|
6
6
|
import { distance2D } from '../../utils/distance.mjs';
|
|
@@ -129,7 +129,7 @@ function getVelocity(history, timeDelta) {
|
|
|
129
129
|
if (!timestampedPoint) {
|
|
130
130
|
return { x: 0, y: 0 };
|
|
131
131
|
}
|
|
132
|
-
const time = (lastPoint.timestamp - timestampedPoint.timestamp)
|
|
132
|
+
const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
|
|
133
133
|
if (time === 0) {
|
|
134
134
|
return { x: 0, y: 0 };
|
|
135
135
|
}
|
package/dist/es/index.mjs
CHANGED
|
@@ -40,14 +40,13 @@ export { useInstantTransition } from './utils/use-instant-transition.mjs';
|
|
|
40
40
|
export { useInstantLayoutTransition } from './projection/use-instant-layout-transition.mjs';
|
|
41
41
|
export { useResetProjection } from './projection/use-reset-projection.mjs';
|
|
42
42
|
export { buildTransform } from './render/html/utils/build-transform.mjs';
|
|
43
|
-
export { animateValue } from './animation/
|
|
44
|
-
export { inertia } from './animation/legacy-popmotion/inertia.mjs';
|
|
43
|
+
export { animateValue } from './animation/js/index.mjs';
|
|
45
44
|
export { color } from './value/types/color/index.mjs';
|
|
46
45
|
export { complex } from './value/types/complex/index.mjs';
|
|
47
46
|
export { px } from './value/types/numbers/units.mjs';
|
|
48
47
|
export { startOptimizedAppearAnimation } from './animation/optimized-appear/start.mjs';
|
|
49
48
|
export { optimizedAppearDataAttribute } from './animation/optimized-appear/data-id.mjs';
|
|
50
|
-
export { spring } from './animation/
|
|
49
|
+
export { spring } from './animation/generators/spring/index.mjs';
|
|
51
50
|
export { MotionContext } from './context/MotionContext/index.mjs';
|
|
52
51
|
export { MotionConfigContext } from './context/MotionConfigContext.mjs';
|
|
53
52
|
export { PresenceContext } from './context/PresenceContext.mjs';
|
|
@@ -22,7 +22,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
22
22
|
* and warn against mismatches.
|
|
23
23
|
*/
|
|
24
24
|
if (process.env.NODE_ENV === "development") {
|
|
25
|
-
warnOnce(nextValue.version === "10.2.
|
|
25
|
+
warnOnce(nextValue.version === "10.2.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.2.4 may not work as expected.`);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
else if (isMotionValue(prevValue)) {
|
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
* @return milliseconds - Converted time in milliseconds.
|
|
6
6
|
*/
|
|
7
7
|
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
8
|
+
const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
|
|
8
9
|
|
|
9
|
-
export { secondsToMilliseconds };
|
|
10
|
+
export { millisecondsToSeconds, secondsToMilliseconds };
|