framer-motion 10.2.2 → 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-b7ab39cb.js → wrap-62da7859.js} +456 -438
- package/dist/dom-entry.d.ts +497 -49
- package/dist/es/animation/GroupPlaybackControls.mjs +25 -0
- package/dist/es/animation/animate.mjs +2 -4
- 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 -459
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +70 -114
- package/dist/projection.dev.js +5849 -5831
- package/dist/three-entry.d.ts +11 -9
- package/package.json +7 -7
- 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
|
@@ -1394,17 +1394,6 @@
|
|
|
1394
1394
|
update() { }
|
|
1395
1395
|
}
|
|
1396
1396
|
|
|
1397
|
-
/*
|
|
1398
|
-
Detect and load appropriate clock setting for the execution environment
|
|
1399
|
-
*/
|
|
1400
|
-
const defaultTimestep = (1 / 60) * 1000;
|
|
1401
|
-
const getCurrentTime = typeof performance !== "undefined"
|
|
1402
|
-
? () => performance.now()
|
|
1403
|
-
: () => Date.now();
|
|
1404
|
-
const onNextFrame = typeof window !== "undefined"
|
|
1405
|
-
? (callback) => window.requestAnimationFrame(callback)
|
|
1406
|
-
: (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
|
|
1407
|
-
|
|
1408
1397
|
function createRenderStep(runNextFrame) {
|
|
1409
1398
|
/**
|
|
1410
1399
|
* We create and reuse two arrays, one to queue jobs for the current frame
|
|
@@ -1533,7 +1522,7 @@
|
|
|
1533
1522
|
const processFrame = (timestamp) => {
|
|
1534
1523
|
runNextFrame = false;
|
|
1535
1524
|
frameData.delta = useDefaultElapsed
|
|
1536
|
-
?
|
|
1525
|
+
? 1000 / 60
|
|
1537
1526
|
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed$1), 1);
|
|
1538
1527
|
frameData.timestamp = timestamp;
|
|
1539
1528
|
isProcessing = true;
|
|
@@ -1541,14 +1530,14 @@
|
|
|
1541
1530
|
isProcessing = false;
|
|
1542
1531
|
if (runNextFrame) {
|
|
1543
1532
|
useDefaultElapsed = false;
|
|
1544
|
-
|
|
1533
|
+
requestAnimationFrame(processFrame);
|
|
1545
1534
|
}
|
|
1546
1535
|
};
|
|
1547
1536
|
const startLoop = () => {
|
|
1548
1537
|
runNextFrame = true;
|
|
1549
1538
|
useDefaultElapsed = true;
|
|
1550
1539
|
if (!isProcessing)
|
|
1551
|
-
|
|
1540
|
+
requestAnimationFrame(processFrame);
|
|
1552
1541
|
};
|
|
1553
1542
|
|
|
1554
1543
|
function addHoverEvent(node, isActive) {
|
|
@@ -1995,7 +1984,7 @@
|
|
|
1995
1984
|
* This will be replaced by the build step with the latest version number.
|
|
1996
1985
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
1997
1986
|
*/
|
|
1998
|
-
this.version = "10.2.
|
|
1987
|
+
this.version = "10.2.4";
|
|
1999
1988
|
/**
|
|
2000
1989
|
* Duration, in milliseconds, since last updating frame.
|
|
2001
1990
|
*
|
|
@@ -2241,7 +2230,7 @@
|
|
|
2241
2230
|
this.stop();
|
|
2242
2231
|
return new Promise((resolve) => {
|
|
2243
2232
|
this.hasAnimated = true;
|
|
2244
|
-
this.animation = startAnimation(resolve)
|
|
2233
|
+
this.animation = startAnimation(resolve);
|
|
2245
2234
|
if (this.events.animationStart) {
|
|
2246
2235
|
this.events.animationStart.notify();
|
|
2247
2236
|
}
|
|
@@ -2275,7 +2264,7 @@
|
|
|
2275
2264
|
return !!this.animation;
|
|
2276
2265
|
}
|
|
2277
2266
|
clearAnimation() {
|
|
2278
|
-
this.animation
|
|
2267
|
+
delete this.animation;
|
|
2279
2268
|
}
|
|
2280
2269
|
/**
|
|
2281
2270
|
* Destroy and clean up subscribers to this `MotionValue`.
|
|
@@ -2723,11 +2712,74 @@
|
|
|
2723
2712
|
* @return milliseconds - Converted time in milliseconds.
|
|
2724
2713
|
*/
|
|
2725
2714
|
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
2715
|
+
const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
|
|
2726
2716
|
|
|
2727
2717
|
const instantAnimationState = {
|
|
2728
2718
|
current: false,
|
|
2729
2719
|
};
|
|
2730
2720
|
|
|
2721
|
+
function isWaapiSupportedEasing(easing) {
|
|
2722
|
+
return (!easing || // Default easing
|
|
2723
|
+
Array.isArray(easing) || // Bezier curve
|
|
2724
|
+
(typeof easing === "string" && supportedWaapiEasing[easing]));
|
|
2725
|
+
}
|
|
2726
|
+
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
2727
|
+
const supportedWaapiEasing = {
|
|
2728
|
+
linear: "linear",
|
|
2729
|
+
ease: "ease",
|
|
2730
|
+
easeIn: "ease-in",
|
|
2731
|
+
easeOut: "ease-out",
|
|
2732
|
+
easeInOut: "ease-in-out",
|
|
2733
|
+
circIn: cubicBezierAsString([0, 0.65, 0.55, 1]),
|
|
2734
|
+
circOut: cubicBezierAsString([0.55, 0, 1, 0.45]),
|
|
2735
|
+
backIn: cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
|
|
2736
|
+
backOut: cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
|
|
2737
|
+
};
|
|
2738
|
+
function mapEasingToNativeEasing(easing) {
|
|
2739
|
+
if (!easing)
|
|
2740
|
+
return undefined;
|
|
2741
|
+
return Array.isArray(easing)
|
|
2742
|
+
? cubicBezierAsString(easing)
|
|
2743
|
+
: supportedWaapiEasing[easing];
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
|
|
2747
|
+
const keyframeOptions = { [valueName]: keyframes };
|
|
2748
|
+
if (times)
|
|
2749
|
+
keyframeOptions.offset = times;
|
|
2750
|
+
return element.animate(keyframeOptions, {
|
|
2751
|
+
delay,
|
|
2752
|
+
duration,
|
|
2753
|
+
easing: mapEasingToNativeEasing(ease),
|
|
2754
|
+
fill: "both",
|
|
2755
|
+
iterations: repeat + 1,
|
|
2756
|
+
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
const featureTests = {
|
|
2761
|
+
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
2762
|
+
};
|
|
2763
|
+
const results = {};
|
|
2764
|
+
const supports = {};
|
|
2765
|
+
/**
|
|
2766
|
+
* Generate features tests that cache their results.
|
|
2767
|
+
*/
|
|
2768
|
+
for (const key in featureTests) {
|
|
2769
|
+
supports[key] = () => {
|
|
2770
|
+
if (results[key] === undefined)
|
|
2771
|
+
results[key] = featureTests[key]();
|
|
2772
|
+
return results[key];
|
|
2773
|
+
};
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }) {
|
|
2777
|
+
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
2778
|
+
? 0
|
|
2779
|
+
: keyframes.length - 1;
|
|
2780
|
+
return keyframes[index];
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2731
2783
|
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
2732
2784
|
// the second half of the animation. Turns easeIn into easeInOut.
|
|
2733
2785
|
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
@@ -3096,8 +3148,7 @@
|
|
|
3096
3148
|
function defaultEasing(values, easing) {
|
|
3097
3149
|
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
3098
3150
|
}
|
|
3099
|
-
function keyframes({ keyframes: keyframeValues,
|
|
3100
|
-
keyframeValues = [...keyframeValues];
|
|
3151
|
+
function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
|
|
3101
3152
|
/**
|
|
3102
3153
|
* Easing functions can be externally defined as strings. Here we convert them
|
|
3103
3154
|
* into actual functions.
|
|
@@ -3122,42 +3173,42 @@
|
|
|
3122
3173
|
times && times.length === keyframeValues.length
|
|
3123
3174
|
? times
|
|
3124
3175
|
: defaultOffset$1(keyframeValues), duration);
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
});
|
|
3131
|
-
}
|
|
3132
|
-
let interpolator = createInterpolator();
|
|
3176
|
+
const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
|
|
3177
|
+
ease: Array.isArray(easingFunctions)
|
|
3178
|
+
? easingFunctions
|
|
3179
|
+
: defaultEasing(keyframeValues, easingFunctions),
|
|
3180
|
+
});
|
|
3133
3181
|
return {
|
|
3182
|
+
calculatedDuration: duration,
|
|
3134
3183
|
next: (t) => {
|
|
3135
|
-
state.value =
|
|
3184
|
+
state.value = mapTimeToKeyframe(t);
|
|
3136
3185
|
state.done = t >= duration;
|
|
3137
3186
|
return state;
|
|
3138
3187
|
},
|
|
3139
|
-
flipTarget: () => {
|
|
3140
|
-
keyframeValues.reverse();
|
|
3141
|
-
interpolator = createInterpolator();
|
|
3142
|
-
},
|
|
3143
3188
|
};
|
|
3144
3189
|
}
|
|
3145
3190
|
|
|
3191
|
+
const velocitySampleDuration = 5; // ms
|
|
3192
|
+
function calcGeneratorVelocity(resolveValue, t, current) {
|
|
3193
|
+
const prevT = Math.max(t - velocitySampleDuration, 0);
|
|
3194
|
+
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3146
3197
|
const safeMin = 0.001;
|
|
3147
3198
|
const minDuration = 0.01;
|
|
3148
|
-
const maxDuration = 10.0;
|
|
3199
|
+
const maxDuration$2 = 10.0;
|
|
3149
3200
|
const minDamping = 0.05;
|
|
3150
3201
|
const maxDamping = 1;
|
|
3151
3202
|
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
3152
3203
|
let envelope;
|
|
3153
3204
|
let derivative;
|
|
3154
|
-
exports.warning(duration <= maxDuration
|
|
3205
|
+
exports.warning(duration <= secondsToMilliseconds(maxDuration$2), "Spring duration must be 10 seconds or less");
|
|
3155
3206
|
let dampingRatio = 1 - bounce;
|
|
3156
3207
|
/**
|
|
3157
3208
|
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
3158
3209
|
*/
|
|
3159
3210
|
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
3160
|
-
duration = clamp(minDuration, maxDuration, duration
|
|
3211
|
+
duration = clamp(minDuration, maxDuration$2, millisecondsToSeconds(duration));
|
|
3161
3212
|
if (dampingRatio < 1) {
|
|
3162
3213
|
/**
|
|
3163
3214
|
* Underdamped spring
|
|
@@ -3198,7 +3249,7 @@
|
|
|
3198
3249
|
}
|
|
3199
3250
|
const initialGuess = 5 / duration;
|
|
3200
3251
|
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
3201
|
-
duration = duration
|
|
3252
|
+
duration = secondsToMilliseconds(duration);
|
|
3202
3253
|
if (isNaN(undampedFreq)) {
|
|
3203
3254
|
return {
|
|
3204
3255
|
stiffness: 100,
|
|
@@ -3255,78 +3306,71 @@
|
|
|
3255
3306
|
}
|
|
3256
3307
|
return springOptions;
|
|
3257
3308
|
}
|
|
3258
|
-
const velocitySampleDuration = 5;
|
|
3259
|
-
/**
|
|
3260
|
-
* This is based on the spring implementation of Wobble https://github.com/skevy/wobble
|
|
3261
|
-
*/
|
|
3262
3309
|
function spring({ keyframes, restDelta, restSpeed, ...options }) {
|
|
3263
|
-
|
|
3264
|
-
|
|
3310
|
+
const origin = keyframes[0];
|
|
3311
|
+
const target = keyframes[keyframes.length - 1];
|
|
3265
3312
|
/**
|
|
3266
3313
|
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3267
3314
|
* to reduce GC during animation.
|
|
3268
3315
|
*/
|
|
3269
3316
|
const state = { done: false, value: origin };
|
|
3270
3317
|
const { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
|
|
3271
|
-
|
|
3272
|
-
let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
|
|
3318
|
+
const initialVelocity = velocity ? -millisecondsToSeconds(velocity) : 0.0;
|
|
3273
3319
|
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
(
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
};
|
|
3326
|
-
}
|
|
3320
|
+
const initialDelta = target - origin;
|
|
3321
|
+
const undampedAngularFreq = millisecondsToSeconds(Math.sqrt(stiffness / mass));
|
|
3322
|
+
/**
|
|
3323
|
+
* If we're working on a granular scale, use smaller defaults for determining
|
|
3324
|
+
* when the spring is finished.
|
|
3325
|
+
*
|
|
3326
|
+
* These defaults have been selected emprically based on what strikes a good
|
|
3327
|
+
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
|
3328
|
+
*/
|
|
3329
|
+
const isGranularScale = Math.abs(initialDelta) < 5;
|
|
3330
|
+
restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
|
|
3331
|
+
restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
|
|
3332
|
+
let resolveSpring;
|
|
3333
|
+
if (dampingRatio < 1) {
|
|
3334
|
+
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
3335
|
+
// Underdamped spring
|
|
3336
|
+
resolveSpring = (t) => {
|
|
3337
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3338
|
+
return (target -
|
|
3339
|
+
envelope *
|
|
3340
|
+
(((initialVelocity +
|
|
3341
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
3342
|
+
angularFreq) *
|
|
3343
|
+
Math.sin(angularFreq * t) +
|
|
3344
|
+
initialDelta * Math.cos(angularFreq * t)));
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
else if (dampingRatio === 1) {
|
|
3348
|
+
// Critically damped spring
|
|
3349
|
+
resolveSpring = (t) => target -
|
|
3350
|
+
Math.exp(-undampedAngularFreq * t) *
|
|
3351
|
+
(initialDelta +
|
|
3352
|
+
(initialVelocity + undampedAngularFreq * initialDelta) * t);
|
|
3353
|
+
}
|
|
3354
|
+
else {
|
|
3355
|
+
// Overdamped spring
|
|
3356
|
+
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
3357
|
+
resolveSpring = (t) => {
|
|
3358
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3359
|
+
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
3360
|
+
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
3361
|
+
return (target -
|
|
3362
|
+
(envelope *
|
|
3363
|
+
((initialVelocity +
|
|
3364
|
+
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
3365
|
+
Math.sinh(freqForT) +
|
|
3366
|
+
dampedAngularFreq *
|
|
3367
|
+
initialDelta *
|
|
3368
|
+
Math.cosh(freqForT))) /
|
|
3369
|
+
dampedAngularFreq);
|
|
3370
|
+
};
|
|
3327
3371
|
}
|
|
3328
|
-
createSpring();
|
|
3329
3372
|
return {
|
|
3373
|
+
calculatedDuration: isResolvedFromDuration ? duration || null : null,
|
|
3330
3374
|
next: (t) => {
|
|
3331
3375
|
const current = resolveSpring(t);
|
|
3332
3376
|
if (!isResolvedFromDuration) {
|
|
@@ -3338,8 +3382,7 @@
|
|
|
3338
3382
|
* checking only for displacement is enough.
|
|
3339
3383
|
*/
|
|
3340
3384
|
if (dampingRatio < 1) {
|
|
3341
|
-
|
|
3342
|
-
currentVelocity = velocityPerSecond(current - resolveSpring(prevT), t - prevT);
|
|
3385
|
+
currentVelocity = calcGeneratorVelocity(resolveSpring, t, current);
|
|
3343
3386
|
}
|
|
3344
3387
|
else {
|
|
3345
3388
|
currentVelocity = 0;
|
|
@@ -3356,29 +3399,23 @@
|
|
|
3356
3399
|
state.value = state.done ? target : current;
|
|
3357
3400
|
return state;
|
|
3358
3401
|
},
|
|
3359
|
-
flipTarget: () => {
|
|
3360
|
-
initialVelocity = -initialVelocity;
|
|
3361
|
-
[origin, target] = [target, origin];
|
|
3362
|
-
createSpring();
|
|
3363
|
-
},
|
|
3364
3402
|
};
|
|
3365
3403
|
}
|
|
3366
|
-
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
|
3367
|
-
const zero = (_t) => 0;
|
|
3368
3404
|
|
|
3369
|
-
function
|
|
3370
|
-
/**
|
|
3371
|
-
* The decay animation dynamically calculates an end of the animation
|
|
3372
|
-
* based on the initial keyframe, so we only need to define a single keyframe
|
|
3373
|
-
* as default.
|
|
3374
|
-
*/
|
|
3375
|
-
keyframes = [0], velocity = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
|
|
3405
|
+
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
|
3376
3406
|
const origin = keyframes[0];
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
const
|
|
3407
|
+
const state = {
|
|
3408
|
+
done: false,
|
|
3409
|
+
value: origin,
|
|
3410
|
+
};
|
|
3411
|
+
const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
3412
|
+
const nearestBoundary = (v) => {
|
|
3413
|
+
if (min === undefined)
|
|
3414
|
+
return max;
|
|
3415
|
+
if (max === undefined)
|
|
3416
|
+
return min;
|
|
3417
|
+
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
3418
|
+
};
|
|
3382
3419
|
let amplitude = power * velocity;
|
|
3383
3420
|
const ideal = origin + amplitude;
|
|
3384
3421
|
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
|
@@ -3388,233 +3425,270 @@
|
|
|
3388
3425
|
*/
|
|
3389
3426
|
if (target !== ideal)
|
|
3390
3427
|
amplitude = target - origin;
|
|
3428
|
+
const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
|
|
3429
|
+
const calcLatest = (t) => target + calcDelta(t);
|
|
3430
|
+
const applyFriction = (t) => {
|
|
3431
|
+
const delta = calcDelta(t);
|
|
3432
|
+
const latest = calcLatest(t);
|
|
3433
|
+
state.done = Math.abs(delta) <= restDelta;
|
|
3434
|
+
state.value = state.done ? target : latest;
|
|
3435
|
+
};
|
|
3436
|
+
/**
|
|
3437
|
+
* Ideally this would resolve for t in a stateless way, we could
|
|
3438
|
+
* do that by always precalculating the animation but as we know
|
|
3439
|
+
* this will be done anyway we can assume that spring will
|
|
3440
|
+
* be discovered during that.
|
|
3441
|
+
*/
|
|
3442
|
+
let timeReachedBoundary;
|
|
3443
|
+
let spring$1;
|
|
3444
|
+
const checkCatchBoundary = (t) => {
|
|
3445
|
+
if (!isOutOfBounds(state.value))
|
|
3446
|
+
return;
|
|
3447
|
+
timeReachedBoundary = t;
|
|
3448
|
+
spring$1 = spring({
|
|
3449
|
+
keyframes: [state.value, nearestBoundary(state.value)],
|
|
3450
|
+
velocity: calcGeneratorVelocity(calcLatest, t, state.value),
|
|
3451
|
+
damping: bounceDamping,
|
|
3452
|
+
stiffness: bounceStiffness,
|
|
3453
|
+
restDelta,
|
|
3454
|
+
restSpeed,
|
|
3455
|
+
});
|
|
3456
|
+
};
|
|
3457
|
+
checkCatchBoundary(0);
|
|
3391
3458
|
return {
|
|
3459
|
+
calculatedDuration: null,
|
|
3392
3460
|
next: (t) => {
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3461
|
+
/**
|
|
3462
|
+
* We need to resolve the friction to figure out if we need a
|
|
3463
|
+
* spring but we don't want to do this twice per frame. So here
|
|
3464
|
+
* we flag if we updated for this frame and later if we did
|
|
3465
|
+
* we can skip doing it again.
|
|
3466
|
+
*/
|
|
3467
|
+
let hasUpdatedFrame = false;
|
|
3468
|
+
if (!spring$1 && timeReachedBoundary === undefined) {
|
|
3469
|
+
hasUpdatedFrame = true;
|
|
3470
|
+
applyFriction(t);
|
|
3471
|
+
checkCatchBoundary(t);
|
|
3472
|
+
}
|
|
3473
|
+
/**
|
|
3474
|
+
* If we have a spring and the provided t is beyond the moment the friction
|
|
3475
|
+
* animation crossed the min/max boundary, use the spring.
|
|
3476
|
+
*/
|
|
3477
|
+
if (timeReachedBoundary !== undefined && t > timeReachedBoundary) {
|
|
3478
|
+
return spring$1.next(t - timeReachedBoundary);
|
|
3479
|
+
}
|
|
3480
|
+
else {
|
|
3481
|
+
!hasUpdatedFrame && applyFriction(t);
|
|
3482
|
+
return state;
|
|
3483
|
+
}
|
|
3397
3484
|
},
|
|
3398
|
-
flipTarget: () => { },
|
|
3399
3485
|
};
|
|
3400
3486
|
}
|
|
3401
3487
|
|
|
3402
|
-
const
|
|
3403
|
-
|
|
3404
|
-
keyframes: keyframes,
|
|
3405
|
-
tween: keyframes,
|
|
3406
|
-
spring,
|
|
3407
|
-
};
|
|
3408
|
-
function loopElapsed(elapsed, duration, delay = 0) {
|
|
3409
|
-
return elapsed - duration - delay;
|
|
3410
|
-
}
|
|
3411
|
-
function reverseElapsed(elapsed, duration = 0, delay = 0, isForwardPlayback = true) {
|
|
3412
|
-
return isForwardPlayback
|
|
3413
|
-
? loopElapsed(duration + -elapsed, duration, delay)
|
|
3414
|
-
: duration - (elapsed - duration) + delay;
|
|
3415
|
-
}
|
|
3416
|
-
function hasRepeatDelayElapsed(elapsed, duration, delay, isForwardPlayback) {
|
|
3417
|
-
return isForwardPlayback ? elapsed >= duration + delay : elapsed <= -delay;
|
|
3418
|
-
}
|
|
3419
|
-
const framesync = (update) => {
|
|
3420
|
-
const passTimestamp = ({ delta }) => update(delta);
|
|
3488
|
+
const frameloopDriver = (update) => {
|
|
3489
|
+
const passTimestamp = ({ timestamp }) => update(timestamp);
|
|
3421
3490
|
return {
|
|
3422
3491
|
start: () => sync.update(passTimestamp, true),
|
|
3423
3492
|
stop: () => cancelSync.update(passTimestamp),
|
|
3493
|
+
now: () => performance.now(),
|
|
3424
3494
|
};
|
|
3425
3495
|
};
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3496
|
+
|
|
3497
|
+
const types = {
|
|
3498
|
+
decay: inertia,
|
|
3499
|
+
inertia,
|
|
3500
|
+
tween: keyframes,
|
|
3501
|
+
keyframes: keyframes,
|
|
3502
|
+
spring,
|
|
3503
|
+
};
|
|
3504
|
+
/**
|
|
3505
|
+
* Implement a practical max duration for keyframe generation
|
|
3506
|
+
* to prevent infinite loops
|
|
3507
|
+
*/
|
|
3508
|
+
const maxDuration$1 = 20000;
|
|
3509
|
+
function calculateDuration(generator) {
|
|
3510
|
+
let duration = 0;
|
|
3511
|
+
const timeStep = 50;
|
|
3512
|
+
let state = generator.next(duration);
|
|
3513
|
+
while (!state.done && duration < maxDuration$1) {
|
|
3514
|
+
duration += timeStep;
|
|
3515
|
+
state = generator.next(duration);
|
|
3516
|
+
}
|
|
3517
|
+
return duration;
|
|
3518
|
+
}
|
|
3519
|
+
function animateValue({ autoplay = true, delay = 0, driver = frameloopDriver, keyframes: keyframes$1, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", onPlay, onStop, onComplete, onUpdate, ...options }) {
|
|
3520
|
+
let animationDriver;
|
|
3521
|
+
const generatorFactory = types[type] || keyframes;
|
|
3438
3522
|
/**
|
|
3439
|
-
* If this
|
|
3440
|
-
*
|
|
3523
|
+
* If this isn't the keyframes generator and we've been provided
|
|
3524
|
+
* strings as keyframes, we need to interpolate these.
|
|
3525
|
+
* TODO: Support velocity for units and complex value types/
|
|
3441
3526
|
*/
|
|
3442
|
-
|
|
3443
|
-
if (
|
|
3444
|
-
|
|
3527
|
+
let mapNumbersToKeyframes;
|
|
3528
|
+
if (generatorFactory !== keyframes &&
|
|
3529
|
+
typeof keyframes$1[0] !== "number") {
|
|
3530
|
+
mapNumbersToKeyframes = interpolate([0, 100], keyframes$1, {
|
|
3445
3531
|
clamp: false,
|
|
3446
3532
|
});
|
|
3447
3533
|
keyframes$1 = [0, 100];
|
|
3448
3534
|
}
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3535
|
+
const generator = generatorFactory({ ...options, keyframes: keyframes$1 });
|
|
3536
|
+
let mirroredGenerator;
|
|
3537
|
+
if (repeatType === "mirror") {
|
|
3538
|
+
mirroredGenerator = generatorFactory({
|
|
3539
|
+
...options,
|
|
3540
|
+
keyframes: [...keyframes$1].reverse(),
|
|
3541
|
+
velocity: -(options.velocity || 0),
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
let playState = "idle";
|
|
3545
|
+
let holdTime = null;
|
|
3546
|
+
let startTime = null;
|
|
3547
|
+
/**
|
|
3548
|
+
* If duration is undefined and we have repeat options,
|
|
3549
|
+
* we need to calculate a duration from the generator.
|
|
3550
|
+
*
|
|
3551
|
+
* We set it to the generator itself to cache the duration.
|
|
3552
|
+
* Any timeline resolver will need to have already precalculated
|
|
3553
|
+
* the duration by this step.
|
|
3554
|
+
*/
|
|
3555
|
+
if (generator.calculatedDuration === null && repeat) {
|
|
3556
|
+
generator.calculatedDuration = calculateDuration(generator);
|
|
3557
|
+
}
|
|
3558
|
+
const { calculatedDuration } = generator;
|
|
3559
|
+
let resolvedDuration = Infinity;
|
|
3560
|
+
let totalDuration = Infinity;
|
|
3561
|
+
if (calculatedDuration) {
|
|
3562
|
+
resolvedDuration = calculatedDuration + repeatDelay;
|
|
3563
|
+
totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
|
|
3564
|
+
}
|
|
3565
|
+
let currentTime = 0;
|
|
3566
|
+
const tick = (timestamp) => {
|
|
3567
|
+
if (startTime === null)
|
|
3568
|
+
return;
|
|
3569
|
+
if (holdTime !== null) {
|
|
3570
|
+
currentTime = holdTime;
|
|
3459
3571
|
}
|
|
3460
3572
|
else {
|
|
3461
|
-
|
|
3462
|
-
if (repeatType === "mirror")
|
|
3463
|
-
animation.flipTarget();
|
|
3573
|
+
currentTime = timestamp - startTime;
|
|
3464
3574
|
}
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
if (
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3575
|
+
// Rebase on delay
|
|
3576
|
+
currentTime = Math.max(currentTime - delay, 0);
|
|
3577
|
+
/**
|
|
3578
|
+
* If this animation has finished, set the current time
|
|
3579
|
+
* to the total duration.
|
|
3580
|
+
*/
|
|
3581
|
+
if (playState === "finished" && holdTime === null) {
|
|
3582
|
+
currentTime = totalDuration;
|
|
3583
|
+
}
|
|
3584
|
+
let elapsed = currentTime;
|
|
3585
|
+
let frameGenerator = generator;
|
|
3586
|
+
if (repeat) {
|
|
3587
|
+
/**
|
|
3588
|
+
* Get the current progress (0-1) of the animation. If t is >
|
|
3589
|
+
* than duration we'll get values like 2.5 (midway through the
|
|
3590
|
+
* third iteration)
|
|
3591
|
+
*/
|
|
3592
|
+
const progress = currentTime / resolvedDuration;
|
|
3593
|
+
/**
|
|
3594
|
+
* Get the current iteration (0 indexed). For instance the floor of
|
|
3595
|
+
* 2.5 is 2.
|
|
3596
|
+
*/
|
|
3597
|
+
let currentIteration = Math.floor(progress);
|
|
3598
|
+
/**
|
|
3599
|
+
* Get the current progress of the iteration by taking the remainder
|
|
3600
|
+
* so 2.5 is 0.5 through iteration 2
|
|
3601
|
+
*/
|
|
3602
|
+
let iterationProgress = progress % 1.0;
|
|
3603
|
+
/**
|
|
3604
|
+
* If iteration progress is 1 we count that as the end
|
|
3605
|
+
* of the previous iteration.
|
|
3606
|
+
*/
|
|
3607
|
+
if (!iterationProgress && progress >= 1) {
|
|
3608
|
+
iterationProgress = 1;
|
|
3490
3609
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3610
|
+
iterationProgress === 1 && currentIteration--;
|
|
3611
|
+
/**
|
|
3612
|
+
* Reverse progress if we're not running in "normal" direction
|
|
3613
|
+
*/
|
|
3614
|
+
const iterationIsOdd = currentIteration % 2;
|
|
3615
|
+
if (iterationIsOdd) {
|
|
3616
|
+
if (repeatType === "reverse") {
|
|
3617
|
+
iterationProgress = 1 - iterationProgress;
|
|
3618
|
+
if (repeatDelay) {
|
|
3619
|
+
iterationProgress -= repeatDelay / resolvedDuration;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
else if (repeatType === "mirror") {
|
|
3623
|
+
frameGenerator = mirroredGenerator;
|
|
3624
|
+
}
|
|
3493
3625
|
}
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3626
|
+
const p = currentTime >= totalDuration
|
|
3627
|
+
? repeatType === "reverse" && iterationIsOdd
|
|
3628
|
+
? 0
|
|
3629
|
+
: 1
|
|
3630
|
+
: clamp(0, 1, iterationProgress);
|
|
3631
|
+
elapsed = p * resolvedDuration;
|
|
3632
|
+
}
|
|
3633
|
+
const state = frameGenerator.next(elapsed);
|
|
3634
|
+
let { value, done } = state;
|
|
3635
|
+
if (onUpdate) {
|
|
3636
|
+
onUpdate(mapNumbersToKeyframes ? mapNumbersToKeyframes(value) : value);
|
|
3637
|
+
}
|
|
3638
|
+
if (calculatedDuration !== null) {
|
|
3639
|
+
done = currentTime >= totalDuration;
|
|
3640
|
+
}
|
|
3641
|
+
const isAnimationFinished = holdTime === null &&
|
|
3642
|
+
(playState === "finished" || (playState === "running" && done));
|
|
3643
|
+
if (isAnimationFinished) {
|
|
3644
|
+
playState = "finished";
|
|
3645
|
+
onComplete && onComplete();
|
|
3646
|
+
animationDriver && animationDriver.stop();
|
|
3647
|
+
}
|
|
3648
|
+
return state;
|
|
3649
|
+
};
|
|
3650
|
+
const play = () => {
|
|
3651
|
+
animationDriver = driver(tick);
|
|
3652
|
+
const now = animationDriver.now();
|
|
3497
3653
|
onPlay && onPlay();
|
|
3498
|
-
|
|
3499
|
-
|
|
3654
|
+
playState = "running";
|
|
3655
|
+
if (holdTime !== null) {
|
|
3656
|
+
startTime = now - holdTime;
|
|
3657
|
+
}
|
|
3658
|
+
else if (!startTime) {
|
|
3659
|
+
// TODO When implementing play/pause, check WAAPI
|
|
3660
|
+
// logic around finished animations
|
|
3661
|
+
startTime = now;
|
|
3662
|
+
}
|
|
3663
|
+
holdTime = null;
|
|
3664
|
+
animationDriver.start();
|
|
3665
|
+
};
|
|
3666
|
+
if (autoplay) {
|
|
3667
|
+
play();
|
|
3500
3668
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
onStop && onStop();
|
|
3505
|
-
driverControls && driverControls.stop();
|
|
3506
|
-
},
|
|
3507
|
-
/**
|
|
3508
|
-
* Set the current time of the animation. This is purposefully
|
|
3509
|
-
* mirroring the WAAPI animation API to make them interchanagable.
|
|
3510
|
-
* Going forward this file should be ported more towards
|
|
3511
|
-
* https://github.com/motiondivision/motionone/blob/main/packages/animation/src/Animation.ts
|
|
3512
|
-
* Which behaviourally adheres to WAAPI as far as possible.
|
|
3513
|
-
*
|
|
3514
|
-
* WARNING: This is not safe to use for most animations. We currently
|
|
3515
|
-
* only use it for handoff from WAAPI within Framer.
|
|
3516
|
-
*
|
|
3517
|
-
* This animation function consumes time every frame rather than being sampled for time.
|
|
3518
|
-
* So the sample() method performs some headless frames to ensure
|
|
3519
|
-
* repeats are handled correctly. Ideally in the future we will replace
|
|
3520
|
-
* that method with this, once repeat calculations are pure.
|
|
3521
|
-
*/
|
|
3522
|
-
set currentTime(t) {
|
|
3523
|
-
elapsed = initialElapsed;
|
|
3524
|
-
update(t);
|
|
3669
|
+
const controls = {
|
|
3670
|
+
get currentTime() {
|
|
3671
|
+
return millisecondsToSeconds(currentTime);
|
|
3525
3672
|
},
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
* temporal-resolution version.
|
|
3530
|
-
*
|
|
3531
|
-
* isControlled should be set to true if sample is being run within
|
|
3532
|
-
* a loop. This indicates that we're not arbitrarily sampling
|
|
3533
|
-
* the animation but running it one step after another. Therefore
|
|
3534
|
-
* we don't need to run a low-res version here. This is a stop-gap
|
|
3535
|
-
* until a rewrite can sample for time.
|
|
3536
|
-
*/
|
|
3537
|
-
sample: (t, isControlled = false) => {
|
|
3538
|
-
elapsed = initialElapsed;
|
|
3539
|
-
if (isControlled) {
|
|
3540
|
-
update(t);
|
|
3541
|
-
return state;
|
|
3673
|
+
set currentTime(newTime) {
|
|
3674
|
+
if (holdTime !== null || !animationDriver) {
|
|
3675
|
+
holdTime = 0;
|
|
3542
3676
|
}
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
let sampleElapsed = 0;
|
|
3547
|
-
update(0);
|
|
3548
|
-
while (sampleElapsed <= t) {
|
|
3549
|
-
const remaining = t - sampleElapsed;
|
|
3550
|
-
update(Math.min(remaining, sampleResolution));
|
|
3551
|
-
sampleElapsed += sampleResolution;
|
|
3677
|
+
else {
|
|
3678
|
+
startTime =
|
|
3679
|
+
animationDriver.now() - secondsToMilliseconds(newTime);
|
|
3552
3680
|
}
|
|
3553
|
-
|
|
3681
|
+
},
|
|
3682
|
+
stop: () => {
|
|
3683
|
+
onStop && onStop();
|
|
3684
|
+
animationDriver && animationDriver.stop();
|
|
3685
|
+
},
|
|
3686
|
+
sample: (elapsed) => {
|
|
3687
|
+
startTime = 0;
|
|
3688
|
+
return tick(elapsed);
|
|
3554
3689
|
},
|
|
3555
3690
|
};
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
function isWaapiSupportedEasing(easing) {
|
|
3559
|
-
return (!easing || // Default easing
|
|
3560
|
-
Array.isArray(easing) || // Bezier curve
|
|
3561
|
-
(typeof easing === "string" && supportedWaapiEasing[easing]));
|
|
3562
|
-
}
|
|
3563
|
-
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
3564
|
-
const supportedWaapiEasing = {
|
|
3565
|
-
linear: "linear",
|
|
3566
|
-
ease: "ease",
|
|
3567
|
-
easeIn: "ease-in",
|
|
3568
|
-
easeOut: "ease-out",
|
|
3569
|
-
easeInOut: "ease-in-out",
|
|
3570
|
-
circIn: cubicBezierAsString([0, 0.65, 0.55, 1]),
|
|
3571
|
-
circOut: cubicBezierAsString([0.55, 0, 1, 0.45]),
|
|
3572
|
-
backIn: cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
|
|
3573
|
-
backOut: cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
|
|
3574
|
-
};
|
|
3575
|
-
function mapEasingToNativeEasing(easing) {
|
|
3576
|
-
if (!easing)
|
|
3577
|
-
return undefined;
|
|
3578
|
-
return Array.isArray(easing)
|
|
3579
|
-
? cubicBezierAsString(easing)
|
|
3580
|
-
: supportedWaapiEasing[easing];
|
|
3581
|
-
}
|
|
3582
|
-
|
|
3583
|
-
function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
|
|
3584
|
-
const keyframeOptions = { [valueName]: keyframes };
|
|
3585
|
-
if (times)
|
|
3586
|
-
keyframeOptions.offset = times;
|
|
3587
|
-
return element.animate(keyframeOptions, {
|
|
3588
|
-
delay,
|
|
3589
|
-
duration,
|
|
3590
|
-
easing: mapEasingToNativeEasing(ease),
|
|
3591
|
-
fill: "both",
|
|
3592
|
-
iterations: repeat + 1,
|
|
3593
|
-
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
3594
|
-
});
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
|
-
const featureTests = {
|
|
3598
|
-
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
3599
|
-
};
|
|
3600
|
-
const results = {};
|
|
3601
|
-
const supports = {};
|
|
3602
|
-
/**
|
|
3603
|
-
* Generate features tests that cache their results.
|
|
3604
|
-
*/
|
|
3605
|
-
for (const key in featureTests) {
|
|
3606
|
-
supports[key] = () => {
|
|
3607
|
-
if (results[key] === undefined)
|
|
3608
|
-
results[key] = featureTests[key]();
|
|
3609
|
-
return results[key];
|
|
3610
|
-
};
|
|
3611
|
-
}
|
|
3612
|
-
|
|
3613
|
-
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }) {
|
|
3614
|
-
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
3615
|
-
? 0
|
|
3616
|
-
: keyframes.length - 1;
|
|
3617
|
-
return keyframes[index];
|
|
3691
|
+
return controls;
|
|
3618
3692
|
}
|
|
3619
3693
|
|
|
3620
3694
|
/**
|
|
@@ -3633,6 +3707,11 @@
|
|
|
3633
3707
|
* keyframe quantity.
|
|
3634
3708
|
*/
|
|
3635
3709
|
const sampleDelta = 10; //ms
|
|
3710
|
+
/**
|
|
3711
|
+
* Implement a practical max duration for keyframe generation
|
|
3712
|
+
* to prevent infinite loops
|
|
3713
|
+
*/
|
|
3714
|
+
const maxDuration = 20000;
|
|
3636
3715
|
const requiresPregeneratedKeyframes = (valueName, options) => options.type === "spring" ||
|
|
3637
3716
|
valueName === "backgroundColor" ||
|
|
3638
3717
|
!isWaapiSupportedEasing(options.ease);
|
|
@@ -3641,10 +3720,11 @@
|
|
|
3641
3720
|
acceleratedValues.has(valueName) &&
|
|
3642
3721
|
!options.repeatDelay &&
|
|
3643
3722
|
options.repeatType !== "mirror" &&
|
|
3644
|
-
options.damping !== 0
|
|
3723
|
+
options.damping !== 0 &&
|
|
3724
|
+
options.type !== "inertia";
|
|
3645
3725
|
if (!canAccelerateAnimation)
|
|
3646
3726
|
return false;
|
|
3647
|
-
let { keyframes, duration = 300,
|
|
3727
|
+
let { keyframes, duration = 300, ease } = options;
|
|
3648
3728
|
/**
|
|
3649
3729
|
* If this animation needs pre-generated keyframes then generate.
|
|
3650
3730
|
*/
|
|
@@ -3652,7 +3732,7 @@
|
|
|
3652
3732
|
const sampleAnimation = animateValue({
|
|
3653
3733
|
...options,
|
|
3654
3734
|
repeat: 0,
|
|
3655
|
-
|
|
3735
|
+
delay: 0,
|
|
3656
3736
|
});
|
|
3657
3737
|
let state = { done: false, value: keyframes[0] };
|
|
3658
3738
|
const pregeneratedKeyframes = [];
|
|
@@ -3661,8 +3741,8 @@
|
|
|
3661
3741
|
* we're heading for an infinite loop.
|
|
3662
3742
|
*/
|
|
3663
3743
|
let t = 0;
|
|
3664
|
-
while (!state.done && t <
|
|
3665
|
-
state = sampleAnimation.sample(t
|
|
3744
|
+
while (!state.done && t < maxDuration) {
|
|
3745
|
+
state = sampleAnimation.sample(t);
|
|
3666
3746
|
pregeneratedKeyframes.push(state.value);
|
|
3667
3747
|
t += sampleDelta;
|
|
3668
3748
|
}
|
|
@@ -3672,7 +3752,6 @@
|
|
|
3672
3752
|
}
|
|
3673
3753
|
const animation = animateStyle(value.owner.current, valueName, keyframes, {
|
|
3674
3754
|
...options,
|
|
3675
|
-
delay: -elapsed,
|
|
3676
3755
|
duration,
|
|
3677
3756
|
/**
|
|
3678
3757
|
* This function is currently not called if ease is provided
|
|
@@ -3702,10 +3781,10 @@
|
|
|
3702
3781
|
*/
|
|
3703
3782
|
return {
|
|
3704
3783
|
get currentTime() {
|
|
3705
|
-
return animation.currentTime || 0;
|
|
3784
|
+
return millisecondsToSeconds(animation.currentTime || 0);
|
|
3706
3785
|
},
|
|
3707
|
-
set currentTime(
|
|
3708
|
-
animation.currentTime =
|
|
3786
|
+
set currentTime(newTime) {
|
|
3787
|
+
animation.currentTime = secondsToMilliseconds(newTime);
|
|
3709
3788
|
},
|
|
3710
3789
|
stop: () => {
|
|
3711
3790
|
/**
|
|
@@ -3729,113 +3808,22 @@
|
|
|
3729
3808
|
};
|
|
3730
3809
|
}
|
|
3731
3810
|
|
|
3732
|
-
|
|
3733
|
-
* Timeout defined in ms
|
|
3734
|
-
*/
|
|
3735
|
-
function delay(callback, timeout) {
|
|
3736
|
-
const start = performance.now();
|
|
3737
|
-
const checkElapsed = ({ timestamp }) => {
|
|
3738
|
-
const elapsed = timestamp - start;
|
|
3739
|
-
if (elapsed >= timeout) {
|
|
3740
|
-
cancelSync.read(checkElapsed);
|
|
3741
|
-
callback(elapsed - timeout);
|
|
3742
|
-
}
|
|
3743
|
-
};
|
|
3744
|
-
sync.read(checkElapsed, true);
|
|
3745
|
-
return () => cancelSync.read(checkElapsed);
|
|
3746
|
-
}
|
|
3747
|
-
|
|
3748
|
-
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
3811
|
+
function createInstantAnimation({ keyframes, delay: delayBy, onUpdate, onComplete, }) {
|
|
3749
3812
|
const setValue = () => {
|
|
3750
3813
|
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
3751
3814
|
onComplete && onComplete();
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
|
|
3757
|
-
const origin = keyframes[0];
|
|
3758
|
-
let currentAnimation;
|
|
3759
|
-
function isOutOfBounds(v) {
|
|
3760
|
-
return (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
3761
|
-
}
|
|
3762
|
-
function findNearestBoundary(v) {
|
|
3763
|
-
if (min === undefined)
|
|
3764
|
-
return max;
|
|
3765
|
-
if (max === undefined)
|
|
3766
|
-
return min;
|
|
3767
|
-
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
3768
|
-
}
|
|
3769
|
-
function startAnimation(options) {
|
|
3770
|
-
currentAnimation && currentAnimation.stop();
|
|
3771
|
-
currentAnimation = animateValue({
|
|
3772
|
-
keyframes: [0, 1],
|
|
3773
|
-
velocity: 0,
|
|
3774
|
-
...options,
|
|
3775
|
-
driver,
|
|
3776
|
-
onUpdate: (v) => {
|
|
3777
|
-
onUpdate && onUpdate(v);
|
|
3778
|
-
options.onUpdate && options.onUpdate(v);
|
|
3779
|
-
},
|
|
3780
|
-
onComplete,
|
|
3781
|
-
onStop,
|
|
3782
|
-
});
|
|
3783
|
-
}
|
|
3784
|
-
function startSpring(options) {
|
|
3785
|
-
startAnimation({
|
|
3786
|
-
type: "spring",
|
|
3787
|
-
stiffness: bounceStiffness,
|
|
3788
|
-
damping: bounceDamping,
|
|
3789
|
-
restDelta,
|
|
3790
|
-
...options,
|
|
3791
|
-
});
|
|
3792
|
-
}
|
|
3793
|
-
if (isOutOfBounds(origin)) {
|
|
3794
|
-
// Start the animation with spring if outside the defined boundaries
|
|
3795
|
-
startSpring({
|
|
3796
|
-
velocity,
|
|
3797
|
-
keyframes: [origin, findNearestBoundary(origin)],
|
|
3798
|
-
});
|
|
3799
|
-
}
|
|
3800
|
-
else {
|
|
3801
|
-
/**
|
|
3802
|
-
* Or if the value is out of bounds, simulate the inertia movement
|
|
3803
|
-
* with the decay animation.
|
|
3804
|
-
*
|
|
3805
|
-
* Pre-calculate the target so we can detect if it's out-of-bounds.
|
|
3806
|
-
* If it is, we want to check per frame when to switch to a spring
|
|
3807
|
-
* animation
|
|
3808
|
-
*/
|
|
3809
|
-
let target = power * velocity + origin;
|
|
3810
|
-
if (typeof modifyTarget !== "undefined")
|
|
3811
|
-
target = modifyTarget(target);
|
|
3812
|
-
const boundary = findNearestBoundary(target);
|
|
3813
|
-
const heading = boundary === min ? -1 : 1;
|
|
3814
|
-
let prev;
|
|
3815
|
-
let current;
|
|
3816
|
-
const checkBoundary = (v) => {
|
|
3817
|
-
prev = current;
|
|
3818
|
-
current = v;
|
|
3819
|
-
velocity = velocityPerSecond(v - prev, frameData.delta);
|
|
3820
|
-
if ((heading === 1 && v > boundary) ||
|
|
3821
|
-
(heading === -1 && v < boundary)) {
|
|
3822
|
-
startSpring({ keyframes: [v, boundary], velocity });
|
|
3823
|
-
}
|
|
3815
|
+
return {
|
|
3816
|
+
stop: () => { },
|
|
3817
|
+
currentTime: 0,
|
|
3824
3818
|
};
|
|
3825
|
-
startAnimation({
|
|
3826
|
-
type: "decay",
|
|
3827
|
-
keyframes: [origin, 0],
|
|
3828
|
-
velocity,
|
|
3829
|
-
timeConstant,
|
|
3830
|
-
power,
|
|
3831
|
-
restDelta,
|
|
3832
|
-
modifyTarget,
|
|
3833
|
-
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
|
3834
|
-
});
|
|
3835
|
-
}
|
|
3836
|
-
return {
|
|
3837
|
-
stop: () => currentAnimation && currentAnimation.stop(),
|
|
3838
3819
|
};
|
|
3820
|
+
return delayBy
|
|
3821
|
+
? animateValue({
|
|
3822
|
+
keyframes: [0, 1],
|
|
3823
|
+
duration: delayBy,
|
|
3824
|
+
onComplete: setValue,
|
|
3825
|
+
})
|
|
3826
|
+
: setValue();
|
|
3839
3827
|
}
|
|
3840
3828
|
|
|
3841
3829
|
const underDampedSpring = {
|
|
@@ -3991,7 +3979,7 @@
|
|
|
3991
3979
|
keyframes,
|
|
3992
3980
|
velocity: value.getVelocity(),
|
|
3993
3981
|
...valueTransition,
|
|
3994
|
-
elapsed,
|
|
3982
|
+
delay: -elapsed,
|
|
3995
3983
|
onUpdate: (v) => {
|
|
3996
3984
|
value.set(v);
|
|
3997
3985
|
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
@@ -4011,13 +3999,6 @@
|
|
|
4011
3999
|
*/
|
|
4012
4000
|
return createInstantAnimation(options);
|
|
4013
4001
|
}
|
|
4014
|
-
else if (valueTransition.type === "inertia") {
|
|
4015
|
-
/**
|
|
4016
|
-
* If this is an inertia animation, we currently don't support pre-generating
|
|
4017
|
-
* keyframes for this as such it must always run on the main thread.
|
|
4018
|
-
*/
|
|
4019
|
-
return inertia(options);
|
|
4020
|
-
}
|
|
4021
4002
|
/**
|
|
4022
4003
|
* If there's no transition defined for this value, we can generate
|
|
4023
4004
|
* unqiue transition settings for this value.
|
|
@@ -4702,7 +4683,7 @@
|
|
|
4702
4683
|
if (!timestampedPoint) {
|
|
4703
4684
|
return { x: 0, y: 0 };
|
|
4704
4685
|
}
|
|
4705
|
-
const time = (lastPoint.timestamp - timestampedPoint.timestamp)
|
|
4686
|
+
const time = millisecondsToSeconds(lastPoint.timestamp - timestampedPoint.timestamp);
|
|
4706
4687
|
if (time === 0) {
|
|
4707
4688
|
return { x: 0, y: 0 };
|
|
4708
4689
|
}
|
|
@@ -5581,6 +5562,30 @@
|
|
|
5581
5562
|
}
|
|
5582
5563
|
}
|
|
5583
5564
|
|
|
5565
|
+
class GroupPlaybackControls {
|
|
5566
|
+
constructor(animations) {
|
|
5567
|
+
this.animations = animations.filter(Boolean);
|
|
5568
|
+
}
|
|
5569
|
+
/**
|
|
5570
|
+
* TODO: Filter out cancelled or stopped animations before returning
|
|
5571
|
+
*/
|
|
5572
|
+
get currentTime() {
|
|
5573
|
+
return this.animations[0].currentTime;
|
|
5574
|
+
}
|
|
5575
|
+
/**
|
|
5576
|
+
* currentTime assignment could reasonably run every frame, so
|
|
5577
|
+
* we iterate using a normal loop to avoid function creation.
|
|
5578
|
+
*/
|
|
5579
|
+
set currentTime(time) {
|
|
5580
|
+
for (let i = 0; i < this.animations.length; i++) {
|
|
5581
|
+
this.animations[i].currentTime = time;
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
stop() {
|
|
5585
|
+
this.animations.forEach((controls) => controls.stop());
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5584
5589
|
/**
|
|
5585
5590
|
* Animate a single value or a `MotionValue`.
|
|
5586
5591
|
*
|
|
@@ -5611,10 +5616,7 @@
|
|
|
5611
5616
|
function animate(from, to, transition = {}) {
|
|
5612
5617
|
const value = isMotionValue(from) ? from : motionValue(from);
|
|
5613
5618
|
value.start(createMotionValueAnimation("", value, to, transition));
|
|
5614
|
-
return
|
|
5615
|
-
stop: () => value.stop(),
|
|
5616
|
-
isAnimating: () => value.isAnimating(),
|
|
5617
|
-
};
|
|
5619
|
+
return value.animation || new GroupPlaybackControls([]);
|
|
5618
5620
|
}
|
|
5619
5621
|
|
|
5620
5622
|
const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
|
|
@@ -5959,6 +5961,22 @@
|
|
|
5959
5961
|
}
|
|
5960
5962
|
}
|
|
5961
5963
|
|
|
5964
|
+
/**
|
|
5965
|
+
* Timeout defined in ms
|
|
5966
|
+
*/
|
|
5967
|
+
function delay(callback, timeout) {
|
|
5968
|
+
const start = performance.now();
|
|
5969
|
+
const checkElapsed = ({ timestamp }) => {
|
|
5970
|
+
const elapsed = timestamp - start;
|
|
5971
|
+
if (elapsed >= timeout) {
|
|
5972
|
+
cancelSync.read(checkElapsed);
|
|
5973
|
+
callback(elapsed - timeout);
|
|
5974
|
+
}
|
|
5975
|
+
};
|
|
5976
|
+
sync.read(checkElapsed, true);
|
|
5977
|
+
return () => cancelSync.read(checkElapsed);
|
|
5978
|
+
}
|
|
5979
|
+
|
|
5962
5980
|
function record(data) {
|
|
5963
5981
|
if (window.MotionDebug) {
|
|
5964
5982
|
window.MotionDebug.record(data);
|
|
@@ -7862,7 +7880,7 @@
|
|
|
7862
7880
|
* and warn against mismatches.
|
|
7863
7881
|
*/
|
|
7864
7882
|
{
|
|
7865
|
-
warnOnce(nextValue.version === "10.2.
|
|
7883
|
+
warnOnce(nextValue.version === "10.2.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.2.4 may not work as expected.`);
|
|
7866
7884
|
}
|
|
7867
7885
|
}
|
|
7868
7886
|
else if (isMotionValue(prevValue)) {
|
|
@@ -9956,6 +9974,16 @@
|
|
|
9956
9974
|
return value;
|
|
9957
9975
|
}
|
|
9958
9976
|
|
|
9977
|
+
function useMotionValueEvent(value, event, callback) {
|
|
9978
|
+
/**
|
|
9979
|
+
* useInsertionEffect will create subscriptions before any other
|
|
9980
|
+
* effects will run. Effects run upwards through the tree so it
|
|
9981
|
+
* can be that binding a useLayoutEffect higher up the tree can
|
|
9982
|
+
* miss changes from lower down the tree.
|
|
9983
|
+
*/
|
|
9984
|
+
React.useInsertionEffect(() => value.on(event, callback), [value, event, callback]);
|
|
9985
|
+
}
|
|
9986
|
+
|
|
9959
9987
|
/**
|
|
9960
9988
|
* Creates a `MotionValue` that updates when the velocity of the provided `MotionValue` changes.
|
|
9961
9989
|
*
|
|
@@ -9969,11 +9997,9 @@
|
|
|
9969
9997
|
*/
|
|
9970
9998
|
function useVelocity(value) {
|
|
9971
9999
|
const velocity = useMotionValue(value.getVelocity());
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
});
|
|
9976
|
-
}, [value]);
|
|
10000
|
+
useMotionValueEvent(value, "velocityChange", (newVelocity) => {
|
|
10001
|
+
velocity.set(newVelocity);
|
|
10002
|
+
});
|
|
9977
10003
|
return velocity;
|
|
9978
10004
|
}
|
|
9979
10005
|
|
|
@@ -10092,16 +10118,6 @@
|
|
|
10092
10118
|
return useConstant(() => new WillChangeMotionValue("auto"));
|
|
10093
10119
|
}
|
|
10094
10120
|
|
|
10095
|
-
function useMotionValueEvent(value, event, callback) {
|
|
10096
|
-
/**
|
|
10097
|
-
* useInsertionEffect will create subscriptions before any other
|
|
10098
|
-
* effects will run. Effects run upwards through the tree so it
|
|
10099
|
-
* can be that binding a useLayoutEffect higher up the tree can
|
|
10100
|
-
* miss changes from lower down the tree.
|
|
10101
|
-
*/
|
|
10102
|
-
React.useInsertionEffect(() => value.on(event, callback), [value, event, callback]);
|
|
10103
|
-
}
|
|
10104
|
-
|
|
10105
10121
|
/**
|
|
10106
10122
|
* A hook that returns `true` if we should be using reduced motion based on the current device's Reduced Motion setting.
|
|
10107
10123
|
*
|
|
@@ -10523,7 +10539,8 @@
|
|
|
10523
10539
|
*/
|
|
10524
10540
|
sync.update(() => {
|
|
10525
10541
|
if (value.animation) {
|
|
10526
|
-
value.animation.currentTime =
|
|
10542
|
+
value.animation.currentTime =
|
|
10543
|
+
performance.now() - millisecondsToSeconds(sampledTime);
|
|
10527
10544
|
}
|
|
10528
10545
|
});
|
|
10529
10546
|
/**
|
|
@@ -10756,7 +10773,6 @@
|
|
|
10756
10773
|
exports.filterProps = filterProps;
|
|
10757
10774
|
exports.frameData = frameData;
|
|
10758
10775
|
exports.inView = inView;
|
|
10759
|
-
exports.inertia = inertia;
|
|
10760
10776
|
exports.interpolate = interpolate;
|
|
10761
10777
|
exports.isBrowser = isBrowser;
|
|
10762
10778
|
exports.isDragActive = isDragActive;
|