framer-motion 8.4.4 → 8.4.5

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.
@@ -79,11 +79,19 @@
79
79
  visualElement && visualElement.render();
80
80
  });
81
81
  /**
82
- * If we have optimised appear animations to handoff from, trigger animateChanges
83
- * from a synchronous useLayoutEffect to ensure there's no flash of incorrectly
84
- * styled component in the event of a hydration error.
82
+ * Ideally this function would always run in a useEffect.
83
+ *
84
+ * However, if we have optimised appear animations to handoff from,
85
+ * it needs to happen synchronously to ensure there's no flash of
86
+ * incorrect styles in the event of a hydration error.
87
+ *
88
+ * So if we detect a situtation where optimised appear animations
89
+ * are running, we use useLayoutEffect to trigger animations.
85
90
  */
86
- useIsomorphicLayoutEffect(() => {
91
+ const useAnimateChangesEffect = window.MotionAppearAnimations
92
+ ? useIsomorphicLayoutEffect
93
+ : React.useEffect;
94
+ useAnimateChangesEffect(() => {
87
95
  if (visualElement && visualElement.animationState) {
88
96
  visualElement.animationState.animateChanges();
89
97
  }
@@ -2079,7 +2087,7 @@
2079
2087
  * This will be replaced by the build step with the latest version number.
2080
2088
  * When MotionValues are provided to motion components, warn if versions are mixed.
2081
2089
  */
2082
- this.version = "8.4.4";
2090
+ this.version = "8.4.5";
2083
2091
  /**
2084
2092
  * Duration, in milliseconds, since last updating frame.
2085
2093
  *
@@ -2318,11 +2326,11 @@
2318
2326
  *
2319
2327
  * @internal
2320
2328
  */
2321
- start(animation) {
2329
+ start(startAnimation) {
2322
2330
  this.stop();
2323
2331
  return new Promise((resolve) => {
2324
2332
  this.hasAnimated = true;
2325
- this.stopAnimation = animation(resolve);
2333
+ this.animation = startAnimation(resolve) || null;
2326
2334
  if (this.events.animationStart) {
2327
2335
  this.events.animationStart.notify();
2328
2336
  }
@@ -2339,8 +2347,8 @@
2339
2347
  * @public
2340
2348
  */
2341
2349
  stop() {
2342
- if (this.stopAnimation) {
2343
- this.stopAnimation();
2350
+ if (this.animation) {
2351
+ this.animation.stop();
2344
2352
  if (this.events.animationCancel) {
2345
2353
  this.events.animationCancel.notify();
2346
2354
  }
@@ -2353,10 +2361,10 @@
2353
2361
  * @public
2354
2362
  */
2355
2363
  isAnimating() {
2356
- return !!this.stopAnimation;
2364
+ return !!this.animation;
2357
2365
  }
2358
2366
  clearAnimation() {
2359
- this.stopAnimation = null;
2367
+ this.animation = null;
2360
2368
  }
2361
2369
  /**
2362
2370
  * Destroy and clean up subscribers to this `MotionValue`.
@@ -2775,11 +2783,28 @@
2775
2783
 
2776
2784
  const appearStoreId = (id, value) => `${id}: ${value}`;
2777
2785
 
2778
- function handoffOptimizedAppearAnimation(id, name) {
2786
+ function handoffOptimizedAppearAnimation(id, name, value) {
2779
2787
  const { MotionAppearAnimations } = window;
2780
2788
  const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
2781
2789
  const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
2782
2790
  if (animation) {
2791
+ const sampledTime = performance.now();
2792
+ /**
2793
+ * Resync handoff animation with optimised animation.
2794
+ *
2795
+ * This step would be unnecessary if we triggered animateChanges() in useEffect,
2796
+ * but due to potential hydration errors we currently fire them in useLayoutEffect.
2797
+ *
2798
+ * By the time we're safely ready to cancel the optimised WAAPI animation,
2799
+ * the main thread might have been blocked and desynced the two animations.
2800
+ *
2801
+ * Here, we resync the two animations before the optimised WAAPI animation is cancelled.
2802
+ */
2803
+ sync.update(() => {
2804
+ if (value.animation) {
2805
+ value.animation.currentTime = performance.now() - sampledTime;
2806
+ }
2807
+ });
2783
2808
  /**
2784
2809
  * We allow the animation to persist until the next frame:
2785
2810
  * 1. So it continues to play until Framer Motion is ready to render
@@ -2788,12 +2813,12 @@
2788
2813
  * it synchronously would prevent subsequent transforms from handing off.
2789
2814
  */
2790
2815
  sync.render(() => {
2816
+ MotionAppearAnimations.delete(animationId);
2791
2817
  /**
2792
2818
  * Animation.cancel() throws so it needs to be wrapped in a try/catch
2793
2819
  */
2794
2820
  try {
2795
2821
  animation.cancel();
2796
- MotionAppearAnimations.delete(animationId);
2797
2822
  }
2798
2823
  catch (e) { }
2799
2824
  });
@@ -3594,6 +3619,25 @@
3594
3619
  onStop && onStop();
3595
3620
  driverControls && driverControls.stop();
3596
3621
  },
3622
+ /**
3623
+ * Set the current time of the animation. This is purposefully
3624
+ * mirroring the WAAPI animation API to make them interchanagable.
3625
+ * Going forward this file should be ported more towards
3626
+ * https://github.com/motiondivision/motionone/blob/main/packages/animation/src/Animation.ts
3627
+ * Which behaviourally adheres to WAAPI as far as possible.
3628
+ *
3629
+ * WARNING: This is not safe to use for most animations. We currently
3630
+ * only use it for handoff from WAAPI within Framer.
3631
+ *
3632
+ * This animation function consumes time every frame rather than being sampled for time.
3633
+ * So the sample() method performs some headless frames to ensure
3634
+ * repeats are handled correctly. Ideally in the future we will replace
3635
+ * that method with this, once repeat calculations are pure.
3636
+ */
3637
+ set currentTime(t) {
3638
+ elapsed = initialElapsed;
3639
+ update(t);
3640
+ },
3597
3641
  /**
3598
3642
  * animate() can't yet be sampled for time, instead it
3599
3643
  * consumes time. So to sample it we have to run a low
@@ -3750,21 +3794,29 @@
3750
3794
  /**
3751
3795
  * Animation interrupt callback.
3752
3796
  */
3753
- return () => {
3754
- /**
3755
- * WAAPI doesn't natively have any interruption capabilities.
3756
- *
3757
- * Rather than read commited styles back out of the DOM, we can
3758
- * create a renderless JS animation and sample it twice to calculate
3759
- * its current value, "previous" value, and therefore allow
3760
- * Motion to calculate velocity for any subsequent animation.
3761
- */
3762
- const { currentTime } = animation;
3763
- if (currentTime) {
3764
- const sampleAnimation = animate$1({ ...options, autoplay: false });
3765
- value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
3766
- }
3767
- sync.update(() => animation.cancel());
3797
+ return {
3798
+ get currentTime() {
3799
+ return animation.currentTime || 0;
3800
+ },
3801
+ set currentTime(t) {
3802
+ animation.currentTime = t;
3803
+ },
3804
+ stop: () => {
3805
+ /**
3806
+ * WAAPI doesn't natively have any interruption capabilities.
3807
+ *
3808
+ * Rather than read commited styles back out of the DOM, we can
3809
+ * create a renderless JS animation and sample it twice to calculate
3810
+ * its current value, "previous" value, and therefore allow
3811
+ * Motion to calculate velocity for any subsequent animation.
3812
+ */
3813
+ const { currentTime } = animation;
3814
+ if (currentTime) {
3815
+ const sampleAnimation = animate$1({ ...options, autoplay: false });
3816
+ value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
3817
+ }
3818
+ sync.update(() => animation.cancel());
3819
+ },
3768
3820
  };
3769
3821
  }
3770
3822
 
@@ -3788,9 +3840,8 @@
3788
3840
  const setValue = () => {
3789
3841
  onUpdate && onUpdate(keyframes[keyframes.length - 1]);
3790
3842
  onComplete && onComplete();
3791
- return () => { };
3792
3843
  };
3793
- return elapsed ? delay(setValue, -elapsed) : setValue();
3844
+ return elapsed ? { stop: delay(setValue, -elapsed) } : setValue();
3794
3845
  }
3795
3846
 
3796
3847
  function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
@@ -4067,8 +4118,7 @@
4067
4118
  * If this is an inertia animation, we currently don't support pre-generating
4068
4119
  * keyframes for this as such it must always run on the main thread.
4069
4120
  */
4070
- const animation = inertia(options);
4071
- return () => animation.stop();
4121
+ return inertia(options);
4072
4122
  }
4073
4123
  /**
4074
4124
  * If there's no transition defined for this value, we can generate
@@ -4106,8 +4156,7 @@
4106
4156
  /**
4107
4157
  * If we didn't create an accelerated animation, create a JS animation
4108
4158
  */
4109
- const animation = animate$1(options);
4110
- return () => animation.stop();
4159
+ return animate$1(options);
4111
4160
  };
4112
4161
  };
4113
4162
 
@@ -4196,7 +4245,7 @@
4196
4245
  if (!value.hasAnimated) {
4197
4246
  const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
4198
4247
  if (appearId) {
4199
- valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
4248
+ valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key, value);
4200
4249
  }
4201
4250
  }
4202
4251
  let animation = value.start(createMotionValueAnimation(key, value, valueTarget, visualElement.shouldReduceMotion && transformProps.has(key)
@@ -5946,7 +5995,7 @@
5946
5995
  * and warn against mismatches.
5947
5996
  */
5948
5997
  {
5949
- warnOnce(nextValue.version === "8.4.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.4 may not work as expected.`);
5998
+ warnOnce(nextValue.version === "8.4.5", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.5 may not work as expected.`);
5950
5999
  }
5951
6000
  }
5952
6001
  else if (isMotionValue(prevValue)) {