framer-motion 11.3.30 → 11.3.31-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.30";
282
+ this.version = "11.3.31-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
@@ -3525,7 +3525,7 @@ const optimizedAppearDataId = "framerAppearId";
3525
3525
  const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
3526
3526
 
3527
3527
  function getOptimisedAppearId(visualElement) {
3528
- return visualElement.getProps()[optimizedAppearDataAttribute];
3528
+ return visualElement.props[optimizedAppearDataAttribute];
3529
3529
  }
3530
3530
 
3531
3531
  function getWillChangeName(name) {
@@ -3790,7 +3790,7 @@ function updateMotionValuesFromProps(element, next, prev) {
3790
3790
  * and warn against mismatches.
3791
3791
  */
3792
3792
  if (process.env.NODE_ENV === "development") {
3793
- warnOnce(nextValue.version === "11.3.30", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.30 may not work as expected.`);
3793
+ warnOnce(nextValue.version === "11.3.31-alpha.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.31-alpha.0 may not work as expected.`);
3794
3794
  }
3795
3795
  }
3796
3796
  else if (isMotionValue(prevValue)) {
@@ -4016,7 +4016,11 @@ class VisualElement {
4016
4016
  if (this.parent && this.isVariantNode && !this.isControllingVariants) {
4017
4017
  this.removeFromVariantTree = this.parent.addVariantChild(this);
4018
4018
  }
4019
- this.values.forEach((value, key) => this.bindToMotionValue(key, value));
4019
+ this.values.forEach((value, key) => {
4020
+ if (!this.valueSubscriptions.has(key)) {
4021
+ this.bindToMotionValue(key, value);
4022
+ }
4023
+ });
4020
4024
  if (!hasReducedMotionListener.current) {
4021
4025
  initPrefersReducedMotion();
4022
4026
  }
@@ -4039,6 +4043,7 @@ class VisualElement {
4039
4043
  cancelFrame(this.notifyUpdate);
4040
4044
  cancelFrame(this.render);
4041
4045
  this.valueSubscriptions.forEach((remove) => remove());
4046
+ this.valueSubscriptions.clear();
4042
4047
  this.removeFromVariantTree && this.removeFromVariantTree();
4043
4048
  this.parent && this.parent.children.delete(this);
4044
4049
  for (const key in this.events) {
@@ -4066,9 +4071,15 @@ class VisualElement {
4066
4071
  }
4067
4072
  });
4068
4073
  const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
4074
+ let removeSyncCheck;
4075
+ if (window.MotionCheckAppearSync) {
4076
+ removeSyncCheck = window.MotionCheckAppearSync(this, key, value);
4077
+ }
4069
4078
  this.valueSubscriptions.set(key, () => {
4070
4079
  removeOnChange();
4071
4080
  removeOnRenderRequest();
4081
+ if (removeSyncCheck)
4082
+ removeSyncCheck();
4072
4083
  if (value.owner)
4073
4084
  value.stop();
4074
4085
  });
package/dist/cjs/index.js CHANGED
@@ -4862,7 +4862,7 @@ class MotionValue {
4862
4862
  * This will be replaced by the build step with the latest version number.
4863
4863
  * When MotionValues are provided to motion components, warn if versions are mixed.
4864
4864
  */
4865
- this.version = "11.3.30";
4865
+ this.version = "11.3.31-alpha.0";
4866
4866
  /**
4867
4867
  * Tracks whether this value can output a velocity. Currently this is only true
4868
4868
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -5167,7 +5167,7 @@ function setTarget(visualElement, definition) {
5167
5167
  }
5168
5168
 
5169
5169
  function getOptimisedAppearId(visualElement) {
5170
- return visualElement.getProps()[optimizedAppearDataAttribute];
5170
+ return visualElement.props[optimizedAppearDataAttribute];
5171
5171
  }
5172
5172
 
5173
5173
  class WillChangeMotionValue extends MotionValue {
@@ -7323,7 +7323,7 @@ function updateMotionValuesFromProps(element, next, prev) {
7323
7323
  * and warn against mismatches.
7324
7324
  */
7325
7325
  if (process.env.NODE_ENV === "development") {
7326
- warnOnce(nextValue.version === "11.3.30", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.30 may not work as expected.`);
7326
+ warnOnce(nextValue.version === "11.3.31-alpha.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 11.3.31-alpha.0 may not work as expected.`);
7327
7327
  }
7328
7328
  }
7329
7329
  else if (isMotionValue(prevValue)) {
@@ -7516,7 +7516,11 @@ class VisualElement {
7516
7516
  if (this.parent && this.isVariantNode && !this.isControllingVariants) {
7517
7517
  this.removeFromVariantTree = this.parent.addVariantChild(this);
7518
7518
  }
7519
- this.values.forEach((value, key) => this.bindToMotionValue(key, value));
7519
+ this.values.forEach((value, key) => {
7520
+ if (!this.valueSubscriptions.has(key)) {
7521
+ this.bindToMotionValue(key, value);
7522
+ }
7523
+ });
7520
7524
  if (!hasReducedMotionListener.current) {
7521
7525
  initPrefersReducedMotion();
7522
7526
  }
@@ -7539,6 +7543,7 @@ class VisualElement {
7539
7543
  cancelFrame(this.notifyUpdate);
7540
7544
  cancelFrame(this.render);
7541
7545
  this.valueSubscriptions.forEach((remove) => remove());
7546
+ this.valueSubscriptions.clear();
7542
7547
  this.removeFromVariantTree && this.removeFromVariantTree();
7543
7548
  this.parent && this.parent.children.delete(this);
7544
7549
  for (const key in this.events) {
@@ -7566,9 +7571,15 @@ class VisualElement {
7566
7571
  }
7567
7572
  });
7568
7573
  const removeOnRenderRequest = value.on("renderRequest", this.scheduleRender);
7574
+ let removeSyncCheck;
7575
+ if (window.MotionCheckAppearSync) {
7576
+ removeSyncCheck = window.MotionCheckAppearSync(this, key, value);
7577
+ }
7569
7578
  this.valueSubscriptions.set(key, () => {
7570
7579
  removeOnChange();
7571
7580
  removeOnRenderRequest();
7581
+ if (removeSyncCheck)
7582
+ removeSyncCheck();
7572
7583
  if (value.owner)
7573
7584
  value.stop();
7574
7585
  });
@@ -8969,8 +8980,9 @@ function cancelTreeOptimisedTransformAnimations(projectionNode) {
8969
8980
  if (!visualElement)
8970
8981
  return;
8971
8982
  const appearId = getOptimisedAppearId(visualElement);
8972
- if (window.MotionHasOptimisedTransformAnimation(appearId)) {
8973
- window.MotionCancelOptimisedTransform(appearId);
8983
+ if (window.MotionHasOptimisedAnimation(appearId, "transform")) {
8984
+ const { layout, layoutId } = projectionNode.options;
8985
+ window.MotionCancelOptimisedAnimation(appearId, "transform", frame, !(layout || layoutId));
8974
8986
  }
8975
8987
  const { parent } = projectionNode;
8976
8988
  if (parent && !parent.hasCheckedOptimisedAppear) {
@@ -9298,7 +9310,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
9298
9310
  * snapshots in startUpdate(), but we only want to cancel optimised animations
9299
9311
  * if a layout animation measurement is actually going to be affected by them.
9300
9312
  */
9301
- if (window.MotionCancelOptimisedTransform &&
9313
+ if (window.MotionCancelOptimisedAnimation &&
9302
9314
  !this.hasCheckedOptimisedAppear) {
9303
9315
  cancelTreeOptimisedTransformAnimations(this);
9304
9316
  }
@@ -12181,21 +12193,33 @@ function useResetProjection() {
12181
12193
  return reset;
12182
12194
  }
12183
12195
 
12184
- const appearStoreId = (id, value) => `${id}: ${value}`;
12196
+ const appearStoreId = (elementId, valueName) => {
12197
+ const key = transformProps.has(valueName) ? "transform" : valueName;
12198
+ return `${elementId}: ${key}`;
12199
+ };
12185
12200
 
12186
12201
  const appearAnimationStore = new Map();
12187
12202
  const elementsWithAppearAnimations = new Set();
12188
12203
 
12189
12204
  function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
12190
- const optimisedValueName = transformProps.has(valueName)
12191
- ? "transform"
12192
- : valueName;
12193
- const storeId = appearStoreId(elementId, optimisedValueName);
12205
+ const storeId = appearStoreId(elementId, valueName);
12194
12206
  const optimisedAnimation = appearAnimationStore.get(storeId);
12195
12207
  if (!optimisedAnimation) {
12196
12208
  return null;
12197
12209
  }
12198
12210
  const { animation, startTime } = optimisedAnimation;
12211
+ function cancelAnimation() {
12212
+ var _a;
12213
+ (_a = window.MotionCancelOptimisedAnimation) === null || _a === void 0 ? void 0 : _a.call(window, elementId, valueName, frame);
12214
+ }
12215
+ /**
12216
+ * We can cancel the animation once it's finished now that we've synced
12217
+ * with Motion.
12218
+ *
12219
+ * Prefer onfinish over finished as onfinish is backwards compatible with
12220
+ * older browsers.
12221
+ */
12222
+ animation.onfinish = cancelAnimation;
12199
12223
  if (startTime === null || window.MotionHandoffIsComplete) {
12200
12224
  /**
12201
12225
  * If the startTime is null, this animation is the Paint Ready detection animation
@@ -12204,13 +12228,7 @@ function handoffOptimizedAppearAnimation(elementId, valueName, frame) {
12204
12228
  * Or if we've already handed off the animation then we're now interrupting it.
12205
12229
  * In which case we need to cancel it.
12206
12230
  */
12207
- appearAnimationStore.delete(storeId);
12208
- frame.render(() => frame.render(() => {
12209
- try {
12210
- animation.cancel();
12211
- }
12212
- catch (error) { }
12213
- }));
12231
+ cancelAnimation();
12214
12232
  return null;
12215
12233
  }
12216
12234
  else {
@@ -12231,6 +12249,18 @@ let startFrameTime;
12231
12249
  * https://bugs.chromium.org/p/chromium/issues/detail?id=1406850
12232
12250
  */
12233
12251
  let readyAnimation;
12252
+ /**
12253
+ * Keep track of animations that were suspended vs cancelled so we
12254
+ * can easily resume them when we're done measuring layout.
12255
+ */
12256
+ const suspendedAnimations = new Set();
12257
+ function resumeSuspendedAnimations() {
12258
+ suspendedAnimations.forEach((data) => {
12259
+ data.animation.play();
12260
+ data.animation.startTime = data.startTime;
12261
+ });
12262
+ suspendedAnimations.clear();
12263
+ }
12234
12264
  function startOptimizedAppearAnimation(element, name, keyframes, options, onReady) {
12235
12265
  // Prevent optimised appear animations if Motion has already started animating.
12236
12266
  if (window.MotionHandoffIsComplete) {
@@ -12258,10 +12288,22 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12258
12288
  * of handoff animations.
12259
12289
  */
12260
12290
  window.MotionHandoffAnimation = handoffOptimizedAppearAnimation;
12261
- window.MotionHasOptimisedTransformAnimation = (elementId) => {
12291
+ window.MotionHasOptimisedAnimation = (elementId, valueName) => {
12262
12292
  if (!elementId)
12263
12293
  return false;
12264
- const animationId = appearStoreId(elementId, "transform");
12294
+ /**
12295
+ * Keep a map of elementIds that have started animating. We check
12296
+ * via ID instead of Element because of hydration errors and
12297
+ * pre-hydration checks. We also actively record IDs as they start
12298
+ * animating rather than simply checking for data-appear-id as
12299
+ * this attrbute might be present but not lead to an animation, for
12300
+ * instance if the element's appear animation is on a different
12301
+ * breakpoint.
12302
+ */
12303
+ if (!valueName) {
12304
+ return elementsWithAppearAnimations.has(elementId);
12305
+ }
12306
+ const animationId = appearStoreId(elementId, valueName);
12265
12307
  return Boolean(appearAnimationStore.get(animationId));
12266
12308
  };
12267
12309
  /**
@@ -12269,24 +12311,59 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
12269
12311
  * they're the ones that will interfere with the
12270
12312
  * layout animation measurements.
12271
12313
  */
12272
- window.MotionCancelOptimisedTransform = (elementId) => {
12273
- const animationId = appearStoreId(elementId, "transform");
12314
+ window.MotionCancelOptimisedAnimation = (elementId, valueName, frame, canResume) => {
12315
+ const animationId = appearStoreId(elementId, valueName);
12274
12316
  const data = appearAnimationStore.get(animationId);
12275
- if (data) {
12317
+ if (!data)
12318
+ return;
12319
+ if (frame && canResume === undefined) {
12320
+ /**
12321
+ * Wait until the end of the subsequent frame to cancel the animation
12322
+ * to ensure we don't remove the animation before the main thread has
12323
+ * had a chance to resolve keyframes and render.
12324
+ */
12325
+ frame.postRender(() => {
12326
+ frame.postRender(() => {
12327
+ data.animation.cancel();
12328
+ });
12329
+ });
12330
+ }
12331
+ else {
12276
12332
  data.animation.cancel();
12333
+ }
12334
+ if (frame && canResume) {
12335
+ suspendedAnimations.add(data);
12336
+ frame.render(resumeSuspendedAnimations);
12337
+ }
12338
+ else {
12277
12339
  appearAnimationStore.delete(animationId);
12340
+ /**
12341
+ * If there are no more animations left, we can remove the cancel function.
12342
+ * This will let us know when we can stop checking for conflicting layout animations.
12343
+ */
12344
+ if (!appearAnimationStore.size) {
12345
+ window.MotionCancelOptimisedAnimation = undefined;
12346
+ }
12278
12347
  }
12279
12348
  };
12280
- /**
12281
- * Keep a map of elementIds that have started animating. We check
12282
- * via ID instead of Element because of hydration errors and
12283
- * pre-hydration checks. We also actively record IDs as they start
12284
- * animating rather than simply checking for data-appear-id as
12285
- * this attrbute might be present but not lead to an animation, for
12286
- * instance if the element's appear animation is on a different
12287
- * breakpoint.
12288
- */
12289
- window.MotionHasOptimisedAnimation = (elementId) => Boolean(elementId && elementsWithAppearAnimations.has(elementId));
12349
+ window.MotionCheckAppearSync = (visualElement, valueName, value) => {
12350
+ var _a, _b;
12351
+ const appearId = getOptimisedAppearId(visualElement);
12352
+ if (!appearId)
12353
+ return;
12354
+ const valueIsOptimised = (_a = window.MotionHasOptimisedAnimation) === null || _a === void 0 ? void 0 : _a.call(window, appearId, valueName);
12355
+ const externalAnimationValue = (_b = visualElement.props.values) === null || _b === void 0 ? void 0 : _b[valueName];
12356
+ if (!valueIsOptimised || !externalAnimationValue)
12357
+ return;
12358
+ const removeSyncCheck = value.on("change", (latestValue) => {
12359
+ var _a;
12360
+ if (externalAnimationValue.get() !== latestValue) {
12361
+ (_a = window.MotionCancelOptimisedAnimation) === null || _a === void 0 ? void 0 : _a.call(window, appearId, valueName);
12362
+ removeSyncCheck();
12363
+ }
12364
+ });
12365
+ return removeSyncCheck;
12366
+ };
12290
12367
  }
12291
12368
  const startAnimation = () => {
12292
12369
  readyAnimation.cancel();
@@ -855,6 +855,20 @@ interface FrameData {
855
855
  isProcessing: boolean;
856
856
  }
857
857
 
858
+ declare const optimizedAppearDataAttribute: "data-framer-appear-id";
859
+
860
+ /**
861
+ * Expose only the needed part of the VisualElement interface to
862
+ * ensure React types don't end up in the generic DOM bundle.
863
+ */
864
+ interface WithAppearProps {
865
+ props: {
866
+ [optimizedAppearDataAttribute]?: string;
867
+ values?: {
868
+ [key: string]: MotionValue<number> | MotionValue<string>;
869
+ };
870
+ };
871
+ }
858
872
  type HandoffFunction = (storeId: string, valueName: string, frame: Batcher) => number | null;
859
873
  /**
860
874
  * The window global object acts as a bridge between our inline script
@@ -864,9 +878,9 @@ declare global {
864
878
  interface Window {
865
879
  MotionHandoffAnimation?: HandoffFunction;
866
880
  MotionHandoffIsComplete?: boolean;
867
- MotionCancelOptimisedTransform?: (id?: string) => void;
868
- MotionHasOptimisedTransformAnimation?: (id?: string) => boolean;
869
- MotionHasOptimisedAnimation?: (id?: string) => boolean;
881
+ MotionHasOptimisedAnimation?: (elementId?: string, valueName?: string) => boolean;
882
+ MotionCancelOptimisedAnimation?: (elementId?: string, valueName?: string, frame?: Batcher, canResume?: boolean) => void;
883
+ MotionCheckAppearSync?: (visualElement: WithAppearProps, valueName: string, value: MotionValue) => VoidFunction | void;
870
884
  }
871
885
  }
872
886