framer-motion 7.6.7 → 7.6.9

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.
@@ -248,11 +248,11 @@
248
248
  hasEverUpdated: false,
249
249
  };
250
250
 
251
- let id$1 = 1;
251
+ let id$2 = 1;
252
252
  function useProjectionId() {
253
253
  return useConstant(() => {
254
254
  if (globalProjectionState.hasEverUpdated) {
255
- return id$1++;
255
+ return id$2++;
256
256
  }
257
257
  });
258
258
  }
@@ -3426,7 +3426,7 @@
3426
3426
  * This will be replaced by the build step with the latest version number.
3427
3427
  * When MotionValues are provided to motion components, warn if versions are mixed.
3428
3428
  */
3429
- this.version = "7.6.7";
3429
+ this.version = "7.6.9";
3430
3430
  /**
3431
3431
  * Duration, in milliseconds, since last updating frame.
3432
3432
  *
@@ -4783,7 +4783,10 @@
4783
4783
  node.options.layoutScroll &&
4784
4784
  node.scroll &&
4785
4785
  node !== node.root) {
4786
- transformBox(box, { x: -node.scroll.x, y: -node.scroll.y });
4786
+ transformBox(box, {
4787
+ x: -node.scroll.offset.x,
4788
+ y: -node.scroll.offset.y,
4789
+ });
4787
4790
  }
4788
4791
  if (delta) {
4789
4792
  // Incoporate each ancestor's scale into a culmulative treeScale for this component
@@ -4832,8 +4835,8 @@
4832
4835
  const viewportBox = measureViewportBox(element, transformPagePoint);
4833
4836
  const { scroll } = rootProjectionNode;
4834
4837
  if (scroll) {
4835
- translateAxis(viewportBox.x, scroll.x);
4836
- translateAxis(viewportBox.y, scroll.y);
4838
+ translateAxis(viewportBox.x, scroll.offset.x);
4839
+ translateAxis(viewportBox.y, scroll.offset.y);
4837
4840
  }
4838
4841
  return viewportBox;
4839
4842
  }
@@ -5689,7 +5692,7 @@
5689
5692
  * and warn against mismatches.
5690
5693
  */
5691
5694
  {
5692
- warnOnce(nextValue.version === "7.6.7", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.7 may not work as expected.`);
5695
+ warnOnce(nextValue.version === "7.6.9", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.9 may not work as expected.`);
5693
5696
  }
5694
5697
  }
5695
5698
  else if (isMotionValue(prevValue)) {
@@ -5891,10 +5894,14 @@
5891
5894
  this.current = null;
5892
5895
  }
