framer-motion 7.8.1 → 7.9.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/README.md +6 -5
- package/dist/cjs/index.js +146 -66
- package/dist/es/animation/index.mjs +14 -11
- package/dist/es/animation/legacy-popmotion/index.mjs +5 -2
- package/dist/es/animation/waapi/create-accelerated-animation.mjs +82 -0
- package/dist/es/animation/waapi/index.mjs +4 -6
- package/dist/es/render/VisualElement.mjs +1 -1
- package/dist/es/render/utils/motion-values.mjs +2 -2
- package/dist/es/render/utils/setters.mjs +1 -1
- package/dist/es/value/index.mjs +10 -4
- package/dist/framer-motion.dev.js +146 -66
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/projection.dev.js +146 -52
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-webpack-dom-animation.js +1 -1
- package/dist/size-webpack-dom-max.js +1 -1
- package/dist/three-entry.d.ts +1 -0
- package/package.json +8 -8
- package/dist/es/animation/create-accelerated-animation.mjs +0 -8
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ It does all this:
|
|
|
43
43
|
- SVG paths
|
|
44
44
|
- Exit animations
|
|
45
45
|
- Server-side rendering
|
|
46
|
+
- Hardware-accelerated animations
|
|
46
47
|
- Orchestrate animations across components
|
|
47
48
|
- CSS variables
|
|
48
49
|
|
|
@@ -70,17 +71,17 @@ export const MyComponent = ({ isVisible }) => (
|
|
|
70
71
|
|
|
71
72
|
### 📚 Docs
|
|
72
73
|
|
|
73
|
-
-
|
|
74
|
-
-
|
|
74
|
+
- Check out [our documentation](https://www.framer.com/docs/) for guides and a full API reference.
|
|
75
|
+
- Or see [our examples](https://www.framer.com/docs/examples/) for inspiration.
|
|
75
76
|
|
|
76
77
|
### 💎 Contribute
|
|
77
78
|
|
|
78
|
-
-
|
|
79
|
+
- Want to contribute to Framer Motion? Our [contributing guide](https://github.com/framer/motion/blob/master/CONTRIBUTING.md) has you covered.
|
|
79
80
|
|
|
80
81
|
### 👩🏻⚖️ License
|
|
81
82
|
|
|
82
|
-
-
|
|
83
|
+
- Framer Motion is MIT licensed.
|
|
83
84
|
|
|
84
85
|
### ✨ Framer
|
|
85
86
|
|
|
86
|
-
-
|
|
87
|
+
- Design and publish sites that inspire. [Try Framer for free](https://www.framer.com/).
|
package/dist/cjs/index.js
CHANGED
|
@@ -2119,12 +2119,12 @@ class MotionValue {
|
|
|
2119
2119
|
*
|
|
2120
2120
|
* @internal
|
|
2121
2121
|
*/
|
|
2122
|
-
constructor(init) {
|
|
2122
|
+
constructor(init, options = {}) {
|
|
2123
2123
|
/**
|
|
2124
2124
|
* This will be replaced by the build step with the latest version number.
|
|
2125
2125
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
2126
2126
|
*/
|
|
2127
|
-
this.version = "7.
|
|
2127
|
+
this.version = "7.9.0";
|
|
2128
2128
|
/**
|
|
2129
2129
|
* Duration, in milliseconds, since last updating frame.
|
|
2130
2130
|
*
|
|
@@ -2213,6 +2213,7 @@ class MotionValue {
|
|
|
2213
2213
|
this.hasAnimated = false;
|
|
2214
2214
|
this.prev = this.current = init;
|
|
2215
2215
|
this.canTrackVelocity = isFloat(this.current);
|
|
2216
|
+
this.owner = options.owner;
|
|
2216
2217
|
}
|
|
2217
2218
|
/**
|
|
2218
2219
|
* Adds a function that will be notified when the `MotionValue` is updated.
|
|
@@ -2312,6 +2313,11 @@ class MotionValue {
|
|
|
2312
2313
|
this.passiveEffect(v, this.updateAndNotify);
|
|
2313
2314
|
}
|
|
2314
2315
|
}
|
|
2316
|
+
setWithVelocity(prev, current, delta) {
|
|
2317
|
+
this.set(current);
|
|
2318
|
+
this.prev = prev;
|
|
2319
|
+
this.timeDelta = delta;
|
|
2320
|
+
}
|
|
2315
2321
|
/**
|
|
2316
2322
|
* Returns the latest state of `MotionValue`
|
|
2317
2323
|
*
|
|
@@ -2398,8 +2404,8 @@ class MotionValue {
|
|
|
2398
2404
|
this.stop();
|
|
2399
2405
|
}
|
|
2400
2406
|
}
|
|
2401
|
-
function motionValue(init) {
|
|
2402
|
-
return new MotionValue(init);
|
|
2407
|
+
function motionValue(init, options) {
|
|
2408
|
+
return new MotionValue(init, options);
|
|
2403
2409
|
}
|
|
2404
2410
|
|
|
2405
2411
|
/**
|
|
@@ -2765,7 +2771,7 @@ function checkTargetForNewValues(visualElement, target, origin) {
|
|
|
2765
2771
|
else if (!findValueType(value) && complex.test(targetValue)) {
|
|
2766
2772
|
value = getAnimatableNone(key, targetValue);
|
|
2767
2773
|
}
|
|
2768
|
-
visualElement.addValue(key, motionValue(value));
|
|
2774
|
+
visualElement.addValue(key, motionValue(value, { owner: visualElement }));
|
|
2769
2775
|
if (origin[key] === undefined) {
|
|
2770
2776
|
origin[key] = value;
|
|
2771
2777
|
}
|
|
@@ -2842,38 +2848,6 @@ const instantAnimationState = {
|
|
|
2842
2848
|
current: false,
|
|
2843
2849
|
};
|
|
2844
2850
|
|
|
2845
|
-
/**
|
|
2846
|
-
*
|
|
2847
|
-
*/
|
|
2848
|
-
function createAcceleratedAnimation() {
|
|
2849
|
-
return () => { };
|
|
2850
|
-
}
|
|
2851
|
-
|
|
2852
|
-
/**
|
|
2853
|
-
* Timeout defined in ms
|
|
2854
|
-
*/
|
|
2855
|
-
function delay(callback, timeout) {
|
|
2856
|
-
const start = performance.now();
|
|
2857
|
-
const checkElapsed = ({ timestamp }) => {
|
|
2858
|
-
const elapsed = timestamp - start;
|
|
2859
|
-
if (elapsed >= timeout) {
|
|
2860
|
-
cancelSync.read(checkElapsed);
|
|
2861
|
-
callback(elapsed - timeout);
|
|
2862
|
-
}
|
|
2863
|
-
};
|
|
2864
|
-
sync.read(checkElapsed, true);
|
|
2865
|
-
return () => cancelSync.read(checkElapsed);
|
|
2866
|
-
}
|
|
2867
|
-
|
|
2868
|
-
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
2869
|
-
const setValue = () => {
|
|
2870
|
-
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
2871
|
-
onComplete && onComplete();
|
|
2872
|
-
return () => { };
|
|
2873
|
-
};
|
|
2874
|
-
return elapsed ? delay(setValue, -elapsed) : setValue();
|
|
2875
|
-
}
|
|
2876
|
-
|
|
2877
2851
|
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
2878
2852
|
// the second half of the animation. Turns easeIn into easeInOut.
|
|
2879
2853
|
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
@@ -3562,7 +3536,7 @@ const framesync = (update) => {
|
|
|
3562
3536
|
stop: () => cancelSync.update(passTimestamp),
|
|
3563
3537
|
};
|
|
3564
3538
|
};
|
|
3565
|
-
function animate$1({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
3539
|
+
function animate$1({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, autoplay = true, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
3566
3540
|
var _a, _b;
|
|
3567
3541
|
let driverControls;
|
|
3568
3542
|
let repeatCount = 0;
|
|
@@ -3633,15 +3607,132 @@ function animate$1({ duration, driver = framesync, elapsed = 0, repeat: repeatMa
|
|
|
3633
3607
|
driverControls = driver(update);
|
|
3634
3608
|
driverControls.start();
|
|
3635
3609
|
}
|
|
3636
|
-
play();
|
|
3610
|
+
autoplay && play();
|
|
3637
3611
|
return {
|
|
3638
3612
|
stop: () => {
|
|
3639
3613
|
onStop && onStop();
|
|
3640
3614
|
driverControls.stop();
|
|
3641
3615
|
},
|
|
3616
|
+
sample: (t) => {
|
|
3617
|
+
return animation.next(Math.max(0, t)).value;
|
|
3618
|
+
},
|
|
3642
3619
|
};
|
|
3643
3620
|
}
|
|
3644
3621
|
|
|
3622
|
+
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
3623
|
+
|
|
3624
|
+
function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
|
|
3625
|
+
return element.animate({ [valueName]: keyframes, offset: times }, {
|
|
3626
|
+
delay,
|
|
3627
|
+
duration,
|
|
3628
|
+
easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
|
|
3629
|
+
fill: "both",
|
|
3630
|
+
iterations: repeat + 1,
|
|
3631
|
+
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
|
|
3635
|
+
/**
|
|
3636
|
+
* 10ms is chosen here as it strikes a balance between smooth
|
|
3637
|
+
* results (more than one keyframe per frame at 60fps) and
|
|
3638
|
+
* keyframe quantity.
|
|
3639
|
+
*/
|
|
3640
|
+
const sampleDelta = 10; //ms
|
|
3641
|
+
function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ...options }) {
|
|
3642
|
+
let { keyframes, duration = 0.3, elapsed = 0, ease } = options;
|
|
3643
|
+
/**
|
|
3644
|
+
* If this is a spring animation, pre-generate keyframes and
|
|
3645
|
+
* record duration.
|
|
3646
|
+
*
|
|
3647
|
+
* TODO: When introducing support for values beyond opacity it
|
|
3648
|
+
* might be better to use `animate.sample()`
|
|
3649
|
+
*/
|
|
3650
|
+
if (options.type === "spring") {
|
|
3651
|
+
const springAnimation = spring(options);
|
|
3652
|
+
let state = { done: false, value: keyframes[0] };
|
|
3653
|
+
const springKeyframes = [];
|
|
3654
|
+
let t = 0;
|
|
3655
|
+
while (!state.done) {
|
|
3656
|
+
state = springAnimation.next(t);
|
|
3657
|
+
springKeyframes.push(state.value);
|
|
3658
|
+
t += sampleDelta;
|
|
3659
|
+
}
|
|
3660
|
+
keyframes = springKeyframes;
|
|
3661
|
+
duration = t - sampleDelta;
|
|
3662
|
+
ease = "linear";
|
|
3663
|
+
}
|
|
3664
|
+
const animation = animateStyle(value.owner.current, valueName, keyframes, {
|
|
3665
|
+
...options,
|
|
3666
|
+
delay: -elapsed,
|
|
3667
|
+
duration,
|
|
3668
|
+
/**
|
|
3669
|
+
* This function is currently not called if ease is provided
|
|
3670
|
+
* as a function so the cast is safe.
|
|
3671
|
+
*
|
|
3672
|
+
* However it would be possible for a future refinement to port
|
|
3673
|
+
* in easing pregeneration from Motion One for browsers that
|
|
3674
|
+
* support the upcoming `linear()` easing function.
|
|
3675
|
+
*/
|
|
3676
|
+
ease: ease,
|
|
3677
|
+
});
|
|
3678
|
+
/**
|
|
3679
|
+
* Prefer the `onfinish` prop as it's more widely supported than
|
|
3680
|
+
* the `finished` promise.
|
|
3681
|
+
*
|
|
3682
|
+
* Here, we synchronously set the provided MotionValue to the end
|
|
3683
|
+
* keyframe. If we didn't, when the WAAPI animation is finished it would
|
|
3684
|
+
* be removed from the element which would then revert to its old styles.
|
|
3685
|
+
*/
|
|
3686
|
+
animation.onfinish = () => {
|
|
3687
|
+
value.set(keyframes[keyframes.length - 1]);
|
|
3688
|
+
onComplete && onComplete();
|
|
3689
|
+
};
|
|
3690
|
+
/**
|
|
3691
|
+
* Animation interrupt callback.
|
|
3692
|
+
*/
|
|
3693
|
+
return () => {
|
|
3694
|
+
/**
|
|
3695
|
+
* WAAPI doesn't natively have any interruption capabilities.
|
|
3696
|
+
*
|
|
3697
|
+
* Rather than read commited styles back out of the DOM, we can
|
|
3698
|
+
* create a renderless JS animation and sample it twice to calculate
|
|
3699
|
+
* its current value, "previous" value, and therefore allow
|
|
3700
|
+
* Motion to calculate velocity for any subsequent animation.
|
|
3701
|
+
*/
|
|
3702
|
+
const { currentTime } = animation;
|
|
3703
|
+
if (currentTime) {
|
|
3704
|
+
const sampleAnimation = animate$1(options);
|
|
3705
|
+
value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta), sampleAnimation.sample(currentTime), sampleDelta);
|
|
3706
|
+
}
|
|
3707
|
+
sync.update(() => animation.cancel());
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
/**
|
|
3712
|
+
* Timeout defined in ms
|
|
3713
|
+
*/
|
|
3714
|
+
function delay(callback, timeout) {
|
|
3715
|
+
const start = performance.now();
|
|
3716
|
+
const checkElapsed = ({ timestamp }) => {
|
|
3717
|
+
const elapsed = timestamp - start;
|
|
3718
|
+
if (elapsed >= timeout) {
|
|
3719
|
+
cancelSync.read(checkElapsed);
|
|
3720
|
+
callback(elapsed - timeout);
|
|
3721
|
+
}
|
|
3722
|
+
};
|
|
3723
|
+
sync.read(checkElapsed, true);
|
|
3724
|
+
return () => cancelSync.read(checkElapsed);
|
|
3725
|
+
}
|
|
3726
|
+
|
|
3727
|
+
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
3728
|
+
const setValue = () => {
|
|
3729
|
+
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
3730
|
+
onComplete && onComplete();
|
|
3731
|
+
return () => { };
|
|
3732
|
+
};
|
|
3733
|
+
return elapsed ? delay(setValue, -elapsed) : setValue();
|
|
3734
|
+
}
|
|
3735
|
+
|
|
3645
3736
|
function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
|
|
3646
3737
|
const origin = keyframes[0];
|
|
3647
3738
|
let currentAnimation;
|
|
@@ -3880,7 +3971,7 @@ for (const key in featureTests) {
|
|
|
3880
3971
|
/**
|
|
3881
3972
|
* A list of values that can be hardware-accelerated.
|
|
3882
3973
|
*/
|
|
3883
|
-
const acceleratedValues = new Set([]);
|
|
3974
|
+
const acceleratedValues = new Set(["opacity"]);
|
|
3884
3975
|
const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
|
|
3885
3976
|
return (onComplete) => {
|
|
3886
3977
|
const valueTransition = getValueTransition(transition, valueName) || {};
|
|
@@ -3960,19 +4051,22 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
|
|
|
3960
4051
|
if (options.repeatDelay) {
|
|
3961
4052
|
options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
|
|
3962
4053
|
}
|
|
3963
|
-
const
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
!options.
|
|
4054
|
+
const visualElement = value.owner;
|
|
4055
|
+
const element = visualElement && visualElement.current;
|
|
4056
|
+
const canAccelerateAnimation = supports.waapi() &&
|
|
4057
|
+
acceleratedValues.has(valueName) &&
|
|
4058
|
+
!options.repeatDelay &&
|
|
4059
|
+
options.repeatType !== "mirror" &&
|
|
4060
|
+
options.damping !== 0 &&
|
|
4061
|
+
typeof options.ease !== "function" &&
|
|
4062
|
+
visualElement &&
|
|
4063
|
+
element instanceof HTMLElement &&
|
|
4064
|
+
!visualElement.getProps().onUpdate;
|
|
3968
4065
|
if (canAccelerateAnimation) {
|
|
3969
4066
|
/**
|
|
3970
4067
|
* If this animation is capable of being run via WAAPI, then do so.
|
|
3971
|
-
*
|
|
3972
|
-
* TODO: Currently no values are hardware accelerated so this clause
|
|
3973
|
-
* will never trigger. Animation to be added in subsequent PR.
|
|
3974
4068
|
*/
|
|
3975
|
-
return createAcceleratedAnimation();
|
|
4069
|
+
return createAcceleratedAnimation(value, valueName, options);
|
|
3976
4070
|
}
|
|
3977
4071
|
else {
|
|
3978
4072
|
/**
|
|
@@ -5833,7 +5927,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5833
5927
|
* and warn against mismatches.
|
|
5834
5928
|
*/
|
|
5835
5929
|
if (process.env.NODE_ENV === "development") {
|
|
5836
|
-
warnOnce(nextValue.version === "7.
|
|
5930
|
+
warnOnce(nextValue.version === "7.9.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.9.0 may not work as expected.`);
|
|
5837
5931
|
}
|
|
5838
5932
|
}
|
|
5839
5933
|
else if (isMotionValue(prevValue)) {
|
|
@@ -5841,7 +5935,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5841
5935
|
* If we're swapping from a motion value to a static value,
|
|
5842
5936
|
* create a new motion value from that
|
|
5843
5937
|
*/
|
|
5844
|
-
element.addValue(key, motionValue(nextValue));
|
|
5938
|
+
element.addValue(key, motionValue(nextValue, { owner: element }));
|
|
5845
5939
|
if (isWillChangeMotionValue(willChange)) {
|
|
5846
5940
|
willChange.remove(key);
|
|
5847
5941
|
}
|
|
@@ -6256,7 +6350,7 @@ class VisualElement {
|
|
|
6256
6350
|
}
|
|
6257
6351
|
let value = this.values.get(key);
|
|
6258
6352
|
if (value === undefined && defaultValue !== undefined) {
|
|
6259
|
-
value = motionValue(defaultValue);
|
|
6353
|
+
value = motionValue(defaultValue, { owner: this });
|
|
6260
6354
|
this.addValue(key, value);
|
|
6261
6355
|
}
|
|
6262
6356
|
return value;
|
|
@@ -9652,20 +9746,6 @@ function useResetProjection() {
|
|
|
9652
9746
|
return reset;
|
|
9653
9747
|
}
|
|
9654
9748
|
|
|
9655
|
-
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
9656
|
-
|
|
9657
|
-
function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
|
|
9658
|
-
if (!supports.waapi())
|
|
9659
|
-
return undefined;
|
|
9660
|
-
const animation = element.animate({ [valueName]: keyframes }, {
|
|
9661
|
-
delay,
|
|
9662
|
-
duration,
|
|
9663
|
-
easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
|
|
9664
|
-
fill: "both",
|
|
9665
|
-
});
|
|
9666
|
-
return animation;
|
|
9667
|
-
}
|
|
9668
|
-
|
|
9669
9749
|
function startOptimizedAppearAnimation(element, name, keyframes, options) {
|
|
9670
9750
|
window.MotionAppearAnimations || (window.MotionAppearAnimations = new Map());
|
|
9671
9751
|
const id = element.dataset[optimizedAppearDataId];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { warning } from 'hey-listen';
|
|
2
2
|
import { secondsToMilliseconds } from '../utils/time-conversion.mjs';
|
|
3
3
|
import { instantAnimationState } from '../utils/use-instant-transition-state.mjs';
|
|
4
|
-
import { createAcceleratedAnimation } from './create-accelerated-animation.mjs';
|
|
4
|
+
import { createAcceleratedAnimation } from './waapi/create-accelerated-animation.mjs';
|
|
5
5
|
import { createInstantAnimation } from './create-instant-animation.mjs';
|
|
6
6
|
import { animate } from './legacy-popmotion/index.mjs';
|
|
7
7
|
import { inertia } from './legacy-popmotion/inertia.mjs';
|
|
@@ -14,7 +14,7 @@ import { supports } from './waapi/supports.mjs';
|
|
|
14
14
|
/**
|
|
15
15
|
* A list of values that can be hardware-accelerated.
|
|
16
16
|
*/
|
|
17
|
-
const acceleratedValues = new Set([]);
|
|
17
|
+
const acceleratedValues = new Set(["opacity"]);
|
|
18
18
|
const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
|
|
19
19
|
return (onComplete) => {
|
|
20
20
|
const valueTransition = getValueTransition(transition, valueName) || {};
|
|
@@ -94,19 +94,22 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
|
|
|
94
94
|
if (options.repeatDelay) {
|
|
95
95
|
options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
|
|
96
96
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
!options.
|
|
97
|
+
const visualElement = value.owner;
|
|
98
|
+
const element = visualElement && visualElement.current;
|
|
99
|
+
const canAccelerateAnimation = supports.waapi() &&
|
|
100
|
+
acceleratedValues.has(valueName) &&
|
|
101
|
+
!options.repeatDelay &&
|
|
102
|
+
options.repeatType !== "mirror" &&
|
|
103
|
+
options.damping !== 0 &&
|
|
104
|
+
typeof options.ease !== "function" &&
|
|
105
|
+
visualElement &&
|
|
106
|
+
element instanceof HTMLElement &&
|
|
107
|
+
!visualElement.getProps().onUpdate;
|
|
102
108
|
if (canAccelerateAnimation) {
|
|
103
109
|
/**
|
|
104
110
|
* If this animation is capable of being run via WAAPI, then do so.
|
|
105
|
-
*
|
|
106
|
-
* TODO: Currently no values are hardware accelerated so this clause
|
|
107
|
-
* will never trigger. Animation to be added in subsequent PR.
|
|
108
111
|
*/
|
|
109
|
-
return createAcceleratedAnimation();
|
|
112
|
+
return createAcceleratedAnimation(value, valueName, options);
|
|
110
113
|
}
|
|
111
114
|
else {
|
|
112
115
|
/**
|
|
@@ -28,7 +28,7 @@ const framesync = (update) => {
|
|
|
28
28
|
stop: () => cancelSync.update(passTimestamp),
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
31
|
+
function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes, autoplay = true, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
32
32
|
var _a, _b;
|
|
33
33
|
let driverControls;
|
|
34
34
|
let repeatCount = 0;
|
|
@@ -99,12 +99,15 @@ function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax
|
|
|
99
99
|
driverControls = driver(update);
|
|
100
100
|
driverControls.start();
|
|
101
101
|
}
|
|
102
|
-
play();
|
|
102
|
+
autoplay && play();
|
|
103
103
|
return {
|
|
104
104
|
stop: () => {
|
|
105
105
|
onStop && onStop();
|
|
106
106
|
driverControls.stop();
|
|
107
107
|
},
|
|
108
|
+
sample: (t) => {
|
|
109
|
+
return animation.next(Math.max(0, t)).value;
|
|
110
|
+
},
|
|
108
111
|
};
|
|
109
112
|
}
|
|
110
113
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { sync } from '../../frameloop/index.mjs';
|
|
2
|
+
import { animate } from '../legacy-popmotion/index.mjs';
|
|
3
|
+
import { spring } from '../legacy-popmotion/spring.mjs';
|
|
4
|
+
import { animateStyle } from './index.mjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 10ms is chosen here as it strikes a balance between smooth
|
|
8
|
+
* results (more than one keyframe per frame at 60fps) and
|
|
9
|
+
* keyframe quantity.
|
|
10
|
+
*/
|
|
11
|
+
const sampleDelta = 10; //ms
|
|
12
|
+
function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ...options }) {
|
|
13
|
+
let { keyframes, duration = 0.3, elapsed = 0, ease } = options;
|
|
14
|
+
/**
|
|
15
|
+
* If this is a spring animation, pre-generate keyframes and
|
|
16
|
+
* record duration.
|
|
17
|
+
*
|
|
18
|
+
* TODO: When introducing support for values beyond opacity it
|
|
19
|
+
* might be better to use `animate.sample()`
|
|
20
|
+
*/
|
|
21
|
+
if (options.type === "spring") {
|
|
22
|
+
const springAnimation = spring(options);
|
|
23
|
+
let state = { done: false, value: keyframes[0] };
|
|
24
|
+
const springKeyframes = [];
|
|
25
|
+
let t = 0;
|
|
26
|
+
while (!state.done) {
|
|
27
|
+
state = springAnimation.next(t);
|
|
28
|
+
springKeyframes.push(state.value);
|
|
29
|
+
t += sampleDelta;
|
|
30
|
+
}
|
|
31
|
+
keyframes = springKeyframes;
|
|
32
|
+
duration = t - sampleDelta;
|
|
33
|
+
ease = "linear";
|
|
34
|
+
}
|
|
35
|
+
const animation = animateStyle(value.owner.current, valueName, keyframes, {
|
|
36
|
+
...options,
|
|
37
|
+
delay: -elapsed,
|
|
38
|
+
duration,
|
|
39
|
+
/**
|
|
40
|
+
* This function is currently not called if ease is provided
|
|
41
|
+
* as a function so the cast is safe.
|
|
42
|
+
*
|
|
43
|
+
* However it would be possible for a future refinement to port
|
|
44
|
+
* in easing pregeneration from Motion One for browsers that
|
|
45
|
+
* support the upcoming `linear()` easing function.
|
|
46
|
+
*/
|
|
47
|
+
ease: ease,
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* Prefer the `onfinish` prop as it's more widely supported than
|
|
51
|
+
* the `finished` promise.
|
|
52
|
+
*
|
|
53
|
+
* Here, we synchronously set the provided MotionValue to the end
|
|
54
|
+
* keyframe. If we didn't, when the WAAPI animation is finished it would
|
|
55
|
+
* be removed from the element which would then revert to its old styles.
|
|
56
|
+
*/
|
|
57
|
+
animation.onfinish = () => {
|
|
58
|
+
value.set(keyframes[keyframes.length - 1]);
|
|
59
|
+
onComplete && onComplete();
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Animation interrupt callback.
|
|
63
|
+
*/
|
|
64
|
+
return () => {
|
|
65
|
+
/**
|
|
66
|
+
* WAAPI doesn't natively have any interruption capabilities.
|
|
67
|
+
*
|
|
68
|
+
* Rather than read commited styles back out of the DOM, we can
|
|
69
|
+
* create a renderless JS animation and sample it twice to calculate
|
|
70
|
+
* its current value, "previous" value, and therefore allow
|
|
71
|
+
* Motion to calculate velocity for any subsequent animation.
|
|
72
|
+
*/
|
|
73
|
+
const { currentTime } = animation;
|
|
74
|
+
if (currentTime) {
|
|
75
|
+
const sampleAnimation = animate(options);
|
|
76
|
+
value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta), sampleAnimation.sample(currentTime), sampleDelta);
|
|
77
|
+
}
|
|
78
|
+
sync.update(() => animation.cancel());
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { createAcceleratedAnimation };
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { supports } from './supports.mjs';
|
|
2
1
|
import { cubicBezierAsString } from './easing.mjs';
|
|
3
2
|
|
|
4
|
-
function animateStyle(element, valueName, keyframes, { delay, duration, ease }) {
|
|
5
|
-
|
|
6
|
-
return undefined;
|
|
7
|
-
const animation = element.animate({ [valueName]: keyframes }, {
|
|
3
|
+
function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
|
|
4
|
+
return element.animate({ [valueName]: keyframes, offset: times }, {
|
|
8
5
|
delay,
|
|
9
6
|
duration,
|
|
10
7
|
easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
|
|
11
8
|
fill: "both",
|
|
9
|
+
iterations: repeat + 1,
|
|
10
|
+
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
12
11
|
});
|
|
13
|
-
return animation;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export { animateStyle };
|
|
@@ -403,7 +403,7 @@ class VisualElement {
|
|
|
403
403
|
}
|
|
404
404
|
let value = this.values.get(key);
|
|
405
405
|
if (value === undefined && defaultValue !== undefined) {
|
|
406
|
-
value = motionValue(defaultValue);
|
|
406
|
+
value = motionValue(defaultValue, { owner: this });
|
|
407
407
|
this.addValue(key, value);
|
|
408
408
|
}
|
|
409
409
|
return value;
|
|
@@ -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 === "7.
|
|
25
|
+
warnOnce(nextValue.version === "7.9.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.9.0 may not work as expected.`);
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
else if (isMotionValue(prevValue)) {
|
|
@@ -30,7 +30,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
30
30
|
* If we're swapping from a motion value to a static value,
|
|
31
31
|
* create a new motion value from that
|
|
32
32
|
*/
|
|
33
|
-
element.addValue(key, motionValue(nextValue));
|
|
33
|
+
element.addValue(key, motionValue(nextValue, { owner: element }));
|
|
34
34
|
if (isWillChangeMotionValue(willChange)) {
|
|
35
35
|
willChange.remove(key);
|
|
36
36
|
}
|
|
@@ -89,7 +89,7 @@ function checkTargetForNewValues(visualElement, target, origin) {
|
|
|
89
89
|
else if (!findValueType(value) && complex.test(targetValue)) {
|
|
90
90
|
value = getAnimatableNone(key, targetValue);
|
|
91
91
|
}
|
|
92
|
-
visualElement.addValue(key, motionValue(value));
|
|
92
|
+
visualElement.addValue(key, motionValue(value, { owner: visualElement }));
|
|
93
93
|
if (origin[key] === undefined) {
|
|
94
94
|
origin[key] = value;
|
|
95
95
|
}
|
package/dist/es/value/index.mjs
CHANGED
|
@@ -20,12 +20,12 @@ class MotionValue {
|
|
|
20
20
|
*
|
|
21
21
|
* @internal
|
|
22
22
|
*/
|
|
23
|
-
constructor(init) {
|
|
23
|
+
constructor(init, options = {}) {
|
|
24
24
|
/**
|
|
25
25
|
* This will be replaced by the build step with the latest version number.
|
|
26
26
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
27
27
|
*/
|
|
28
|
-
this.version = "7.
|
|
28
|
+
this.version = "7.9.0";
|
|
29
29
|
/**
|
|
30
30
|
* Duration, in milliseconds, since last updating frame.
|
|
31
31
|
*
|
|
@@ -114,6 +114,7 @@ class MotionValue {
|
|
|
114
114
|
this.hasAnimated = false;
|
|
115
115
|
this.prev = this.current = init;
|
|
116
116
|
this.canTrackVelocity = isFloat(this.current);
|
|
117
|
+
this.owner = options.owner;
|
|
117
118
|
}
|
|
118
119
|
/**
|
|
119
120
|
* Adds a function that will be notified when the `MotionValue` is updated.
|
|
@@ -213,6 +214,11 @@ class MotionValue {
|
|
|
213
214
|
this.passiveEffect(v, this.updateAndNotify);
|
|
214
215
|
}
|
|
215
216
|
}
|
|
217
|
+
setWithVelocity(prev, current, delta) {
|
|
218
|
+
this.set(current);
|
|
219
|
+
this.prev = prev;
|
|
220
|
+
this.timeDelta = delta;
|
|
221
|
+
}
|
|
216
222
|
/**
|
|
217
223
|
* Returns the latest state of `MotionValue`
|
|
218
224
|
*
|
|
@@ -299,8 +305,8 @@ class MotionValue {
|
|
|
299
305
|
this.stop();
|
|
300
306
|
}
|
|
301
307
|
}
|
|
302
|
-
function motionValue(init) {
|
|
303
|
-
return new MotionValue(init);
|
|
308
|
+
function motionValue(init, options) {
|
|
309
|
+
return new MotionValue(init, options);
|
|
304
310
|
}
|
|
305
311
|
|
|
306
312
|
export { MotionValue, motionValue };
|