framer-motion 8.4.4 → 8.4.6

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/index.js CHANGED
@@ -81,11 +81,19 @@ function useVisualElement(Component, visualState, props, createVisualElement) {
81
81
  visualElement && visualElement.render();
82
82
  });
83
83
  /**
84
- * If we have optimised appear animations to handoff from, trigger animateChanges
85
- * from a synchronous useLayoutEffect to ensure there's no flash of incorrectly
86
- * styled component in the event of a hydration error.
84
+ * Ideally this function would always run in a useEffect.
85
+ *
86
+ * However, if we have optimised appear animations to handoff from,
87
+ * it needs to happen synchronously to ensure there's no flash of
88
+ * incorrect styles in the event of a hydration error.
89
+ *
90
+ * So if we detect a situtation where optimised appear animations
91
+ * are running, we use useLayoutEffect to trigger animations.
87
92
  */
88
- useIsomorphicLayoutEffect(() => {
93
+ const useAnimateChangesEffect = window.MotionAppearAnimations
94
+ ? useIsomorphicLayoutEffect
95
+ : React.useEffect;
96
+ useAnimateChangesEffect(() => {
89
97
  if (visualElement && visualElement.animationState) {
90
98
  visualElement.animationState.animateChanges();
91
99
  }
@@ -1465,14 +1473,14 @@ function isDragActive() {
1465
1473
  return false;
1466
1474
  }
1467
1475
 
1468
- function createHoverEvent(visualElement, isActive, callback) {
1476
+ function createHoverEvent(visualElement, isActive, applyVariants, callback) {
1469
1477
  return (event, info) => {
1470
1478
  if (event.type === "touch" || isDragActive())
1471
1479
  return;
1472
1480
  /**
1473
1481
  * Ensure we trigger animations before firing event callback
1474
1482
  */
1475
- if (visualElement.animationState) {
1483
+ if (applyVariants && visualElement.animationState) {
1476
1484
  visualElement.animationState.setActive(exports.AnimationType.Hover, isActive);
1477
1485
  }
1478
1486
  callback && callback(event, info);
@@ -1481,12 +1489,12 @@ function createHoverEvent(visualElement, isActive, callback) {
1481
1489
  function useHoverGesture({ onHoverStart, onHoverEnd, whileHover, visualElement, }) {
1482
1490
  usePointerEvent(visualElement, "pointerenter", React.useMemo(() => {
1483
1491
  return onHoverStart || whileHover
1484
- ? createHoverEvent(visualElement, true, onHoverStart)
1492
+ ? createHoverEvent(visualElement, true, Boolean(whileHover), onHoverStart)
1485
1493
  : undefined;
1486
1494
  }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverStart });
1487
1495
  usePointerEvent(visualElement, "pointerleave", React.useMemo(() => {
1488
1496
  return onHoverEnd || whileHover
1489
- ? createHoverEvent(visualElement, false, onHoverEnd)
1497
+ ? createHoverEvent(visualElement, false, Boolean(whileHover), onHoverEnd)
1490
1498
  : undefined;
1491
1499
  }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverEnd });
1492
1500
  }
@@ -1548,8 +1556,10 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1548
1556
  function checkPointerEnd() {
1549
1557
  removePointerEndListener();
1550
1558
  isPressing.current = false;
1551
- visualElement.animationState &&
1559
+ const latestProps = visualElement.getProps();
1560
+ if (latestProps.whileTap && visualElement.animationState) {
1552
1561
  visualElement.animationState.setActive(exports.AnimationType.Tap, false);
1562
+ }
1553
1563
  return !isDragActive();
1554
1564
  }
1555
1565
  function onPointerUp(event, info) {
@@ -1571,18 +1581,20 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
1571
1581
  (_b = (_a = visualElement.getProps()).onTapCancel) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
1572
1582
  }
1573
1583
  const startPress = React.useCallback((event, info) => {
1574
- var _a, _b;
1584
+ var _a;
1575
1585
  removePointerEndListener();
1576
1586
  if (isPressing.current)
1577
1587
  return;
1578
1588
  isPressing.current = true;
1579
1589
  cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
1590
+ const latestProps = visualElement.getProps();
1580
1591
  /**
1581
1592
  * Ensure we trigger animations before firing event callback
1582
1593
  */
1583
- visualElement.animationState &&
1594
+ if (latestProps.whileTap && visualElement.animationState) {
1584
1595
  visualElement.animationState.setActive(exports.AnimationType.Tap, true);
1585
- (_b = (_a = visualElement.getProps()).onTapStart) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
1596
+ }
1597
+ (_a = latestProps.onTapStart) === null || _a === void 0 ? void 0 : _a.call(latestProps, event, info);
1586
1598
  }, [Boolean(onTapStart), visualElement]);
1587
1599
  usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
1588
1600
  useUnmountEffect(removePointerEndListener);
@@ -2081,7 +2093,7 @@ class MotionValue {
2081
2093
  * This will be replaced by the build step with the latest version number.
2082
2094
  * When MotionValues are provided to motion components, warn if versions are mixed.
2083
2095
  */
2084
- this.version = "8.4.4";
2096
+ this.version = "8.4.6";
2085
2097
  /**
2086
2098
  * Duration, in milliseconds, since last updating frame.
2087
2099
  *
@@ -2320,11 +2332,11 @@ class MotionValue {
2320
2332
  *
2321
2333
  * @internal
2322
2334
  */
2323
- start(animation) {
2335
+ start(startAnimation) {
2324
2336
  this.stop();
2325
2337
  return new Promise((resolve) => {
2326
2338
  this.hasAnimated = true;
2327
- this.stopAnimation = animation(resolve);
2339
+ this.animation = startAnimation(resolve) || null;
2328
2340
  if (this.events.animationStart) {
2329
2341
  this.events.animationStart.notify();
2330
2342
  }
@@ -2341,8 +2353,8 @@ class MotionValue {
2341
2353
  * @public
2342
2354
  */
2343
2355
  stop() {
2344
- if (this.stopAnimation) {
2345
- this.stopAnimation();
2356
+ if (this.animation) {
2357
+ this.animation.stop();
2346
2358
  if (this.events.animationCancel) {
2347
2359
  this.events.animationCancel.notify();
2348
2360
  }
@@ -2355,10 +2367,10 @@ class MotionValue {
2355
2367
  * @public
2356
2368
  */
2357
2369
  isAnimating() {
2358
- return !!this.stopAnimation;
2370
+ return !!this.animation;
2359
2371
  }
2360
2372
  clearAnimation() {
2361
- this.stopAnimation = null;
2373
+ this.animation = null;
2362
2374
  }
2363
2375
  /**
2364
2376
  * Destroy and clean up subscribers to this `MotionValue`.
@@ -2777,11 +2789,28 @@ function isWillChangeMotionValue(value) {
2777
2789
 
2778
2790
  const appearStoreId = (id, value) => `${id}: ${value}`;
2779
2791
 
2780
- function handoffOptimizedAppearAnimation(id, name) {
2792
+ function handoffOptimizedAppearAnimation(id, name, value) {
2781
2793
  const { MotionAppearAnimations } = window;
2782
2794
  const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
2783
2795
  const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
2784
2796
  if (animation) {
2797
+ const sampledTime = performance.now();
2798
+ /**
2799
+ * Resync handoff animation with optimised animation.
2800
+ *
2801
+ * This step would be unnecessary if we triggered animateChanges() in useEffect,
2802
+ * but due to potential hydration errors we currently fire them in useLayoutEffect.
2803
+ *
2804
+ * By the time we're safely ready to cancel the optimised WAAPI animation,
2805
+ * the main thread might have been blocked and desynced the two animations.
2806
+ *
2807
+ * Here, we resync the two animations before the optimised WAAPI animation is cancelled.
2808
+ */
2809
+ sync.update(() => {
2810
+ if (value.animation) {
2811
+ value.animation.currentTime = performance.now() - sampledTime;
2812
+ }
2813
+ });
2785
2814
  /**
2786
2815
  * We allow the animation to persist until the next frame:
2787
2816
  * 1. So it continues to play until Framer Motion is ready to render
@@ -2790,12 +2819,12 @@ function handoffOptimizedAppearAnimation(id, name) {
2790
2819
  * it synchronously would prevent subsequent transforms from handing off.
2791
2820
  */
2792
2821
  sync.render(() => {
2822
+ MotionAppearAnimations.delete(animationId);
2793
2823
  /**
2794
2824
  * Animation.cancel() throws so it needs to be wrapped in a try/catch
2795
2825
  */
2796
2826
  try {
2797
2827
  animation.cancel();
2798
- MotionAppearAnimations.delete(animationId);
2799
2828
  }
2800
2829
  catch (e) { }
2801
2830
  });
@@ -3581,6 +3610,25 @@ function animate$1({ duration, driver = framesync, elapsed = 0, repeat: repeatMa
3581
3610
  onStop && onStop();
3582
3611
  driverControls && driverControls.stop();
3583
3612
  },
3613
+ /**
3614
+ * Set the current time of the animation. This is purposefully
3615
+ * mirroring the WAAPI animation API to make them interchanagable.
3616
+ * Going forward this file should be ported more towards
3617
+ * https://github.com/motiondivision/motionone/blob/main/packages/animation/src/Animation.ts
3618
+ * Which behaviourally adheres to WAAPI as far as possible.
3619
+ *
3620
+ * WARNING: This is not safe to use for most animations. We currently
3621
+ * only use it for handoff from WAAPI within Framer.
3622
+ *
3623
+ * This animation function consumes time every frame rather than being sampled for time.
3624
+ * So the sample() method performs some headless frames to ensure
3625
+ * repeats are handled correctly. Ideally in the future we will replace
3626
+ * that method with this, once repeat calculations are pure.
3627
+ */
3628
+ set currentTime(t) {
3629
+ elapsed = initialElapsed;
3630
+ update(t);
3631
+ },
3584
3632
  /**
3585
3633
  * animate() can't yet be sampled for time, instead it
3586
3634
  * consumes time. So to sample it we have to run a low
@@ -3737,21 +3785,29 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
3737
3785
  /**
3738
3786
  * Animation interrupt callback.
3739
3787
  */
3740
- return () => {
3741
- /**
3742
- * WAAPI doesn't natively have any interruption capabilities.
3743
- *
3744
- * Rather than read commited styles back out of the DOM, we can
3745
- * create a renderless JS animation and sample it twice to calculate
3746
- * its current value, "previous" value, and therefore allow
3747
- * Motion to calculate velocity for any subsequent animation.
3748
- */
3749
- const { currentTime } = animation;
3750
- if (currentTime) {
3751
- const sampleAnimation = animate$1({ ...options, autoplay: false });
3752
- value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
3753
- }
3754
- sync.update(() => animation.cancel());
3788
+ return {
3789
+ get currentTime() {
3790
+ return animation.currentTime || 0;
3791
+ },
3792
+ set currentTime(t) {
3793
+ animation.currentTime = t;
3794
+ },
3795
+ stop: () => {
3796
+ /**
3797
+ * WAAPI doesn't natively have any interruption capabilities.
3798
+ *
3799
+ * Rather than read commited styles back out of the DOM, we can
3800
+ * create a renderless JS animation and sample it twice to calculate
3801
+ * its current value, "previous" value, and therefore allow
3802
+ * Motion to calculate velocity for any subsequent animation.
3803
+ */
3804
+ const { currentTime } = animation;
3805
+ if (currentTime) {
3806
+ const sampleAnimation = animate$1({ ...options, autoplay: false });
3807
+ value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
3808
+ }
3809
+ sync.update(() => animation.cancel());
3810
+ },
3755
3811
  };
3756
3812
  }
3757
3813
 
@@ -3775,9 +3831,8 @@ function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
3775
3831
  const setValue = () => {
3776
3832
  onUpdate && onUpdate(keyframes[keyframes.length - 1]);
3777
3833
  onComplete && onComplete();
3778
- return () => { };
3779
3834
  };
3780
- return elapsed ? delay(setValue, -elapsed) : setValue();
3835
+ return elapsed ? { stop: delay(setValue, -elapsed) } : setValue();
3781
3836
  }
3782
3837
 
3783
3838
  function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
@@ -4054,8 +4109,7 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
4054
4109
  * If this is an inertia animation, we currently don't support pre-generating
4055
4110
  * keyframes for this as such it must always run on the main thread.
4056
4111
  */
4057
- const animation = inertia(options);
4058
- return () => animation.stop();
4112
+ return inertia(options);
4059
4113
  }
4060
4114
  /**
4061
4115
  * If there's no transition defined for this value, we can generate
@@ -4093,8 +4147,7 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
4093
4147
  /**
4094
4148
  * If we didn't create an accelerated animation, create a JS animation
4095
4149
  */
4096
- const animation = animate$1(options);
4097
- return () => animation.stop();
4150
+ return animate$1(options);
4098
4151
  };
4099
4152
  };
4100
4153
 
@@ -4183,7 +4236,7 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
4183
4236
  if (!value.hasAnimated) {
4184
4237
  const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
4185
4238
  if (appearId) {
4186
- valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
4239
+ valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key, value);
4187
4240
  }
4188
4241
  }
4189
4242
  let animation = value.start(createMotionValueAnimation(key, value, valueTarget, visualElement.shouldReduceMotion && transformProps.has(key)
@@ -5933,7 +5986,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5933
5986
  * and warn against mismatches.
5934
5987
  */
5935
5988
  if (process.env.NODE_ENV === "development") {
5936
- warnOnce(nextValue.version === "8.4.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.4 may not work as expected.`);
5989
+ warnOnce(nextValue.version === "8.4.6", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.6 may not work as expected.`);
5937
5990
  }
5938
5991
  }
5939
5992
  else if (isMotionValue(prevValue)) {
@@ -5959,7 +6012,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5959
6012
  }
5960
6013
  else {
5961
6014
  const latestValue = element.getStaticValue(key);
5962
- element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue));
6015
+ element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element }));
5963
6016
  }
5964
6017
  }
5965
6018
  }
@@ -4,9 +4,8 @@ function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
4
4
  const setValue = () => {
5
5
  onUpdate && onUpdate(keyframes[keyframes.length - 1]);
6
6
  onComplete && onComplete();
7
- return () => { };
8
7
  };
9
- return elapsed ? delay(setValue, -elapsed) : setValue();
8
+ return elapsed ? { stop: delay(setValue, -elapsed) } : setValue();
10
9
  }
11
10
 
12
11
  export { createInstantAnimation };
@@ -65,8 +65,7 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
65
65
  * If this is an inertia animation, we currently don't support pre-generating
66
66
  * keyframes for this as such it must always run on the main thread.
67
67
  */
68
- const animation = inertia(options);
69
- return () => animation.stop();
68
+ return inertia(options);
70
69
  }
71
70
  /**
72
71
  * If there's no transition defined for this value, we can generate
@@ -104,8 +103,7 @@ const createMotionValueAnimation = (valueName, value, target, transition = {}) =
104
103
  /**
105
104
  * If we didn't create an accelerated animation, create a JS animation
106
105
  */
107
- const animation = animate(options);
108
- return () => animation.stop();
106
+ return animate(options);
109
107
  };
110
108
  };
111
109
 
@@ -105,6 +105,25 @@ function animate({ duration, driver = framesync, elapsed = 0, repeat: repeatMax
105
105
  onStop && onStop();
106
106
  driverControls && driverControls.stop();
107
107
  },
108
+ /**
109
+ * Set the current time of the animation. This is purposefully
110
+ * mirroring the WAAPI animation API to make them interchanagable.
111
+ * Going forward this file should be ported more towards
112
+ * https://github.com/motiondivision/motionone/blob/main/packages/animation/src/Animation.ts
113
+ * Which behaviourally adheres to WAAPI as far as possible.
114
+ *
115
+ * WARNING: This is not safe to use for most animations. We currently
116
+ * only use it for handoff from WAAPI within Framer.
117
+ *
118
+ * This animation function consumes time every frame rather than being sampled for time.
119
+ * So the sample() method performs some headless frames to ensure
120
+ * repeats are handled correctly. Ideally in the future we will replace
121
+ * that method with this, once repeat calculations are pure.
122
+ */
123
+ set currentTime(t) {
124
+ elapsed = initialElapsed;
125
+ update(t);
126
+ },
108
127
  /**
109
128
  * animate() can't yet be sampled for time, instead it
110
129
  * consumes time. So to sample it we have to run a low
@@ -2,11 +2,28 @@ import { sync } from '../../frameloop/index.mjs';
2
2
  import { transformProps } from '../../render/html/utils/transform.mjs';
3
3
  import { appearStoreId } from './store-id.mjs';
4
4
 
5
- function handoffOptimizedAppearAnimation(id, name) {
5
+ function handoffOptimizedAppearAnimation(id, name, value) {
6
6
  const { MotionAppearAnimations } = window;
7
7
  const animationId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
8
8
  const animation = MotionAppearAnimations && MotionAppearAnimations.get(animationId);
9
9
  if (animation) {
10
+ const sampledTime = performance.now();
11
+ /**
12
+ * Resync handoff animation with optimised animation.
13
+ *
14
+ * This step would be unnecessary if we triggered animateChanges() in useEffect,
15
+ * but due to potential hydration errors we currently fire them in useLayoutEffect.
16
+ *
17
+ * By the time we're safely ready to cancel the optimised WAAPI animation,
18
+ * the main thread might have been blocked and desynced the two animations.
19
+ *
20
+ * Here, we resync the two animations before the optimised WAAPI animation is cancelled.
21
+ */
22
+ sync.update(() => {
23
+ if (value.animation) {
24
+ value.animation.currentTime = performance.now() - sampledTime;
25
+ }
26
+ });
10
27
  /**
11
28
  * We allow the animation to persist until the next frame:
12
29
  * 1. So it continues to play until Framer Motion is ready to render
@@ -15,12 +32,12 @@ function handoffOptimizedAppearAnimation(id, name) {
15
32
  * it synchronously would prevent subsequent transforms from handing off.
16
33
  */
17
34
  sync.render(() => {
35
+ MotionAppearAnimations.delete(animationId);
18
36
  /**
19
37
  * Animation.cancel() throws so it needs to be wrapped in a try/catch
20
38
  */
21
39
  try {
22
40
  animation.cancel();
23
- MotionAppearAnimations.delete(animationId);
24
41
  }
25
42
  catch (e) { }
26
43
  });
@@ -80,21 +80,29 @@ function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ..
80
80
  /**
81
81
  * Animation interrupt callback.
82
82
  */
83
- return () => {
84
- /**
85
- * WAAPI doesn't natively have any interruption capabilities.
86
- *
87
- * Rather than read commited styles back out of the DOM, we can
88
- * create a renderless JS animation and sample it twice to calculate
89
- * its current value, "previous" value, and therefore allow
90
- * Motion to calculate velocity for any subsequent animation.
91
- */
92
- const { currentTime } = animation;
93
- if (currentTime) {
94
- const sampleAnimation = animate({ ...options, autoplay: false });
95
- value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
96
- }
97
- sync.update(() => animation.cancel());
83
+ return {
84
+ get currentTime() {
85
+ return animation.currentTime || 0;
86
+ },
87
+ set currentTime(t) {
88
+ animation.currentTime = t;
89
+ },
90
+ stop: () => {
91
+ /**
92
+ * WAAPI doesn't natively have any interruption capabilities.
93
+ *
94
+ * Rather than read commited styles back out of the DOM, we can
95
+ * create a renderless JS animation and sample it twice to calculate
96
+ * its current value, "previous" value, and therefore allow
97
+ * Motion to calculate velocity for any subsequent animation.
98
+ */
99
+ const { currentTime } = animation;
100
+ if (currentTime) {
101
+ const sampleAnimation = animate({ ...options, autoplay: false });
102
+ value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
103
+ }
104
+ sync.update(() => animation.cancel());
105
+ },
98
106
  };
99
107
  }
100
108
 
@@ -3,14 +3,14 @@ import { usePointerEvent } from '../events/use-pointer-event.mjs';
3
3
  import { isDragActive } from './drag/utils/lock.mjs';
4
4
  import { useMemo } from 'react';
5
5
 
6
- function createHoverEvent(visualElement, isActive, callback) {
6
+ function createHoverEvent(visualElement, isActive, applyVariants, callback) {
7
7
  return (event, info) => {
8
8
  if (event.type === "touch" || isDragActive())
9
9
  return;
10
10
  /**
11
11
  * Ensure we trigger animations before firing event callback
12
12
  */
13
- if (visualElement.animationState) {
13
+ if (applyVariants && visualElement.animationState) {
14
14
  visualElement.animationState.setActive(AnimationType.Hover, isActive);
15
15
  }
16
16
  callback && callback(event, info);
@@ -19,12 +19,12 @@ function createHoverEvent(visualElement, isActive, callback) {
19
19
  function useHoverGesture({ onHoverStart, onHoverEnd, whileHover, visualElement, }) {
20
20
  usePointerEvent(visualElement, "pointerenter", useMemo(() => {
21
21
  return onHoverStart || whileHover
22
- ? createHoverEvent(visualElement, true, onHoverStart)
22
+ ? createHoverEvent(visualElement, true, Boolean(whileHover), onHoverStart)
23
23
  : undefined;
24
24
  }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverStart });
25
25
  usePointerEvent(visualElement, "pointerleave", useMemo(() => {
26
26
  return onHoverEnd || whileHover
27
- ? createHoverEvent(visualElement, false, onHoverEnd)
27
+ ? createHoverEvent(visualElement, false, Boolean(whileHover), onHoverEnd)
28
28
  : undefined;
29
29
  }, [onHoverStart, Boolean(whileHover), visualElement]), { passive: !onHoverEnd });
30
30
  }
@@ -30,8 +30,10 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
30
30
  function checkPointerEnd() {
31
31
  removePointerEndListener();
32
32
  isPressing.current = false;
33
- visualElement.animationState &&
33
+ const latestProps = visualElement.getProps();
34
+ if (latestProps.whileTap && visualElement.animationState) {
34
35
  visualElement.animationState.setActive(AnimationType.Tap, false);
36
+ }
35
37
  return !isDragActive();
36
38
  }
37
39
  function onPointerUp(event, info) {
@@ -53,18 +55,20 @@ function useTapGesture({ onTap, onTapStart, onTapCancel, whileTap, visualElement
53
55
  (_b = (_a = visualElement.getProps()).onTapCancel) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
54
56
  }
55
57
  const startPress = useCallback((event, info) => {
56
- var _a, _b;
58
+ var _a;
57
59
  removePointerEndListener();
58
60
  if (isPressing.current)
59
61
  return;
60
62
  isPressing.current = true;
61
63
  cancelPointerEndListeners.current = pipe(addPointerEvent(window, "pointerup", onPointerUp, eventOptions), addPointerEvent(window, "pointercancel", onPointerCancel, eventOptions));
64
+ const latestProps = visualElement.getProps();
62
65
  /**
63
66
  * Ensure we trigger animations before firing event callback
64
67
  */
65
- visualElement.animationState &&
68
+ if (latestProps.whileTap && visualElement.animationState) {
66
69
  visualElement.animationState.setActive(AnimationType.Tap, true);
67
- (_b = (_a = visualElement.getProps()).onTapStart) === null || _b === void 0 ? void 0 : _b.call(_a, event, info);
70
+ }
71
+ (_a = latestProps.onTapStart) === null || _a === void 0 ? void 0 : _a.call(latestProps, event, info);
68
72
  }, [Boolean(onTapStart), visualElement]);
69
73
  usePointerEvent(visualElement, "pointerdown", hasPressListeners ? startPress : undefined, eventOptions);
70
74
  useUnmountEffect(removePointerEndListener);
@@ -1,4 +1,4 @@
1
- import { useContext, useRef } from 'react';
1
+ import { useContext, useRef, useEffect } from 'react';
2
2
  import { PresenceContext } from '../../context/PresenceContext.mjs';
3
3
  import { useVisualElementContext } from '../../context/MotionContext/index.mjs';
4
4
  import { useIsomorphicLayoutEffect } from '../../utils/use-isomorphic-effect.mjs';
@@ -32,11 +32,19 @@ function useVisualElement(Component, visualState, props, createVisualElement) {
32
32
  visualElement && visualElement.render();
33
33
  });
34
34
  /**
35
- * If we have optimised appear animations to handoff from, trigger animateChanges
36
- * from a synchronous useLayoutEffect to ensure there's no flash of incorrectly
37
- * styled component in the event of a hydration error.
35
+ * Ideally this function would always run in a useEffect.
36
+ *
37
+ * However, if we have optimised appear animations to handoff from,
38
+ * it needs to happen synchronously to ensure there's no flash of
39
+ * incorrect styles in the event of a hydration error.
40
+ *
41
+ * So if we detect a situtation where optimised appear animations
42
+ * are running, we use useLayoutEffect to trigger animations.
38
43
  */
39
- useIsomorphicLayoutEffect(() => {
44
+ const useAnimateChangesEffect = window.MotionAppearAnimations
45
+ ? useIsomorphicLayoutEffect
46
+ : useEffect;
47
+ useAnimateChangesEffect(() => {
40
48
  if (visualElement && visualElement.animationState) {
41
49
  visualElement.animationState.animateChanges();
42
50
  }
@@ -91,7 +91,7 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
91
91
  if (!value.hasAnimated) {
92
92
  const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
93
93
  if (appearId) {
94
- valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key);
94
+ valueTransition.elapsed = handoffOptimizedAppearAnimation(appearId, key, value);
95
95
  }
96
96
  }
97
97
  let animation = value.start(createMotionValueAnimation(key, value, valueTarget, visualElement.shouldReduceMotion && transformProps.has(key)
@@ -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 === "8.4.4", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.4 may not work as expected.`);
25
+ warnOnce(nextValue.version === "8.4.6", `Attempting to mix Framer Motion versions ${nextValue.version} with 8.4.6 may not work as expected.`);
26
26
  }
27
27
  }
28
28
  else if (isMotionValue(prevValue)) {
@@ -48,7 +48,7 @@ function updateMotionValuesFromProps(element, next, prev) {
48
48
  }
49
49
  else {
50
50
  const latestValue = element.getStaticValue(key);
51
- element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue));
51
+ element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element }));
52
52
  }
53
53
  }
54
54
  }
@@ -25,7 +25,7 @@ class MotionValue {
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 = "8.4.4";
28
+ this.version = "8.4.6";
29
29
  /**
30
30
  * Duration, in milliseconds, since last updating frame.
31
31
  *
@@ -264,11 +264,11 @@ class MotionValue {
264
264
  *
265
265
  * @internal
266
266
  */
267
- start(animation) {
267
+ start(startAnimation) {
268
268
  this.stop();
269
269
  return new Promise((resolve) => {
270
270
  this.hasAnimated = true;
271
- this.stopAnimation = animation(resolve);
271
+ this.animation = startAnimation(resolve) || null;
272
272
  if (this.events.animationStart) {
273
273
  this.events.animationStart.notify();
274
274
  }
@@ -285,8 +285,8 @@ class MotionValue {
285
285
  * @public
286
286
  */
287
287
  stop() {
288
- if (this.stopAnimation) {
289
- this.stopAnimation();
288
+ if (this.animation) {
289
+ this.animation.stop();
290
290
  if (this.events.animationCancel) {
291
291
  this.events.animationCancel.notify();
292
292
  }
@@ -299,10 +299,10 @@ class MotionValue {
299
299
  * @public
300
300
  */
301
301
  isAnimating() {
302
- return !!this.stopAnimation;
302
+ return !!this.animation;
303
303
  }
304
304
  clearAnimation() {
305
- this.stopAnimation = null;
305
+ this.animation = null;
306
306
  }
307
307
  /**
308
308
  * Destroy and clean up subscribers to this `MotionValue`.