framer-motion 7.6.7 → 7.6.8

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
@@ -254,11 +254,11 @@ const globalProjectionState = {
254
254
  hasEverUpdated: false,
255
255
  };
256
256
 
257
- let id$1 = 1;
257
+ let id$2 = 1;
258
258
  function useProjectionId() {
259
259
  return useConstant(() => {
260
260
  if (globalProjectionState.hasEverUpdated) {
261
- return id$1++;
261
+ return id$2++;
262
262
  }
263
263
  });
264
264
  }
@@ -2258,7 +2258,7 @@ class MotionValue {
2258
2258
  * This will be replaced by the build step with the latest version number.
2259
2259
  * When MotionValues are provided to motion components, warn if versions are mixed.
2260
2260
  */
2261
- this.version = "7.6.7";
2261
+ this.version = "7.6.8";
2262
2262
  /**
2263
2263
  * Duration, in milliseconds, since last updating frame.
2264
2264
  *
@@ -3615,7 +3615,10 @@ function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
3615
3615
  node.options.layoutScroll &&
3616
3616
  node.scroll &&
3617
3617
  node !== node.root) {
3618
- transformBox(box, { x: -node.scroll.x, y: -node.scroll.y });
3618
+ transformBox(box, {
3619
+ x: -node.scroll.offset.x,
3620
+ y: -node.scroll.offset.y,
3621
+ });
3619
3622
  }
3620
3623
  if (delta) {
3621
3624
  // Incoporate each ancestor's scale into a culmulative treeScale for this component
@@ -3664,8 +3667,8 @@ function measurePageBox(element, rootProjectionNode, transformPagePoint) {
3664
3667
  const viewportBox = measureViewportBox(element, transformPagePoint);
3665
3668
  const { scroll } = rootProjectionNode;
3666
3669
  if (scroll) {
3667
- translateAxis(viewportBox.x, scroll.x);
3668
- translateAxis(viewportBox.y, scroll.y);
3670
+ translateAxis(viewportBox.x, scroll.offset.x);
3671
+ translateAxis(viewportBox.y, scroll.offset.y);
3669
3672
  }
3670
3673
  return viewportBox;
3671
3674
  }
@@ -4521,7 +4524,7 @@ function updateMotionValuesFromProps(element, next, prev) {
4521
4524
  * and warn against mismatches.
4522
4525
  */
4523
4526
  if (process.env.NODE_ENV === "development") {
4524
- warnOnce(nextValue.version === "7.6.7", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.7 may not work as expected.`);
4527
+ warnOnce(nextValue.version === "7.6.8", `Attempting to mix Framer Motion versions ${nextValue.version} with 7.6.8 may not work as expected.`);
4525
4528
  }
4526
4529
  }
4527
4530
  else if (isMotionValue(prevValue)) {
@@ -5599,7 +5602,6 @@ class NodeStack {
5599
5602
  node.snapshot = prevLead.snapshot;
5600
5603
  node.snapshot.latestValues =
5601
5604
  prevLead.animationValues || prevLead.latestValues;
5602
- node.snapshot.isShared = true;
5603
5605
  }
5604
5606
  if ((_a = node.root) === null || _a === void 0 ? void 0 : _a.isUpdating) {
5605
5607
  node.isLayoutDirty = true;
@@ -5645,8 +5647,8 @@ class NodeStack {
5645
5647
  }
5646
5648
  }
5647
5649
 
5648
- const identityProjection = "translate3d(0px, 0px, 0) scale(1, 1) scale(1, 1)";
5649
5650
  function buildProjectionTransform(delta, treeScale, latestTransform) {
5651
+ let transform = "";
5650
5652
  /**
5651
5653
  * The translations we use to calculate are always relative to the viewport coordinate space.
5652
5654
  * But when we apply scales, we also scale the coordinate space of an element and its children.
@@ -5655,12 +5657,16 @@ function buildProjectionTransform(delta, treeScale, latestTransform) {
5655
5657
  */
5656
5658
  const xTranslate = delta.x.translate / treeScale.x;
5657
5659
  const yTranslate = delta.y.translate / treeScale.y;
5658
- let transform = `translate3d(${xTranslate}px, ${yTranslate}px, 0) `;
5660
+ if (xTranslate || yTranslate) {
5661
+ transform = `translate3d(${xTranslate}px, ${yTranslate}px, 0) `;
5662
+ }
5659
5663
  /**
5660
5664
  * Apply scale correction for the tree transform.
5661
5665
  * This will apply scale to the screen-orientated axes.
5662
5666
  */
5663
- transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
5667
+ if (treeScale.x !== 1 || treeScale.y !== 1) {
5668
+ transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
5669
+ }
5664
5670
  if (latestTransform) {
5665
5671
  const { rotate, rotateX, rotateY } = latestTransform;
5666
5672
  if (rotate)
@@ -5676,8 +5682,10 @@ function buildProjectionTransform(delta, treeScale, latestTransform) {
5676
5682
  */
5677
5683
  const elementScaleX = delta.x.scale * treeScale.x;
5678
5684
  const elementScaleY = delta.y.scale * treeScale.y;
5679
- transform += `scale(${elementScaleX}, ${elementScaleY})`;
5680
- return transform === identityProjection ? "none" : transform;
5685
+ if (elementScaleX !== 1 || elementScaleY !== 1) {
5686
+ transform += `scale(${elementScaleX}, ${elementScaleY})`;
5687
+ }
5688
+ return transform || "none";
5681
5689
  }
5682
5690
 
5683
5691
  const compareByDepth = (a, b) => a.depth - b.depth;
@@ -5708,9 +5716,18 @@ const transformAxes = ["", "X", "Y", "Z"];
5708
5716
  * which has a noticeable difference in spring animations
5709
5717
  */
5710
5718
  const animationTarget = 1000;
5719
+ let id$1 = 0;
5711
5720
  function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
5712
5721
  return class ProjectionNode {
5713
5722
  constructor(elementId, latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
5723
+ /**
5724
+ * A unique ID generated for every projection node.
5725
+ */
5726
+ this.id = id$1++;
5727
+ /**
5728
+ * An id that represents a unique session instigated by startUpdate.
5729
+ */
5730
+ this.animationId = 0;
5714
5731
  /**
5715
5732
  * A Set containing all this component's children. This is used to iterate
5716
5733
  * through the children.
@@ -5819,8 +5836,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
5819
5836
  hasListeners(name) {
5820
5837
  return this.eventHandlers.has(name);
5821
5838
  }
5822
- registerPotentialNode(id, node) {
5823
- this.potentialNodes.set(id, node);
5839
+ registerPotentialNode(elementId, node) {
5840
+ this.potentialNodes.set(elementId, node);
5824
5841
  }
5825
5842
  /**
5826
5843
  * Lifecycles
@@ -5953,6 +5970,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
5953
5970
  return;
5954
5971
  this.isUpdating = true;
5955
5972
  (_a = this.nodes) === null || _a === void 0 ? void 0 : _a.forEach(resetRotation);
5973
+ this.animationId++;
5956
5974
  }
5957
5975
  willUpdate(shouldNotifyListeners = true) {
5958
5976
  var _a, _b, _c;
@@ -5967,11 +5985,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
5967
5985
  for (let i = 0; i < this.path.length; i++) {
5968
5986
  const node = this.path[i];
5969
5987
  node.shouldResetTransform = true;
5970
- /**
5971
- * TODO: Check we haven't updated the scroll
5972
- * since the last didUpdate
5973
- */
5974
- node.updateScroll();
5988
+ node.updateScroll("snapshot");
5975
5989
  }
5976
5990
  const { layoutId, layout } = this.options;
5977
5991
  if (layoutId === undefined && !layout)
@@ -6087,10 +6101,20 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6087
6101
  this.notifyListeners("measure", this.layout.layoutBox);
6088
6102
  (_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);
6089
6103
  }
6090
- updateScroll() {
6091
- if (this.options.layoutScroll && this.instance) {
6092
- this.isScrollRoot = checkIsScrollRoot(this.instance);
6093
- this.scroll = measureScroll(this.instance);
6104
+ updateScroll(phase = "measure") {
6105
+ let needsMeasurement = Boolean(this.options.layoutScroll && this.instance);
6106
+ if (this.scroll &&
6107
+ this.scroll.animationId === this.root.animationId &&
6108
+ this.scroll.phase === phase) {
6109
+ needsMeasurement = false;
6110
+ }
6111
+ if (needsMeasurement) {
6112
+ this.scroll = {
6113
+ animationId: this.root.animationId,
6114
+ phase,
6115
+ isRoot: checkIsScrollRoot(this.instance),
6116
+ offset: measureScroll(this.instance),
6117
+ };
6094
6118
  }
6095
6119
  }
6096
6120
  resetTransform() {
@@ -6112,6 +6136,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6112
6136
  }
6113
6137
  }
6114
6138
  measure(removeTransform = true) {
6139
+ var _a;
6115
6140
  const pageBox = this.measurePageBox();
6116
6141
  let layoutBox = this.removeElementScroll(pageBox);
6117
6142
  /**
@@ -6123,10 +6148,17 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6123
6148
  layoutBox = this.removeTransform(layoutBox);
6124
6149
  }
6125
6150
  roundBox(layoutBox);
6151
+ const positionStyle = (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.readValue("position");
6152
+ const position = positionStyle === "fixed" || positionStyle === "sticky"
6153
+ ? positionStyle
6154
+ : "static";
6126
6155
  return {
6156
+ animationId: this.root.animationId,
6127
6157
  measuredBox: pageBox,
6128
6158
  layoutBox,
6129
6159
  latestValues: {},
6160
+ source: this.id,
6161
+ position,
6130
6162
  };
6131
6163
  }
6132
6164
  measurePageBox() {
@@ -6137,8 +6169,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6137
6169
  // Remove viewport scroll to give page-relative coordinates
6138
6170
  const { scroll } = this.root;
6139
6171
  if (scroll) {
6140
- translateAxis(box.x, scroll.x);
6141
- translateAxis(box.y, scroll.y);
6172
+ translateAxis(box.x, scroll.offset.x);
6173
+ translateAxis(box.y, scroll.offset.y);
6142
6174
  }
6143
6175
  return box;
6144
6176
  }
@@ -6151,13 +6183,13 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6151
6183
  */
6152
6184
  for (let i = 0; i < this.path.length; i++) {
6153
6185
  const node = this.path[i];
6154
- const { scroll, options, isScrollRoot } = node;
6186
+ const { scroll, options } = node;
6155
6187
  if (node !== this.root && scroll && options.layoutScroll) {
6156
6188
  /**
6157
6189
  * If this is a new scroll root, we want to remove all previous scrolls
6158
6190
  * from the viewport box.
6159
6191
  */
6160
- if (isScrollRoot) {
6192
+ if (scroll.isRoot) {
6161
6193
  copyBoxInto(boxWithoutScroll, box);
6162
6194
  const { scroll: rootScroll } = this.root;
6163
6195
  /**
@@ -6165,12 +6197,12 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6165
6197
  * to the measured bounding box.
6166
6198
  */
6167
6199
  if (rootScroll) {
6168
- translateAxis(boxWithoutScroll.x, -rootScroll.x);
6169
- translateAxis(boxWithoutScroll.y, -rootScroll.y);
6200
+ translateAxis(boxWithoutScroll.x, -rootScroll.offset.x);
6201
+ translateAxis(boxWithoutScroll.y, -rootScroll.offset.y);
6170
6202
  }
6171
6203
  }
6172
- translateAxis(boxWithoutScroll.x, scroll.x);
6173
- translateAxis(boxWithoutScroll.y, scroll.y);
6204
+ translateAxis(boxWithoutScroll.x, scroll.offset.x);
6205
+ translateAxis(boxWithoutScroll.y, scroll.offset.y);
6174
6206
  }
6175
6207
  }
6176
6208
  return boxWithoutScroll;
@@ -6185,8 +6217,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6185
6217
  node.scroll &&
6186
6218
  node !== node.root) {
6187
6219
  transformBox(withTransforms, {
6188
- x: -node.scroll.x,
6189
- y: -node.scroll.y,
6220
+ x: -node.scroll.offset.x,
6221
+ y: -node.scroll.offset.y,
6190
6222
  });
6191
6223
  }
6192
6224
  if (!hasTransform(node.latestValues))
@@ -6420,7 +6452,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6420
6452
  }
6421
6453
  }
6422
6454
  setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
6423
- var _a;
6455
+ var _a, _b;
6424
6456
  const snapshot = this.snapshot;
6425
6457
  const snapshotLatestValues = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.latestValues) || {};
6426
6458
  const mixedValues = { ...this.latestValues };
@@ -6428,8 +6460,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6428
6460
  this.relativeTarget = this.relativeTargetOrigin = undefined;
6429
6461
  this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
6430
6462
  const relativeLayout = createBox();
6431
- const isSharedLayoutAnimation = snapshot === null || snapshot === void 0 ? void 0 : snapshot.isShared;
6432
- const isOnlyMember = (((_a = this.getStack()) === null || _a === void 0 ? void 0 : _a.members.length) || 0) <= 1;
6463
+ const isSharedLayoutAnimation = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.source) !== ((_a = this.layout) === null || _a === void 0 ? void 0 : _a.source);
6464
+ const isOnlyMember = (((_b = this.getStack()) === null || _b === void 0 ? void 0 : _b.members.length) || 0) <= 1;
6433
6465
  const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
6434
6466
  !isOnlyMember &&
6435
6467
  this.options.crossfade === true &&
@@ -6610,25 +6642,30 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
6610
6642
  return;
6611
6643
  // If there's no detected rotation values, we can early return without a forced render.
6612
6644
  let hasRotate = false;
6613
- // Keep a record of all the values we've reset
6614
- const resetValues = {};
6615
- // Check the rotate value of all axes and reset to 0
6616
- for (let i = 0; i < transformAxes.length; i++) {
6617
- const axis = transformAxes[i];
6618
- const key = "rotate" + axis;
6619
- // If this rotation doesn't exist as a motion value, then we don't
6620
- // need to reset it
6621
- if (!visualElement.getStaticValue(key)) {
6622
- continue;
6623
- }
6645
+ /**
6646
+ * An unrolled check for rotation values. Most elements don't have any rotation and
6647
+ * skipping the nested loop and new object creation is 50% faster.
6648
+ */
6649
+ const { latestValues } = visualElement;
6650
+ if (latestValues.rotate ||
6651
+ latestValues.rotateX ||
6652
+ latestValues.rotateY ||
6653
+ latestValues.rotateZ) {
6624
6654
  hasRotate = true;
6625
- // Record the rotation and then temporarily set it to 0
6626
- resetValues[key] = visualElement.getStaticValue(key);
6627
- visualElement.setStaticValue(key, 0);
6628
6655
  }
6629
6656
  // If there's no rotation values, we don't need to do any more.
6630
6657
  if (!hasRotate)
6631
6658
  return;
6659
+ const resetValues = {};
6660
+ // Check the rotate value of all axes and reset to 0
6661
+ for (let i = 0; i < transformAxes.length; i++) {
6662
+ const key = "rotate" + transformAxes[i];
6663
+ // Record the rotation and then temporarily set it to 0
6664
+ if (latestValues[key]) {
6665
+ resetValues[key] = latestValues[key];
6666
+ visualElement.setStaticValue(key, 0);
6667
+ }
6668
+ }
6632
6669
  // Force a render of this element to apply the transform with all rotations
6633
6670
  // set to 0.
6634
6671
  visualElement === null || visualElement === void 0 ? void 0 : visualElement.render();
@@ -6770,11 +6807,12 @@ function notifyLayoutUpdate(node) {
6770
6807
  node.hasListeners("didUpdate")) {
6771
6808
  const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
6772
6809
  const { animationType } = node.options;
6810
+ const isShared = snapshot.source !== node.layout.source;
6773
6811
  // TODO Maybe we want to also resize the layout snapshot so we don't trigger
6774
6812
  // animations for instance if layout="size" and an element has only changed position
6775
6813
  if (animationType === "size") {
6776
6814
  eachAxis((axis) => {
6777
- const axisSnapshot = snapshot.isShared
6815
+ const axisSnapshot = isShared
6778
6816
  ? snapshot.measuredBox[axis]
6779
6817
  : snapshot.layoutBox[axis];
6780
6818
  const length = calcLength(axisSnapshot);
@@ -6784,7 +6822,7 @@ function notifyLayoutUpdate(node) {
6784
6822
  }
6785
6823
  else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
6786
6824
  eachAxis((axis) => {
6787
- const axisSnapshot = snapshot.isShared
6825
+ const axisSnapshot = isShared
6788
6826
  ? snapshot.measuredBox[axis]
6789
6827
  : snapshot.layoutBox[axis];
6790
6828
  const length = calcLength(layout[axis]);
@@ -6794,7 +6832,7 @@ function notifyLayoutUpdate(node) {
6794
6832
  const layoutDelta = createDelta();
6795
6833
  calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
6796
6834
  const visualDelta = createDelta();
6797
- if (snapshot.isShared) {
6835
+ if (isShared) {
6798
6836
  calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
6799
6837
  }
6800
6838
  else {
@@ -6890,7 +6928,7 @@ const defaultLayoutTransition = {
6890
6928
  duration: 0.45,
6891
6929
  ease: [0.4, 0, 0.1, 1],
6892
6930
  };
6893
- function mountNodeEarly(node, id) {
6931
+ function mountNodeEarly(node, elementId) {
6894
6932
  /**
6895
6933
  * Rather than searching the DOM from document we can search the
6896
6934
  * path for the deepest mounted ancestor and search from there
@@ -6903,7 +6941,7 @@ function mountNodeEarly(node, id) {
6903
6941
  }
6904
6942
  }
6905
6943
  const searchElement = searchNode && searchNode !== node.root ? searchNode.instance : document;
6906
- const element = searchElement.querySelector(`[data-projection-id="${id}"]`);
6944
+ const element = searchElement.querySelector(`[data-projection-id="${elementId}"]`);
6907
6945
  if (element)
6908
6946
  node.mount(element, true);
6909
6947
  }
@@ -56,7 +56,10 @@ function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
56
56
  node.options.layoutScroll &&
57
57
  node.scroll &&
58
58
  node !== node.root) {
59
- transformBox(box, { x: -node.scroll.x, y: -node.scroll.y });
59
+ transformBox(box, {
60
+ x: -node.scroll.offset.x,
61
+ y: -node.scroll.offset.y,
62
+ });
60
63
  }
61
64
  if (delta) {
62
65
  // Incoporate each ancestor's scale into a culmulative treeScale for this component
@@ -26,9 +26,18 @@ const transformAxes = ["", "X", "Y", "Z"];
26
26
  * which has a noticeable difference in spring animations
27
27
  */
28
28
  const animationTarget = 1000;
29
+ let id = 0;
29
30
  function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
30
31
  return class ProjectionNode {
31
32
  constructor(elementId, latestValues = {}, parent = defaultParent === null || defaultParent === void 0 ? void 0 : defaultParent()) {
33
+ /**
34
+ * A unique ID generated for every projection node.
35
+ */
36
+ this.id = id++;
37
+ /**
38
+ * An id that represents a unique session instigated by startUpdate.
39
+ */
40
+ this.animationId = 0;
32
41
  /**
33
42
  * A Set containing all this component's children. This is used to iterate
34
43
  * through the children.
@@ -137,8 +146,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
137
146
  hasListeners(name) {
138
147
  return this.eventHandlers.has(name);
139
148
  }
140
- registerPotentialNode(id, node) {
141
- this.potentialNodes.set(id, node);
149
+ registerPotentialNode(elementId, node) {
150
+ this.potentialNodes.set(elementId, node);
142
151
  }
143
152
  /**
144
153
  * Lifecycles
@@ -271,6 +280,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
271
280
  return;
272
281
  this.isUpdating = true;
273
282
  (_a = this.nodes) === null || _a === void 0 ? void 0 : _a.forEach(resetRotation);
283
+ this.animationId++;
274
284
  }
275
285
  willUpdate(shouldNotifyListeners = true) {
276
286
  var _a, _b, _c;
@@ -285,11 +295,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
285
295
  for (let i = 0; i < this.path.length; i++) {
286
296
  const node = this.path[i];
287
297
  node.shouldResetTransform = true;
288
- /**
289
- * TODO: Check we haven't updated the scroll
290
- * since the last didUpdate
291
- */
292
- node.updateScroll();
298
+ node.updateScroll("snapshot");
293
299
  }
294
300
  const { layoutId, layout } = this.options;
295
301
  if (layoutId === undefined && !layout)
@@ -405,10 +411,20 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
405
411
  this.notifyListeners("measure", this.layout.layoutBox);
406
412
  (_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);
407
413
  }
408
- updateScroll() {
409
- if (this.options.layoutScroll && this.instance) {
410
- this.isScrollRoot = checkIsScrollRoot(this.instance);
411
- this.scroll = measureScroll(this.instance);
414
+ updateScroll(phase = "measure") {
415
+ let needsMeasurement = Boolean(this.options.layoutScroll && this.instance);
416
+ if (this.scroll &&
417
+ this.scroll.animationId === this.root.animationId &&
418
+ this.scroll.phase === phase) {
419
+ needsMeasurement = false;
420
+ }
421
+ if (needsMeasurement) {
422
+ this.scroll = {
423
+ animationId: this.root.animationId,
424
+ phase,
425
+ isRoot: checkIsScrollRoot(this.instance),
426
+ offset: measureScroll(this.instance),
427
+ };
412
428
  }
413
429
  }
414
430
  resetTransform() {
@@ -430,6 +446,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
430
446
  }
431
447
  }
432
448
  measure(removeTransform = true) {
449
+ var _a;
433
450
  const pageBox = this.measurePageBox();
434
451
  let layoutBox = this.removeElementScroll(pageBox);
435
452
  /**
@@ -441,10 +458,17 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
441
458
  layoutBox = this.removeTransform(layoutBox);
442
459
  }
443
460
  roundBox(layoutBox);
461
+ const positionStyle = (_a = this.options.visualElement) === null || _a === void 0 ? void 0 : _a.readValue("position");
462
+ const position = positionStyle === "fixed" || positionStyle === "sticky"
463
+ ? positionStyle
464
+ : "static";
444
465
  return {
466
+ animationId: this.root.animationId,
445
467
  measuredBox: pageBox,
446
468
  layoutBox,
447
469
  latestValues: {},
470
+ source: this.id,
471
+ position,
448
472
  };
449
473
  }
450
474
  measurePageBox() {
@@ -455,8 +479,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
455
479
  // Remove viewport scroll to give page-relative coordinates
456
480
  const { scroll } = this.root;
457
481
  if (scroll) {
458
- translateAxis(box.x, scroll.x);
459
- translateAxis(box.y, scroll.y);
482
+ translateAxis(box.x, scroll.offset.x);
483
+ translateAxis(box.y, scroll.offset.y);
460
484
  }
461
485
  return box;
462
486
  }
@@ -469,13 +493,13 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
469
493
  */
470
494
  for (let i = 0; i < this.path.length; i++) {
471
495
  const node = this.path[i];
472
- const { scroll, options, isScrollRoot } = node;
496
+ const { scroll, options } = node;
473
497
  if (node !== this.root && scroll && options.layoutScroll) {
474
498
  /**
475
499
  * If this is a new scroll root, we want to remove all previous scrolls
476
500
  * from the viewport box.
477
501
  */
478
- if (isScrollRoot) {
502
+ if (scroll.isRoot) {
479
503
  copyBoxInto(boxWithoutScroll, box);
480
504
  const { scroll: rootScroll } = this.root;
481
505
  /**
@@ -483,12 +507,12 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
483
507
  * to the measured bounding box.
484
508
  */
485
509
  if (rootScroll) {
486
- translateAxis(boxWithoutScroll.x, -rootScroll.x);
487
- translateAxis(boxWithoutScroll.y, -rootScroll.y);
510
+ translateAxis(boxWithoutScroll.x, -rootScroll.offset.x);
511
+ translateAxis(boxWithoutScroll.y, -rootScroll.offset.y);
488
512
  }
489
513
  }
490
- translateAxis(boxWithoutScroll.x, scroll.x);
491
- translateAxis(boxWithoutScroll.y, scroll.y);
514
+ translateAxis(boxWithoutScroll.x, scroll.offset.x);
515
+ translateAxis(boxWithoutScroll.y, scroll.offset.y);
492
516
  }
493
517
  }
494
518
  return boxWithoutScroll;
@@ -503,8 +527,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
503
527
  node.scroll &&
504
528
  node !== node.root) {
505
529
  transformBox(withTransforms, {
506
- x: -node.scroll.x,
507
- y: -node.scroll.y,
530
+ x: -node.scroll.offset.x,
531
+ y: -node.scroll.offset.y,
508
532
  });
509
533
  }
510
534
  if (!hasTransform(node.latestValues))
@@ -738,7 +762,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
738
762
  }
739
763
  }
740
764
  setAnimationOrigin(delta, hasOnlyRelativeTargetChanged = false) {
741
- var _a;
765
+ var _a, _b;
742
766
  const snapshot = this.snapshot;
743
767
  const snapshotLatestValues = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.latestValues) || {};
744
768
  const mixedValues = { ...this.latestValues };
@@ -746,8 +770,8 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
746
770
  this.relativeTarget = this.relativeTargetOrigin = undefined;
747
771
  this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
748
772
  const relativeLayout = createBox();
749
- const isSharedLayoutAnimation = snapshot === null || snapshot === void 0 ? void 0 : snapshot.isShared;
750
- const isOnlyMember = (((_a = this.getStack()) === null || _a === void 0 ? void 0 : _a.members.length) || 0) <= 1;
773
+ const isSharedLayoutAnimation = (snapshot === null || snapshot === void 0 ? void 0 : snapshot.source) !== ((_a = this.layout) === null || _a === void 0 ? void 0 : _a.source);
774
+ const isOnlyMember = (((_b = this.getStack()) === null || _b === void 0 ? void 0 : _b.members.length) || 0) <= 1;
751
775
  const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
752
776
  !isOnlyMember &&
753
777
  this.options.crossfade === true &&
@@ -928,25 +952,30 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
928
952
  return;
929
953
  // If there's no detected rotation values, we can early return without a forced render.
930
954
  let hasRotate = false;
931
- // Keep a record of all the values we've reset
932
- const resetValues = {};
933
- // Check the rotate value of all axes and reset to 0
934
- for (let i = 0; i < transformAxes.length; i++) {
935
- const axis = transformAxes[i];
936
- const key = "rotate" + axis;
937
- // If this rotation doesn't exist as a motion value, then we don't
938
- // need to reset it
939
- if (!visualElement.getStaticValue(key)) {
940
- continue;
941
- }
955
+ /**
956
+ * An unrolled check for rotation values. Most elements don't have any rotation and
957
+ * skipping the nested loop and new object creation is 50% faster.
958
+ */
959
+ const { latestValues } = visualElement;
960
+ if (latestValues.rotate ||
961
+ latestValues.rotateX ||
962
+ latestValues.rotateY ||
963
+ latestValues.rotateZ) {
942
964
  hasRotate = true;
943
- // Record the rotation and then temporarily set it to 0
944
- resetValues[key] = visualElement.getStaticValue(key);
945
- visualElement.setStaticValue(key, 0);
946
965
  }
947
966
  // If there's no rotation values, we don't need to do any more.
948
967
  if (!hasRotate)
949
968
  return;
969
+ const resetValues = {};
970
+ // Check the rotate value of all axes and reset to 0
971
+ for (let i = 0; i < transformAxes.length; i++) {
972
+ const key = "rotate" + transformAxes[i];
973
+ // Record the rotation and then temporarily set it to 0
974
+ if (latestValues[key]) {
975
+ resetValues[key] = latestValues[key];
976
+ visualElement.setStaticValue(key, 0);
977
+ }
978
+ }
950
979
  // Force a render of this element to apply the transform with all rotations
951
980
  // set to 0.
952
981
  visualElement === null || visualElement === void 0 ? void 0 : visualElement.render();
@@ -1088,11 +1117,12 @@ function notifyLayoutUpdate(node) {
1088
1117
  node.hasListeners("didUpdate")) {
1089
1118
  const { layoutBox: layout, measuredBox: measuredLayout } = node.layout;
1090
1119
  const { animationType } = node.options;
1120
+ const isShared = snapshot.source !== node.layout.source;
1091
1121
  // TODO Maybe we want to also resize the layout snapshot so we don't trigger
1092
1122
  // animations for instance if layout="size" and an element has only changed position
1093
1123
  if (animationType === "size") {
1094
1124
  eachAxis((axis) => {
1095
- const axisSnapshot = snapshot.isShared
1125
+ const axisSnapshot = isShared
1096
1126
  ? snapshot.measuredBox[axis]
1097
1127
  : snapshot.layoutBox[axis];
1098
1128
  const length = calcLength(axisSnapshot);
@@ -1102,7 +1132,7 @@ function notifyLayoutUpdate(node) {
1102
1132
  }
1103
1133
  else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
1104
1134
  eachAxis((axis) => {
1105
- const axisSnapshot = snapshot.isShared
1135
+ const axisSnapshot = isShared
1106
1136
  ? snapshot.measuredBox[axis]
1107
1137
  : snapshot.layoutBox[axis];
1108
1138
  const length = calcLength(layout[axis]);
@@ -1112,7 +1142,7 @@ function notifyLayoutUpdate(node) {
1112
1142
  const layoutDelta = createDelta();
1113
1143
  calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
1114
1144
  const visualDelta = createDelta();
1115
- if (snapshot.isShared) {
1145
+ if (isShared) {
1116
1146
  calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
1117
1147
  }
1118
1148
  else {
@@ -1208,7 +1238,7 @@ const defaultLayoutTransition = {
1208
1238
  duration: 0.45,
1209
1239
  ease: [0.4, 0, 0.1, 1],
1210
1240
  };
1211
- function mountNodeEarly(node, id) {
1241
+ function mountNodeEarly(node, elementId) {
1212
1242
  /**
1213
1243
  * Rather than searching the DOM from document we can search the
1214
1244
  * path for the deepest mounted ancestor and search from there
@@ -1221,7 +1251,7 @@ function mountNodeEarly(node, id) {
1221
1251
  }
1222
1252
  }
1223
1253
  const searchElement = searchNode && searchNode !== node.root ? searchNode.instance : document;
1224
- const element = searchElement.querySelector(`[data-projection-id="${id}"]`);
1254
+ const element = searchElement.querySelector(`[data-projection-id="${elementId}"]`);
1225
1255
  if (element)
1226
1256
  node.mount(element, true);
1227
1257
  }
@@ -62,7 +62,6 @@ class NodeStack {
62
62
  node.snapshot = prevLead.snapshot;
63
63
  node.snapshot.latestValues =
64
64
  prevLead.animationValues || prevLead.latestValues;
65
- node.snapshot.isShared = true;
66
65
  }
67
66
  if ((_a = node.root) === null || _a === void 0 ? void 0 : _a.isUpdating) {
68
67
  node.isLayoutDirty = true;