framer-motion 11.3.21 → 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.21";
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
@@ -1984,19 +1984,17 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
1984
1984
  next: (t) => {
1985
1985
  const current = resolveSpring(t);
1986
1986
  if (!isResolvedFromDuration) {
1987
- let currentVelocity = initialVelocity;
1988
- if (t !== 0) {
1989
- /**
1990
- * We only need to calculate velocity for under-damped springs
1991
- * as over- and critically-damped springs can't overshoot, so
1992
- * checking only for displacement is enough.
1993
- */
1994
- if (dampingRatio < 1) {
1995
- currentVelocity = calcGeneratorVelocity(resolveSpring, t, current);
1996
- }
1997
- else {
1998
- currentVelocity = 0;
1999
- }
1987
+ let currentVelocity = 0.0;
1988
+ /**
1989
+ * We only need to calculate velocity for under-damped springs
1990
+ * as over- and critically-damped springs can't overshoot, so
1991
+ * checking only for displacement is enough.
1992
+ */
1993
+ if (dampingRatio < 1) {
1994
+ currentVelocity =
1995
+ t === 0
1996
+ ? secondsToMilliseconds(initialVelocity)
1997
+ : calcGeneratorVelocity(resolveSpring, t, current);
2000
1998
  }
2001
1999
  const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
2002
2000
  const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
@@ -2887,7 +2885,7 @@ class MainThreadAnimation extends BaseAnimation {
2887
2885
  }
2888
2886
  if (this.isStopped)
2889
2887
  return;
2890
- const { driver = frameloopDriver, onPlay } = this.options;
2888
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
2891
2889
  if (!this.driver) {
2892
2890
  this.driver = driver((timestamp) => this.tick(timestamp));
2893
2891
  }
@@ -2897,7 +2895,7 @@ class MainThreadAnimation extends BaseAnimation {
2897
2895
  this.startTime = now - this.holdTime;
2898
2896
  }
2899
2897
  else if (!this.startTime || this.state === "finished") {
2900
- this.startTime = now;
2898
+ this.startTime = startTime || now;
2901
2899
  }
2902
2900
  if (this.state === "finished") {
2903
2901
  this.updateFinishedPromise();
@@ -3608,13 +3606,16 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
3608
3606
  * to see if we're handling off from an existing animation.
3609
3607
  */
3610
3608
  let isHandoff = false;
3611
- if (window.HandoffAppearAnimations) {
3609
+ if (window.MotionHandoffAnimation) {
3612
3610
  const appearId = getOptimisedAppearId(visualElement);
3613
3611
  if (appearId) {
3614
- const elapsed = window.HandoffAppearAnimations(appearId, key, value, frame);
3615
- if (elapsed !== null) {
3616
- valueTransition.elapsed = elapsed;
3612
+ const handoffInfo = window.MotionHandoffAnimation(appearId, key, frame);
3613
+ if (handoffInfo !== null) {
3617
3614
  isHandoff = true;
3615
+ const { elapsed, startTime } = handoffInfo;
3616
+ valueTransition.elapsed = elapsed;
3617
+ if (startTime)
3618
+ valueTransition.startTime = startTime;
3618
3619
  }
3619
3620
  }
3620
3621
  }
@@ -3754,7 +3755,7 @@ function updateMotionValuesFromProps(element, next, prev) {
3754
3755
  * and warn against mismatches.
3755
3756
  */
3756
3757
  if (process.env.NODE_ENV === "development") {
3757
- warnOnce(nextValue.version === "11.3.21", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.21 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.`);
3758
3759
  }
3759
3760
  }
3760
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;
@@ -3243,19 +3244,17 @@ function spring({ keyframes, restDelta, restSpeed, ...options }) {
3243
3244
  next: (t) => {
3244
3245
  const current = resolveSpring(t);
3245
3246
  if (!isResolvedFromDuration) {
3246
- let currentVelocity = initialVelocity;
3247
- if (t !== 0) {
3248
- /**
3249
- * We only need to calculate velocity for under-damped springs
3250
- * as over- and critically-damped springs can't overshoot, so
3251
- * checking only for displacement is enough.
3252
- */
3253
- if (dampingRatio < 1) {
3254
- currentVelocity = calcGeneratorVelocity(resolveSpring, t, current);
3255
- }
3256
- else {
3257
- currentVelocity = 0;
3258
- }
3247
+ let currentVelocity = 0.0;
3248
+ /**
3249
+ * We only need to calculate velocity for under-damped springs
3250
+ * as over- and critically-damped springs can't overshoot, so
3251
+ * checking only for displacement is enough.
3252
+ */
3253
+ if (dampingRatio < 1) {
3254
+ currentVelocity =
3255
+ t === 0
3256
+ ? secondsToMilliseconds(initialVelocity)
3257
+ : calcGeneratorVelocity(resolveSpring, t, current);
3259
3258
  }
3260
3259
  const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
3261
3260
  const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
@@ -4136,7 +4135,7 @@ class MainThreadAnimation extends BaseAnimation {
4136
4135
  }
4137
4136
  if (this.isStopped)
4138
4137
  return;
4139
- const { driver = frameloopDriver, onPlay } = this.options;
4138
+ const { driver = frameloopDriver, onPlay, startTime } = this.options;
4140
4139
  if (!this.driver) {
4141
4140
  this.driver = driver((timestamp) => this.tick(timestamp));
4142
4141
  }
@@ -4146,7 +4145,7 @@ class MainThreadAnimation extends BaseAnimation {
4146
4145
  this.startTime = now - this.holdTime;
4147
4146
  }
4148
4147
  else if (!this.startTime || this.state === "finished") {
4149
- this.startTime = now;
4148
+ this.startTime = startTime || now;
4150
4149
  }
4151
4150
  if (this.state === "finished") {
4152
4151
  this.updateFinishedPromise();
@@ -4824,7 +4823,7 @@ class MotionValue {
4824
4823
  * This will be replaced by the build step with the latest version number.
4825
4824
  * When MotionValues are provided to motion components, warn if versions are mixed.
4826
4825
  */
4827
- this.version = "11.3.21";
4826
+ this.version = "11.3.23-alpha.0";
4828
4827
  /**
4829
4828
  * Tracks whether this value can output a velocity. Currently this is only true
4830
4829
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5238,13 +5237,16 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
5238
5237
  * to see if we're handling off from an existing animation.
5239
5238
  */
5240
5239
  let isHandoff = false;
5241
- if (window.HandoffAppearAnimations) {
5240
+ if (window.MotionHandoffAnimation) {
5242
5241
  const appearId = getOptimisedAppearId(visualElement);
5243
5242
  if (appearId) {
5244
- const elapsed = window.HandoffAppearAnimations(appearId, key, value, frame);
5245
- if (elapsed !== null) {
5246
- valueTransition.elapsed = elapsed;
5243
+ const handoffInfo = window.MotionHandoffAnimation(appearId, key, frame);
5244
+ if (handoffInfo !== null) {
5247
5245
  isHandoff = true;
5246
+ const { elapsed, startTime } = handoffInfo;
5247
+ valueTransition.elapsed = elapsed;
5248
+ if (startTime)
5249
+ valueTransition.startTime = startTime;
5248
5250
  }
5249
5251
  }
5250
5252
  }
@@ -7286,7 +7288,7 @@ function updateMotionValuesFromProps(element, next, prev) {
7286
7288
  * and warn against mismatches.
7287
7289
  */
7288
7290
  if (process.env.NODE_ENV === "development") {
7289
- warnOnce(nextValue.version === "11.3.21", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.21 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.`);
7290
7292
  }
7291
7293
  }
7292
7294
  else if (isMotionValue(prevValue)) {
@@ -8928,7 +8930,7 @@ function resetDistortingTransform(key, visualElement, values, sharedAnimationVal
8928
8930
  }
8929
8931
  }
8930
8932
  }
8931
- function isOptimisedAppearTree(projectionNode) {
8933
+ function isOptimisedTransformAnimationInTree(projectionNode) {
8932
8934
  projectionNode.hasCheckedOptimisedAppear = true;
8933
8935
  if (projectionNode.root === projectionNode)
8934
8936
  return false;
@@ -8936,12 +8938,12 @@ function isOptimisedAppearTree(projectionNode) {
8936
8938
  if (!visualElement) {
8937
8939
  return false;
8938
8940
  }
8939
- else if (getOptimisedAppearId(visualElement)) {
8941
+ else if (window.MotionHasOptimisedTransformAnimation(getOptimisedAppearId(visualElement))) {
8940
8942
  return true;
8941
8943
  }
8942
8944
  else if (projectionNode.parent &&
8943
8945
  !projectionNode.parent.hasCheckedOptimisedAppear) {
8944
- return isOptimisedAppearTree(projectionNode.parent);
8946
+ return isOptimisedTransformAnimationInTree(projectionNode.parent);
8945
8947
  }
8946
8948
  else {
8947
8949
  return false;
@@ -9268,9 +9270,9 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
9268
9270
  * snapshots in startUpdate(), but we only want to cancel optimised animations
9269
9271
  * if a layout animation measurement is actually going to be affected by them.
9270
9272
  */
9271
- if (window.HandoffCancelAllAnimations &&
9272
- isOptimisedAppearTree(this)) {
9273
- window.HandoffCancelAllAnimations();
9273
+ if (window.MotionHandoffCancelAll &&
9274
+ isOptimisedTransformAnimationInTree(this)) {
9275
+ window.MotionHandoffCancelAll();
9274
9276
  }
9275
9277
  !this.root.isUpdating && this.root.startUpdate();
9276
9278
  if (this.isLayoutDirty)
@@ -12154,16 +12156,10 @@ function useResetProjection() {
12154
12156
  const appearStoreId = (id, value) => `${id}: ${value}`;
12155
12157
 
12156
12158
  const appearAnimationStore = new Map();
12159
+ const elementsWithAppearAnimations = new Set();
12157
12160
 
12158
12161
  let handoffFrameTime;
12159
- function handoffOptimizedAppearAnimation(elementId, valueName,
12160
- /**
12161
- * Legacy arguments. This function is inlined as part of SSG so it can be there's
12162
- * a version mismatch between the main included Motion and the inlined script.
12163
- *
12164
- * Remove in early 2024.
12165
- */
12166
- _value, frame) {
12162
+ function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
12167
12163
  const optimisedValueName = transformProps.has(valueName)
12168
12164
  ? "transform"
12169
12165
  : valueName;
@@ -12173,48 +12169,26 @@ _value, frame) {
12173
12169
  return null;
12174
12170
  }
12175
12171
  const { animation, startTime } = optimisedAnimation;
12176
- 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
+ */
12177
12180
  appearAnimationStore.delete(storeId);
12178
- if (frame) {
12179
- /**
12180
- * If we've been provided the frameloop as an argument, use it to defer
12181
- * cancellation until keyframes of the subsequent animation have been resolved.
12182
- * This "papers over" a gap where the JS animations haven't rendered with
12183
- * the latest time after a potential heavy blocking workload.
12184
- * Otherwise cancel immediately.
12185
- *
12186
- * This is an optional dependency to deal with the fact that this inline
12187
- * script and the library can be version sharded, and there have been
12188
- * times when this isn't provided as an argument.
12189
- */
12190
- frame.render(() => frame.render(() => {
12191
- try {
12192
- animation.cancel();
12193
- }
12194
- catch (error) { }
12195
- }));
12196
- }
12197
- else {
12181
+ frame.render(() => frame.render(() => {
12198
12182
  try {
12199
12183
  animation.cancel();
12200
12184
  }
12201
12185
  catch (error) { }
12202
- }
12203
- };
12204
- /**
12205
- * If the startTime is null, this animation is the Paint Ready detection animation
12206
- * and we can cancel it immediately without handoff.
12207
- *
12208
- * Or if we've already handed off the animation then we're now interrupting it.
12209
- * In which case we need to cancel it.
12210
- */
12211
- if (startTime === null || window.HandoffComplete) {
12212
- cancelAnimation();
12186
+ }));
12213
12187
  return null;
12214
12188
  }
12215
12189
  else {
12216
12190
  /**
12217
- * Otherwise we're handing off this animation to the main thread.
12191
+ * Otherwise we're starting a main thread animation.
12218
12192
  *
12219
12193
  * Record the time of the first handoff. We call performance.now() once
12220
12194
  * here and once in startOptimisedAnimation to ensure we're getting
@@ -12229,7 +12203,10 @@ _value, frame) {
12229
12203
  * an updated value for several frames, even as the animation plays smoothly via
12230
12204
  * the GPU.
12231
12205
  */
12232
- return handoffFrameTime - startTime || 0;
12206
+ return {
12207
+ elapsed: handoffFrameTime - startTime || 0,
12208
+ startTime: handoffFrameTime || 0,
12209
+ };
12233
12210
  }
12234
12211
  }
12235
12212
 
@@ -12248,14 +12225,14 @@ let startFrameTime;
12248
12225
  let readyAnimation;
12249
12226
  function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
12250
12227
  // Prevent optimised appear animations if Motion has already started animating.
12251
- if (window.HandoffComplete) {
12252
- window.HandoffAppearAnimations = undefined;
12228
+ if (window.MotionHandoffIsComplete) {
12229
+ window.MotionHandoffAnimation = undefined;
12253
12230
  return;
12254
12231
  }
12255
12232
  const id = element.dataset[optimizedAppearDataId];
12256
12233
  if (!id)
12257
12234
  return;
12258
- window.HandoffAppearAnimations = handoffOptimizedAppearAnimation;
12235
+ window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
12259
12236
  const storeId = appearStoreId(id, name);
12260
12237
  if (!readyAnimation) {
12261
12238
  readyAnimation = animateStyle(element, name, [keyframes[0], keyframes[0]],
@@ -12268,15 +12245,37 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12268
12245
  animation: readyAnimation,
12269
12246
  startTime: null,
12270
12247
  });
12271
- if (!window.HandoffCancelAllAnimations) {
12272
- window.HandoffCancelAllAnimations = () => {
12273
- 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")) {
12274
12261
  animation.cancel();
12275
- });
12276
- appearAnimationStore.clear();
12277
- window.HandoffCancelAllAnimations = undefined;
12278
- };
12279
- }
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")));
12280
12279
  }
12281
12280
  const startAnimation = () => {
12282
12281
  readyAnimation.cancel();
@@ -12297,6 +12296,7 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12297
12296
  if (onReady)
12298
12297
  onReady(appearAnimation);
12299
12298
  };
12299
+ elementsWithAppearAnimations.add(id);
12300
12300
  if (readyAnimation.ready) {
12301
12301
  readyAnimation.ready.then(startAnimation).catch(noop);
12302
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