framer-motion 11.3.22 → 11.3.23-alpha.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.
@@ -279,7 +279,7 @@ class MotionValue {
279
279
  * This will be replaced by the build step with the latest version number.
280
280
  * When MotionValues are provided to motion components, warn if versions are mixed.
281
281
  */
282
- this.version = "11.3.22";
282
+ this.version = "11.3.23-alpha.0";
283
283
  /**
284
284
  * Tracks whether this value can output a velocity. Currently this is only true
285
285
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -2885,7 +2885,7 @@ class MainThreadAnimation extends BaseAnimation {
2885
2885
  }
2886
2886
  if (this.isStopped)
2887
2887
  return;
2888
- const { driver = frameloopDriver, onPlay } = this.options;
2888
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
2889
2889
  if (!this.driver) {
2890
2890
  this.driver = driver((timestamp) => this.tick(timestamp));
2891
2891
  }
@@ -2895,7 +2895,7 @@ class MainThreadAnimation extends BaseAnimation {
2895
2895
  this.startTime = now - this.holdTime;
2896
2896
  }
2897
2897
  else if (!this.startTime || this.state === "finished") {
2898
- this.startTime = now;
2898
+ this.startTime = startTime || now;
2899
2899
  }
2900
2900
  if (this.state === "finished") {
2901
2901
  this.updateFinishedPromise();
@@ -3606,13 +3606,16 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
3606
3606
  * to see if we're handling off from an existing animation.
3607
3607
  */
3608
3608
  let isHandoff = false;
3609
- if (window.HandoffAppearAnimations) {
3609
+ if (window.MotionHandoffAnimation) {
3610
3610
  const appearId = getOptimisedAppearId(visualElement);
3611
3611
  if (appearId) {
3612
- const elapsed = window.HandoffAppearAnimations(appearId, key, value, frame);
3613
- if (elapsed !== null) {
3614
- valueTransition.elapsed = elapsed;
3612
+ const handoffInfo = window.MotionHandoffAnimation(appearId, key, frame);
3613
+ if (handoffInfo !== null) {
3615
3614
  isHandoff = true;
3615
+ const { elapsed, startTime } = handoffInfo;
3616
+ valueTransition.elapsed = elapsed;
3617
+ if (startTime)
3618
+ valueTransition.startTime = startTime;
3616
3619
  }
3617
3620
  }
3618
3621
  }
@@ -3752,7 +3755,7 @@ function updateMotionValuesFromProps(element, next, prev) {
3752
3755
  * and warn against mismatches.
3753
3756
  */
3754
3757
  if (process.env.NODE_ENV === "development") {
3755
- warnOnce(nextValue.version === "11.3.22", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.22 may not work as expected.`);
3758
+ warnOnce(nextValue.version === "11.3.23-alpha.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.23-alpha.0 may not work as expected.`);
3756
3759
  }
3757
3760
  }
3758
3761
  else if (isMotionValue(prevValue)) {
package/dist/cjs/index.js CHANGED
@@ -223,6 +223,7 @@ const SwitchLayoutGroupContext = React.createContext({});
223
223
 
224
224
  let scheduleHandoffComplete = false;
225
225
  function useVisualElement(Component, visualState, props, createVisualElement, ProjectionNodeConstructor) {
226
+ var _a;
226
227
  const { visualElement: parent } = React.useContext(MotionContext);
227
228
  const lazyContext = React.useContext(LazyContext);
228
229
  const presenceContext = React.useContext(PresenceContext);
@@ -263,8 +264,10 @@ function useVisualElement(Component, visualState, props, createVisualElement, Pr
263
264
  * Cache this value as we want to know whether HandoffAppearAnimations
264
265
  * was present on initial render - it will be deleted after this.
265
266
  */
266
- const wantsHandoff = React.useRef(Boolean(props[optimizedAppearDataAttribute] &&
267
- !window.HandoffComplete));
267
+ const optimisedAppearId = props[optimizedAppearDataAttribute];
268
+ const wantsHandoff = React.useRef(Boolean(optimisedAppearId) &&
269
+ !window.MotionHandoffIsComplete &&
270
+ ((_a = window.MotionHasOptimisedAnimation) === null || _a === void 0 ? void 0 : _a.call(window, optimisedAppearId)));
268
271
  useIsomorphicLayoutEffect(() => {
269
272
  if (!visualElement)
270
273
  return;
@@ -290,19 +293,17 @@ function useVisualElement(Component, visualState, props, createVisualElement, Pr
290
293
  if (!wantsHandoff.current && visualElement.animationState) {
291
294
  visualElement.animationState.animateChanges();
292
295
  }
293
- if (wantsHandoff.current) {
294
- wantsHandoff.current = false;
295
- // This ensures all future calls to animateChanges() will run in useEffect
296
- if (!scheduleHandoffComplete) {
297
- scheduleHandoffComplete = true;
298
- queueMicrotask(completeHandoff);
299
- }
296
+ wantsHandoff.current = false;
297
+ // This ensures all future calls to animateChanges() will run in useEffect
298
+ if (!scheduleHandoffComplete) {
299
+ scheduleHandoffComplete = true;
300
+ queueMicrotask(completeHandoff);
300
301
  }
301
302
  });
302
303
  return visualElement;
303
304
  }
304
305
  function completeHandoff() {
305
- window.HandoffComplete = true;
306
+ window.MotionHandoffIsComplete = true;
306
307
  }
307
308
  function createProjectionNode$1(visualElement, props, ProjectionNodeConstructor, initialPromotionConfig) {
308
309
  const { layoutId, layout, drag, dragConstraints, layoutScroll, layoutRoot, } = props;
@@ -4134,7 +4135,7 @@ class MainThreadAnimation extends BaseAnimation {
4134
4135
  }
4135
4136
  if (this.isStopped)
4136
4137
  return;
4137
- const { driver = frameloopDriver, onPlay } = this.options;
4138
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
4138
4139
  if (!this.driver) {
4139
4140
  this.driver = driver((timestamp) => this.tick(timestamp));
4140
4141
  }
@@ -4144,7 +4145,7 @@ class MainThreadAnimation extends BaseAnimation {
4144
4145
  this.startTime = now - this.holdTime;
4145
4146
  }
4146
4147
  else if (!this.startTime || this.state === "finished") {
4147
- this.startTime = now;
4148
+ this.startTime = startTime || now;
4148
4149
  }
4149
4150
  if (this.state === "finished") {
4150
4151
  this.updateFinishedPromise();
@@ -4822,7 +4823,7 @@ class MotionValue {
4822
4823
  * This will be replaced by the build step with the latest version number.
4823
4824
  * When MotionValues are provided to motion components, warn if versions are mixed.
4824
4825
  */
4825
- this.version = "11.3.22";
4826
+ this.version = "11.3.23-alpha.0";
4826
4827
  /**
4827
4828
  * Tracks whether this value can output a velocity. Currently this is only true
4828
4829
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5236,13 +5237,16 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
5236
5237
  * to see if we're handling off from an existing animation.
5237
5238
  */
5238
5239
  let isHandoff = false;
5239
- if (window.HandoffAppearAnimations) {
5240
+ if (window.MotionHandoffAnimation) {
5240
5241
  const appearId = getOptimisedAppearId(visualElement);
5241
5242
  if (appearId) {
5242
- const elapsed = window.HandoffAppearAnimations(appearId, key, value, frame);
5243
- if (elapsed !== null) {
5244
- valueTransition.elapsed = elapsed;
5243
+ const handoffInfo = window.MotionHandoffAnimation(appearId, key, frame);
5244
+ if (handoffInfo !== null) {
5245
5245
  isHandoff = true;
5246
+ const { elapsed, startTime } = handoffInfo;
5247
+ valueTransition.elapsed = elapsed;
5248
+ if (startTime)
5249
+ valueTransition.startTime = startTime;
5246
5250
  }
5247
5251
  }
5248
5252
  }
@@ -7284,7 +7288,7 @@ function updateMotionValuesFromProps(element, next, prev) {
7284
7288
  * and warn against mismatches.
7285
7289
  */
7286
7290
  if (process.env.NODE_ENV === "development") {
7287
- warnOnce(nextValue.version === "11.3.22", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.22 may not work as expected.`);
7291
+ warnOnce(nextValue.version === "11.3.23-alpha.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.23-alpha.0 may not work as expected.`);
7288
7292
  }
7289
7293
  }
7290
7294
  else if (isMotionValue(prevValue)) {
@@ -8926,7 +8930,7 @@ function resetDistortingTransform(key, visualElement, values, sharedAnimationVal
8926
8930
  }
8927
8931
  }
8928
8932
  }
8929
- function isOptimisedAppearTree(projectionNode) {
8933
+ function isOptimisedTransformAnimationInTree(projectionNode) {
8930
8934
  projectionNode.hasCheckedOptimisedAppear = true;
8931
8935
  if (projectionNode.root === projectionNode)
8932
8936
  return false;
@@ -8934,12 +8938,12 @@ function isOptimisedAppearTree(projectionNode) {
8934
8938
  if (!visualElement) {
8935
8939
  return false;
8936
8940
  }
8937
- else if (getOptimisedAppearId(visualElement)) {
8941
+ else if (window.MotionHasOptimisedTransformAnimation(getOptimisedAppearId(visualElement))) {
8938
8942
  return true;
8939
8943
  }
8940
8944
  else if (projectionNode.parent &&
8941
8945
  !projectionNode.parent.hasCheckedOptimisedAppear) {
8942
- return isOptimisedAppearTree(projectionNode.parent);
8946
+ return isOptimisedTransformAnimationInTree(projectionNode.parent);
8943
8947
  }
8944
8948
  else {
8945
8949
  return false;
@@ -9266,9 +9270,9 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
9266
9270
  * snapshots in startUpdate(), but we only want to cancel optimised animations
9267
9271
  * if a layout animation measurement is actually going to be affected by them.
9268
9272
  */
9269
- if (window.HandoffCancelAllAnimations &&
9270
- isOptimisedAppearTree(this)) {
9271
- window.HandoffCancelAllAnimations();
9273
+ if (window.MotionHandoffCancelAll &&
9274
+ isOptimisedTransformAnimationInTree(this)) {
9275
+ window.MotionHandoffCancelAll();
9272
9276
  }
9273
9277
  !this.root.isUpdating && this.root.startUpdate();
9274
9278
  if (this.isLayoutDirty)
@@ -12152,16 +12156,10 @@ function useResetProjection() {
12152
12156
  const appearStoreId = (id, value) => `${id}: ${value}`;
12153
12157
 
12154
12158
  const appearAnimationStore = new Map();
12159
+ const elementsWithAppearAnimations = new Set();
12155
12160
 
12156
12161
  let handoffFrameTime;
12157
- function handoffOptimizedAppearAnimation(elementId, valueName,
12158
- /**
12159
- * Legacy arguments. This function is inlined as part of SSG so it can be there's
12160
- * a version mismatch between the main included Motion and the inlined script.
12161
- *
12162
- * Remove in early 2024.
12163
- */
12164
- _value, frame) {
12162
+ function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
12165
12163
  const optimisedValueName = transformProps.has(valueName)
12166
12164
  ? "transform"
12167
12165
  : valueName;
@@ -12171,48 +12169,26 @@ _value, frame) {
12171
12169
  return null;
12172
12170
  }
12173
12171
  const { animation, startTime } = optimisedAnimation;
12174
- const cancelAnimation = () => {
12172
+ if (startTime === null || window.MotionHandoffIsComplete) {
12173
+ /**
12174
+ * If the startTime is null, this animation is the Paint Ready detection animation
12175
+ * and we can cancel it immediately without handoff.
12176
+ *
12177
+ * Or if we've already handed off the animation then we're now interrupting it.
12178
+ * In which case we need to cancel it.
12179
+ */
12175
12180
  appearAnimationStore.delete(storeId);
12176
- if (frame) {
12177
- /**
12178
- * If we've been provided the frameloop as an argument, use it to defer
12179
- * cancellation until keyframes of the subsequent animation have been resolved.
12180
- * This "papers over" a gap where the JS animations haven't rendered with
12181
- * the latest time after a potential heavy blocking workload.
12182
- * Otherwise cancel immediately.
12183
- *
12184
- * This is an optional dependency to deal with the fact that this inline
12185
- * script and the library can be version sharded, and there have been
12186
- * times when this isn't provided as an argument.
12187
- */
12188
- frame.render(() => frame.render(() => {
12189
- try {
12190
- animation.cancel();
12191
- }
12192
- catch (error) { }
12193
- }));
12194
- }
12195
- else {
12181
+ frame.render(() => frame.render(() => {
12196
12182
  try {
12197
12183
  animation.cancel();
12198
12184
  }
12199
12185
  catch (error) { }
12200
- }
12201
- };
12202
- /**
12203
- * If the startTime is null, this animation is the Paint Ready detection animation
12204
- * and we can cancel it immediately without handoff.
12205
- *
12206
- * Or if we've already handed off the animation then we're now interrupting it.
12207
- * In which case we need to cancel it.
12208
- */
12209
- if (startTime === null || window.HandoffComplete) {
12210
- cancelAnimation();
12186
+ }));
12211
12187
  return null;
12212
12188
  }
12213
12189
  else {
12214
12190
  /**
12215
- * Otherwise we're handing off this animation to the main thread.
12191
+ * Otherwise we're starting a main thread animation.
12216
12192
  *
12217
12193
  * Record the time of the first handoff. We call performance.now() once
12218
12194
  * here and once in startOptimisedAnimation to ensure we're getting
@@ -12227,7 +12203,10 @@ _value, frame) {
12227
12203
  * an updated value for several frames, even as the animation plays smoothly via
12228
12204
  * the GPU.
12229
12205
  */
12230
- return handoffFrameTime - startTime || 0;
12206
+ return {
12207
+ elapsed: handoffFrameTime - startTime || 0,
12208
+ startTime: handoffFrameTime || 0,
12209
+ };
12231
12210
  }
12232
12211
  }
12233
12212
 
@@ -12246,14 +12225,14 @@ let startFrameTime;
12246
12225
  let readyAnimation;
12247
12226
  function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
12248
12227
  // Prevent optimised appear animations if Motion has already started animating.
12249
- if (window.HandoffComplete) {
12250
- window.HandoffAppearAnimations = undefined;
12228
+ if (window.MotionHandoffIsComplete) {
12229
+ window.MotionHandoffAnimation = undefined;
12251
12230
  return;
12252
12231
  }
12253
12232
  const id = element.dataset[optimizedAppearDataId];
12254
12233
  if (!id)
12255
12234
  return;
12256
- window.HandoffAppearAnimations = handoffOptimizedAppearAnimation;
12235
+ window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
12257
12236
  const storeId = appearStoreId(id, name);
12258
12237
  if (!readyAnimation) {
12259
12238
  readyAnimation = animateStyle(element, name, [keyframes[0], keyframes[0]],
@@ -12266,15 +12245,37 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12266
12245
  animation: readyAnimation,
12267
12246
  startTime: null,
12268
12247
  });
12269
- if (!window.HandoffCancelAllAnimations) {
12270
- window.HandoffCancelAllAnimations = () => {
12271
- appearAnimationStore.forEach(({ animation }) => {
12248
+ /**
12249
+ * If there's no readyAnimation then there's been no instantiation
12250
+ * of handoff animations.
12251
+ */
12252
+ window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
12253
+ /**
12254
+ * We only need to cancel transform animations as
12255
+ * they're the ones that will interfere with the
12256
+ * layout animation measurements.
12257
+ */
12258
+ window.MotionHandoffCancelAll = () => {
12259
+ appearAnimationStore.forEach(({ animation }, animationId) => {
12260
+ if (animationId.endsWith("transform")) {
12272
12261
  animation.cancel();
12273
- });
12274
- appearAnimationStore.clear();
12275
- window.HandoffCancelAllAnimations = undefined;
12276
- };
12277
- }
12262
+ appearAnimationStore.delete(animationId);
12263
+ }
12264
+ });
12265
+ window.MotionHandoffCancelAll = undefined;
12266
+ };
12267
+ /**
12268
+ * Keep a map of elementIds that have started animating. We check
12269
+ * via ID instead of Element because of hydration errors and
12270
+ * pre-hydration checks. We also actively record IDs as they start
12271
+ * animating rather than simply checking for data-appear-id as
12272
+ * this attrbute might be present but not lead to an animation, for
12273
+ * instance if the element's appear animation is on a different
12274
+ * breakpoint.
12275
+ */
12276
+ window.MotionHasOptimisedAnimation = (elementId) => Boolean(elementId && elementsWithAppearAnimations.has(elementId));
12277
+ window.MotionHasOptimisedTransformAnimation = (elementId) => Boolean(elementId &&
12278
+ appearAnimationStore.has(appearStoreId(elementId, "transform")));
12278
12279
  }
12279
12280
  const startAnimation = () => {
12280
12281
  readyAnimation.cancel();
@@ -12295,6 +12296,7 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12295
12296
  if (onReady)
12296
12297
  onReady(appearAnimation);
12297
12298
  };
12299
+ elementsWithAppearAnimations.add(id);
12298
12300
  if (readyAnimation.ready) {
12299
12301
  readyAnimation.ready.then(startAnimation).catch(noop);
12300
12302
  }
@@ -852,16 +852,21 @@ interface FrameData {
852
852
  isProcessing: boolean;
853
853
  }
854
854
 
855
- type HandoffFunction = (storeId: string, valueName: string, value?: MotionValue, frame?: Batcher) => null | number;
855
+ type HandoffFunction = (storeId: string, valueName: string, frame: Batcher) => null | {
856
+ elapsed: number;
857
+ startTime: number;
858
+ };
856
859
  /**
857
860
  * The window global object acts as a bridge between our inline script
858
861
  * triggering the optimized appear animations, and Framer Motion.
859
862
  */
860
863
  declare global {
861
864
  interface Window {
862
- HandoffAppearAnimations?: HandoffFunction;
863
- HandoffComplete?: boolean;
864
- HandoffCancelAllAnimations?: VoidFunction;
865
+ MotionHandoffAnimation?: HandoffFunction;
866
+ MotionHandoffIsComplete?: boolean;
867
+ MotionHandoffCancelAll?: VoidFunction;
868
+ MotionHasOptimisedAnimation?: (id?: string) => boolean;
869
+ MotionHasOptimisedTransformAnimation?: (id?: string) => boolean;
865
870
  }
866
871
  }
867
872