5893
5896
  bindToMotionValue(key, value) {
5897
+ const valueIsTransform = transformProps.has(key);
5894
5898
  const removeOnChange = value.onChange((latestValue) => {
5895
5899
  this.latestValues[key] = latestValue;
5896
5900
  this.props.onUpdate &&
5897
5901
  sync.update(this.notifyUpdate, false, true);
5902
+ if (valueIsTransform && this.projection) {
5903
+ this.projection.isProjectionDirty = true;
5904
+ }
5898
5905
  });
5899
5906
  const removeOnRenderRequest = value.onRenderRequest(this.scheduleRender);
5900
5907
  this.valueSubscriptions.set(key, () => {
@@ -6767,7 +6774,6 @@
6767
6774
  node.snapshot = prevLead.snapshot;
6768
6775
  node.snapshot.latestValues =
6769
6776
  prevLead.animationValues || prevLead.latestValues;
6770
- node.snapshot.isShared = true;
6771
6777
  }
6772
6778
  if ((_a = node.root) === null || _a === void 0 ? void 0 : _a.isUpdating) {
6773
6779
  node.isLayoutDirty = true;
@@ -6813,8 +6819,8 @@
6813
6819
  }
6814
6820
  }
6815
6821
 
6816
- const identityProjection = "translate3d(0px, 0px, 0) scale(1, 1) scale(1, 1)";
6817
6822
  function buildProjectionTransform(delta, treeScale, latestTransform) {
6823
+ let transform = "";
6818
6824
  /**
6819
6825
  * The translations we use to calculate are always relative to the viewport coordinate space.
6820
6826
  * But when we apply scales, we also scale the coordinate space of an element and its children.
@@ -6823,12 +6829,16 @@
6823
6829
  */
6824
6830
  const xTranslate = delta.x.translate / treeScale.x;
6825
6831
  const yTranslate = delta.y.translate / treeScale.y;
6826
- let transform = `translate3d(${xTranslate}px, ${yTranslate}px, 0) `;
6832
+ if (xTranslate || yTranslate) {
6833
+ transform = `translate3d(${xTranslate}px, ${yTranslate}px, 0) `;
6834
+ }
6827
6835
  /**
6828
6836
  * Apply scale correction for the tree transform.
6829
6837
  * This will apply scale to the screen-orientated axes.
6830
6838
  */
6831
- transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
6839
+ if (treeScale.x !== 1 || treeScale.y !== 1) {
6840
+ transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
6841
+ }
6832
6842
  if (latestTransform) {
6833
6843
  const { rotate, rotateX, rotateY } = latestTransform;
6834
6844
  if (rotate)
@@ -6844,8 +6854,10 @@
6844
6854
  */
6845
6855
  const elementScaleX = delta.x.scale * treeScale.x;
6846
6856
  const elementScaleY = delta.y.scale * treeScale.y;
6847
- transform += `scale(${elementScaleX}, ${elementScaleY})`;
6848
- return transform === identityProjection ? "none" : transform;
6857
+ if (elementScaleX !== 1 || elementScaleY !== 1) {
6858
+ transform += `scale(${elementScaleX}, ${elementScaleY})`;
6859
+ }
6860
+ return transform || "none";
6849
6861
  }
6850
6862
 
6851
6863
  const compareByDepth = (a, b) => a.depth - b.depth;
@@ -6876,9 +6888,18 @@
6876
6888
  * which has a noticeable difference in spring animations
6877
6889
  */
6878
6890
  const animationTarget = 1000;
6891
+ let id$1 = 0;
6879
6892
  function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
6880
6893
  return class ProjectionNode {
6881
6894
  constructor(elementId, latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
6895
+ /**
6896
+ * A unique ID generated for every projection node.
6897
+ */
6898
+ this.id = id$1++;
6899
+ /**
6900
+ * An id that represents a unique session instigated by startUpdate.
6901
+ */
6902
+ this.animationId = 0;
6882
6903
  /**
6883
6904
  * A Set containing all this component's children. This is used to iterate
6884
6905
  * through the children.
@@ -6905,6 +6926,11 @@
6905
6926
  * and if one node is dirtied, they all are.
6906
6927
  */
6907
6928
  this.isLayoutDirty = false;
6929
+ /**
6930
+ * Flag to true if we think the projection calculations for this or any
6931
+ * child might need recalculating as a result of an updated transform or layout animation.
6932
+ */
6933
+ this.isProjectionDirty = false;
6908
6934
  /**
6909
6935
  * Block layout updates for instant layout transitions throughout the tree.
6910
6936
  */
@@ -6987,8 +7013,8 @@
6987
7013
  hasListeners(name) {
6988
7014
  return this.eventHandlers.has(name);
6989
7015
  }
6990
- registerPotentialNode(id, node) {
6991
- this.potentialNodes.set(id, node);
7016
+ registerPotentialNode(elementId, node) {
7017
+ this.potentialNodes.set(elementId, node);
6992
7018
  }
6993
7019
  /**
6994
7020
  * Lifecycles
@@ -7121,6 +7147,7 @@
7121
7147
  return;
7122
7148
  this.isUpdating = true;
7123
7149
  (_a = this.nodes) === null || _a === void 0 ? void 0 : _a.forEach(resetRotation);
7150
+ this.animationId++;
7124
7151
  }
7125
7152
  willUpdate(shouldNotifyListeners = true) {
7126
7153
  var _a, _b, _c;
@@ -7135,11 +7162,7 @@
7135
7162
  for (let i = 0; i < this.path.length; i++) {
7136
7163
  const node = this.path[i];
7137
7164
  node.shouldResetTransform = true;
7138
- /**
7139
- * TODO: Check we haven't updated the scroll
7140
- * since the last didUpdate
7141
- */
7142
- node.updateScroll();
7165
+ node.updateScroll("snapshot");
7143
7166
  }
7144
7167
  const { layoutId, layout } = this.options;
7145
7168
  if (layoutId === undefined && !layout)
@@ -7255,10 +7278,20 @@
7255
7278
  this.notifyListeners("measure", this.layout.layoutBox);
7256
7279
  (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.notify("LayoutMeasure", this.layout.layoutBox, prevLayout === null || prevLayout === void 0 ? void 0 : prevLayout.layoutBox);
7257
7280
  }
7258
- updateScroll() {
7259
- if (this.options.layoutScroll && this.instance) {
7260
- this.isScrollRoot = checkIsScrollRoot(this.instance);
7261
- this.scroll = measureScroll(this.instance);
7281
+ updateScroll(phase = "measure") {
7282
+ let needsMeasurement = Boolean(this.options.layoutScroll && this.instance);
7283
+ if (this.scroll &&
7284
+ this.scroll.animationId === this.root.animationId &&
7285
+ this.scroll.phase === phase) {
7286
+ needsMeasurement = false;
7287
+ }
7288
+ if (needsMeasurement) {
7289
+ this.scroll = {
7290
+ animationId: this.root.animationId,
7291
+ phase,
7292
+ isRoot: checkIsScrollRoot(this.instance),
7293
+ offset: measureScroll(this.instance),
7294
+ };
7262
7295
  }
7263
7296
  }
7264
7297
  resetTransform() {
@@ -7280,6 +7313,7 @@
7280
7313
  }
7281
7314
  }
7282
7315
  measure(removeTransform = true) {
7316
+ var _a;
7283
7317
  const pageBox = this.measurePageBox();
7284
7318
  let layoutBox = this.removeElementScroll(pageBox);
7285
7319
  /**
@@ -7291,10 +7325,17 @@
7291
7325
  layoutBox = this.removeTransform(layoutBox);
7292
7326
  }
7293
7327
  roundBox(layoutBox);
7328
+ const positionStyle = (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.readValue("position");
7329
+ const position = positionStyle === "fixed" || positionStyle === "sticky"
7330
+ ? positionStyle
7331
+ : "static";
7294
7332
  return {
7333
+ animationId: this.root.animationId,
7295
7334
  measuredBox: pageBox,
7296
7335
  layoutBox,
7297
7336
  latestValues: {},
7337
+ source: this.id,
7338
+ position,
7298
7339
  };
7299
7340
  }
7300
7341
  measurePageBox() {
@@ -7305,8 +7346,8 @@
7305
7346
  // Remove viewport scroll to give page-relative coordinates
7306
7347
  const { scroll } = this.root;
7307
7348
  if (scroll) {
7308
- translateAxis(box.x, scroll.x);
7309
- translateAxis(box.y, scroll.y);
7349
+ translateAxis(box.x, scroll.offset.x);
7350
+ translateAxis(box.y, scroll.offset.y);
7310
7351
  }
7311
7352
  return box;
7312
7353
  }
@@ -7319,13 +7360,13 @@
7319
7360
  */
7320
7361
  for (let i = 0; i < this.path.length; i++) {
7321
7362
  const node = this.path[i];
7322
- const { scroll, options, isScrollRoot } = node;
7363
+ const { scroll, options } = node;
7323
7364
  if (node !== this.root && scroll && options.layoutScroll) {
7324
7365
  /**
7325
7366
  * If this is a new scroll root, we want to remove all previous scrolls
7326
7367
  * from the viewport box.
7327
7368
  */
7328
- if (isScrollRoot) {
7369
+ if (scroll.isRoot) {
7329
7370
  copyBoxInto(boxWithoutScroll, box);
7330
7371
  const { scroll: rootScroll } = this.root;
7331
7372
  /**
@@ -7333,12 +7374,12 @@
7333
7374
  * to the measured bounding box.
7334
7375
  */
7335
7376
  if (rootScroll) {
7336
- translateAxis(boxWithoutScroll.x, -rootScroll.x);
7337
- translateAxis(boxWithoutScroll.y, -rootScroll.y);
7377
+ translateAxis(boxWithoutScroll.x, -rootScroll.offset.x);
7378
+ translateAxis(boxWithoutScroll.y, -rootScroll.offset.y);
7338
7379
  }
7339
7380
  }
7340
- translateAxis(boxWithoutScroll.x, scroll.x);
7341
- translateAxis(boxWithoutScroll.y, scroll.y);
7381
+ translateAxis(boxWithoutScroll.x, scroll.offset.x);
7382
+ translateAxis(boxWithoutScroll.y, scroll.offset.y);
7342
7383
  }
7343
7384
  }
7344
7385
  return boxWithoutScroll;
@@ -7353,8 +7394,8 @@
7353
7394
  node.scroll &&
7354
7395
  node !== node.root) {
7355
7396
  transformBox(withTransforms, {
7356
- x: -node.scroll.x,
7357
- y: -node.scroll.y,
7397
+ x: -node.scroll.offset.x,
7398
+ y: -node.scroll.offset.y,
7358
7399
  });
7359
7400
  }
7360
7401
  if (!hasTransform(node.latestValues))
@@ -7392,6 +7433,7 @@
7392
7433
  */
7393
7434
  setTargetDelta(delta) {
7394
7435
  this.targetDelta = delta;
7436
+ this.isProjectionDirty = true;
7395
7437
  this.root.scheduleUpdateProjection();
7396
7438
  }
7397
7439
  setOptions(options) {
@@ -7415,6 +7457,14 @@
7415
7457
  */
7416
7458
  resolveTargetDelta() {
7417
7459
  var _a;
7460
+ /**
7461
+ * Propagate isProjectionDirty. Nodes are ordered by depth, so if the parent here
7462
+ * is dirty we can simply pass this forward.
7463
+ */
7464
+ this.isProjectionDirty || (this.isProjectionDirty = this.getLead().isProjectionDirty ||
7465
+ Boolean(this.parent && this.parent.isProjectionDirty));
7466
+ if (!this.isProjectionDirty)
7467
+ return;
7418
7468
  const { layout, layoutId } = this.options;
7419
7469
  /**
7420
7470
  * If we have no layout, we can't perform projection, so early return
@@ -7518,6 +7568,9 @@
7518
7568
  }
7519
7569
  calcProjection() {
7520
7570
  var _a;
7571
+ if (!this.isProjectionDirty)
7572
+ return;
7573
+ this.isProjectionDirty = false;
7521
7574
  const { layout, layoutId } = this.options;
7522
7575
  /**
7523
7576
  * If this section of the tree isn't animating we can
@@ -7588,7 +7641,7 @@
7588
7641
  }
7589
7642
  }
7590
7643
  setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
7591
- var _a;
7644
+ var _a, _b;
7592
7645
  const snapshot = this.snapshot;
7593
7646
  const snapshotLatestValues = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.latestValues) || {};
7594
7647
  const mixedValues = { ...this.latestValues };
@@ -7596,8 +7649,8 @@
7596
7649
  this.relativeTarget = this.relativeTargetOrigin = undefined;
7597
7650
  this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
7598
7651
  const relativeLayout = createBox();
7599
- const isSharedLayoutAnimation = snapshot === null || snapshot === void 0 ? void 0 : snapshot.isShared;
7600
- const isOnlyMember = (((_a = this.getStack()) === null || _a === void 0 ? void 0 : _a.members.length) || 0) <= 1;
7652
+ const isSharedLayoutAnimation = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.source) !== ((_a = this.layout) === null || _a === void 0 ? void 0 : _a.source);
7653
+ const isOnlyMember = (((_b = this.getStack()) === null || _b === void 0 ? void 0 : _b.members.length) || 0) <= 1;
7601
7654
  const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
7602
7655
  !isOnlyMember &&
7603
7656
  this.options.crossfade === true &&
@@ -7778,25 +7831,30 @@
7778
7831
  return;
7779
7832
  // If there's no detected rotation values, we can early return without a forced render.
7780
7833
  let hasRotate = false;
7781
- // Keep a record of all the values we've reset
7782
- const resetValues = {};
7783
- // Check the rotate value of all axes and reset to 0
7784
- for (let i = 0; i < transformAxes.length; i++) {
7785
- const axis = transformAxes[i];
7786
- const key = "rotate" + axis;
7787
- // If this rotation doesn't exist as a motion value, then we don't
7788
- // need to reset it
7789
- if (!visualElement.getStaticValue(key)) {
7790
- continue;
7791
- }
7834
+ /**
7835
+ * An unrolled check for rotation values. Most elements don't have any rotation and
7836
+ * skipping the nested loop and new object creation is 50% faster.
7837
+ */
7838
+ const { latestValues } = visualElement;
7839
+ if (latestValues.rotate ||
7840
+ latestValues.rotateX ||
7841
+ latestValues.rotateY ||
7842
+ latestValues.rotateZ) {
7792
7843
  hasRotate = true;
7793
- // Record the rotation and then temporarily set it to 0
7794
- resetValues[key] = visualElement.getStaticValue(key);
7795
- visualElement.setStaticValue(key, 0);
7796
7844
  }
7797
7845
  // If there's no rotation values, we don't need to do any more.
7798
7846
  if (!hasRotate)
7799
7847
  return;
7848
+ const resetValues = {};
7849
+ // Check the rotate value of all axes and reset to 0
7850
+ for (let i = 0; i < transformAxes.length; i++) {
7851
+ const key = "rotate" + transformAxes[i];
7852
+ // Record the rotation and then temporarily set it to 0
7853
+ if (latestValues[key]) {
7854
+ resetValues[key] = latestValues[key];
7855
+ visualElement.setStaticValue(key, 0);
7856
+ }
7857
+ }
7800
7858
  // Force a render of this element to apply the transform with all rotations
7801
7859
  // set to 0.
7802
7860
  visualElement === null || visualElement === void 0 ? void 0 : visualElement.render();
@@ -7938,11 +7996,12 @@
7938
7996
  node.hasListeners("didUpdate")) {
7939
7997
  const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
7940
7998
  const { animationType } = node.options;
7999
+ const isShared = snapshot.source !== node.layout.source;
7941
8000
  // TODO Maybe we want to also resize the layout snapshot so we don't trigger
7942
8001
  // animations for instance if layout="size" and an element has only changed position
7943
8002
  if (animationType === "size") {
7944
8003
  eachAxis((axis) => {
7945
- const axisSnapshot = snapshot.isShared
8004
+ const axisSnapshot = isShared
7946
8005
  ? snapshot.measuredBox[axis]
7947
8006
  : snapshot.layoutBox[axis];
7948
8007
  const length = calcLength(axisSnapshot);
@@ -7952,7 +8011,7 @@
7952
8011
  }
7953
8012
  else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
7954
8013
  eachAxis((axis) => {
7955
- const axisSnapshot = snapshot.isShared
8014
+ const axisSnapshot = isShared
7956
8015
  ? snapshot.measuredBox[axis]
7957
8016
  : snapshot.layoutBox[axis];
7958
8017
  const length = calcLength(layout[axis]);
@@ -7962,7 +8021,7 @@
7962
8021
  const layoutDelta = createDelta();
7963
8022
  calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
7964
8023
  const visualDelta = createDelta();
7965
- if (snapshot.isShared) {
8024
+ if (isShared) {
7966
8025
  calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
7967
8026
  }
7968
8027
  else {
@@ -8058,7 +8117,7 @@
8058
8117
  duration: 0.45,
8059
8118
  ease: [0.4, 0, 0.1, 1],
8060
8119
  };
8061
- function mountNodeEarly(node, id) {
8120
+ function mountNodeEarly(node, elementId) {
8062
8121
  /**
8063
8122
  * Rather than searching the DOM from document we can search the
8064
8123
  * path for the deepest mounted ancestor and search from there
@@ -8071,7 +8130,7 @@
8071
8130
  }
8072
8131
  }
8073
8132
  const searchElement = searchNode && searchNode !== node.root ? searchNode.instance : document;
8074
- const element = searchElement.querySelector(`[data-projection-id="${id}"]`);
8133
+ const element = searchElement.querySelector(`[data-projection-id="${elementId}"]`);
8075
8134
  if (element)
8076
8135
  node.mount(element, true);
8077
8136
  }