motion 14.5.0 → 14.6.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.
@@ -247,8 +247,8 @@
247
247
  : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
248
248
 
249
249
  const circIn = (p) => 1 - Math.sin(Math.acos(p));
250
- const circOut = reverseEasing(circIn);
251
- const circInOut = mirrorEasing(circIn);
250
+ const circOut = /* @__PURE__ */ reverseEasing(circIn);
251
+ const circInOut = /* @__PURE__ */ mirrorEasing(circIn);
252
252
 
253
253
  const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
254
254
  const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
@@ -928,6 +928,11 @@
928
928
  */
929
929
  let isProcessing = false;
930
930
  let flushNextFrame = false;
931
+ /**
932
+ * The callback currently running. If it schedules itself with `immediate`
933
+ * it's left in place, so as before it doesn't run again this step.
934
+ */
935
+ let running;
931
936
  /**
932
937
  * A set of processes which were marked keepAlive when scheduled.
933
938
  * A keepAlive process is always also held by a frame queue until
@@ -945,7 +950,7 @@
945
950
  nextFrame.add(callback);
946
951
  runNextFrame();
947
952
  }
948
- callback(latestFrameData);
953
+ (running = callback)(latestFrameData);
949
954
  }
950
955
  const step = {
951
956
  /**
@@ -956,6 +961,14 @@
956
961
  const queue = addToCurrentFrame ? thisFrame : nextFrame;
957
962
  if (keepAlive)
958
963
  toKeepAlive.add(callback);
964
+ /**
965
+ * A callback scheduled with `immediate` during processing may
966
+ * already have run this step (for instance, a value it reads was
967
+ * changed by a later callback). Re-adding moves it to the end of
968
+ * the queue, so it runs again. The running callback is excluded,
969
+ * so one that re-schedules itself can't loop forever.
970
+ */
971
+ addToCurrentFrame && callback !== running && queue.delete(callback);
959
972
  queue.add(callback);
960
973
  return callback;
961
974
  },
@@ -990,6 +1003,7 @@
990
1003
  // Clear the frame so no callbacks remain. This is to avoid
991
1004
  // memory leaks should this render step not run for a while.
992
1005
  thisFrame.clear();
1006
+ running = undefined;
993
1007
  isProcessing = false;
994
1008
  if (flushNextFrame) {
995
1009
  flushNextFrame = false;
@@ -1336,7 +1350,7 @@
1336
1350
  }
1337
1351
  return springOptions;
1338
1352
  }
1339
- function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
1353
+ function springGenerator(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
1340
1354
  const options = typeof optionsOrVisualDuration !== "object"
1341
1355
  ? {
1342
1356
  visualDuration: optionsOrVisualDuration,
@@ -1521,13 +1535,15 @@
1521
1535
  };
1522
1536
  return generator;
1523
1537
  }
1524
- spring.applyToOptions = (options) => {
1525
- const generatorOptions = createGeneratorEasing(options, 100, spring);
1526
- options.ease = generatorOptions.ease;
1527
- options.duration = secondsToMilliseconds(generatorOptions.duration);
1528
- options.type = "keyframes";
1529
- return options;
1530
- };
1538
+ const spring = /* @__PURE__ */ Object.assign(springGenerator, {
1539
+ applyToOptions: (options) => {
1540
+ const generatorOptions = createGeneratorEasing(options, 100, springGenerator);
1541
+ options.ease = generatorOptions.ease;
1542
+ options.duration = secondsToMilliseconds(generatorOptions.duration);
1543
+ options.type = "keyframes";
1544
+ return options;
1545
+ },
1546
+ });
1531
1547
 
1532
1548
  function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
1533
1549
  const origin = keyframes[0];
@@ -2609,7 +2625,7 @@
2609
2625
 
2610
2626
  const isNumOrPxType = (v) => v === number || v === px;
2611
2627
  const transformKeys = new Set(["x", "y", "z"]);
2612
- const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
2628
+ const nonTranslationalTransformKeys = /* @__PURE__ */ transformPropOrder.filter((key) => !transformKeys.has(key));
2613
2629
  /**
2614
2630
  * Reset any bounding box-changing transforms so the element can be
2615
2631
  * measured. Returns the values to restore. Values already at their
@@ -2651,6 +2667,8 @@
2651
2667
  ? size
2652
2668
  : size - parseFloat(paddingStart) - parseFloat(paddingEnd);
2653
2669
  }
2670
+ const transformX = ({ transform }) => parseValueFromTransform(transform, "x");
2671
+ const transformY = ({ transform }) => parseValueFromTransform(transform, "y");
2654
2672
  const positionalValues = {
2655
2673
  // Dimensions
2656
2674
  width: ({ width, paddingLeft = "0", paddingRight = "0", boxSizing }, measureBox) => measureDimension(width, measureBox, "x", paddingLeft, paddingRight, boxSizing),
@@ -2665,13 +2683,12 @@
2665
2683
  const { x } = measureBox();
2666
2684
  return parseFloat(left) + (x.max - x.min);
2667
2685
  },
2668
- // Transform
2669
- x: ({ transform }) => parseValueFromTransform(transform, "x"),
2670
- y: ({ transform }) => parseValueFromTransform(transform, "y"),
2686
+ // Transform, including translate longform names
2687
+ x: transformX,
2688
+ y: transformY,
2689
+ translateX: transformX,
2690
+ translateY: transformY,
2671
2691
  };
2672
- // Alias translate longform names
2673
- positionalValues.translateX = positionalValues.x;
2674
- positionalValues.translateY = positionalValues.y;
2675
2692
 
2676
2693
  const toResolve = new Set();
2677
2694
  let isScheduled = false;
@@ -3025,18 +3042,24 @@
3025
3042
  if (this.isStopped)
3026
3043
  return;
3027
3044
  this.isStopped = true;
3028
- const { state } = this;
3029
- if (state === "idle" || state === "finished") {
3045
+ /**
3046
+ * Before its finish event, a finished animation (an instant one is
3047
+ * as soon as it starts) is still showing its end, so it's stopped
3048
+ * like a running one. Otherwise that event would apply its end
3049
+ * over whatever animates the value next.
3050
+ */
3051
+ if (this.state === "idle" || this.finishedTime !== null)
3030
3052
  return;
3031
- }
3032
3053
  if (this.updateMotionValue) {
3033
3054
  this.updateMotionValue();
3034
3055
  }
3035
3056
  else {
3036
3057
  this.commitStyles();
3037
3058
  }
3038
- if (!this.isPseudoElement)
3059
+ if (!this.isPseudoElement) {
3060
+ this.animation.onfinish = null;
3039
3061
  this.cancel();
3062
+ }
3040
3063
  }
3041
3064
  /**
3042
3065
  * WAAPI doesn't natively have any interruption capabilities.
@@ -3345,7 +3368,13 @@
3345
3368
  "borderBottomColor",
3346
3369
  "borderLeftColor",
3347
3370
  ]);
3348
- const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
3371
+ /**
3372
+ * WAAPI runs on the browser's clock, so on a manual clock every animation
3373
+ * runs in JS where processFrame can drive it. Set the flag before starting
3374
+ * animations: running WAAPI animations stay on the browser's clock.
3375
+ */
3376
+ const supportsWaapi = () => !MotionGlobalConfig.useManualTiming &&
3377
+ Object.hasOwnProperty.call(Element.prototype, "animate");
3349
3378
  function supportsBrowserAnimation(options) {
3350
3379
  const { motionValue, name, repeatDelay, repeatType, damping, type, keyframes, } = options;
3351
3380
  /**
@@ -5536,12 +5565,8 @@
5536
5565
  }
5537
5566
  }
5538
5567
 
5539
- function camelToDash(str) {
5540
- return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
5541
- }
5542
-
5543
5568
  const optimizedAppearDataId = "framerAppearId";
5544
- const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
5569
+ const optimizedAppearDataAttribute = "data-framer-appear-id";
5545
5570
 
5546
5571
  function getOptimisedAppearId(visualElement) {
5547
5572
  return visualElement.props[optimizedAppearDataAttribute];
@@ -6469,6 +6494,10 @@
6469
6494
  const styleEffect =
6470
6495
  /*@__PURE__*/ createSelectorEffect(styleSubjectEffect);
6471
6496
 
6497
+ function camelToDash(str) {
6498
+ return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
6499
+ }
6500
+
6472
6501
  function buildHTMLStyles(state, latestValues, transformTemplate) {
6473
6502
  const { style, vars, transformOrigin } = state;
6474
6503
  // Track whether we encounter any transform or transformOrigin values.
@@ -7044,6 +7073,41 @@
7044
7073
  const { schedule: microtask, cancel: cancelMicrotask } =
7045
7074
  /* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
7046
7075
 
7076
+ /**
7077
+ * Process a frame now, for driving Motion from a loop other than
7078
+ * window.requestAnimationFrame, like an XRSession's during an immersive
7079
+ * WebXR session. Wherever window.requestAnimationFrame does fire, Motion
7080
+ * keeps processing frames from it too.
7081
+ *
7082
+ * `timestamp` defaults to now. For a timeline you control, set
7083
+ * `MotionGlobalConfig.useManualTiming = true` and pass each frame's time in
7084
+ * milliseconds.
7085
+ *
7086
+ * Animations Motion hands to the browser (WAAPI), like `opacity` or
7087
+ * `transform` on an HTML or SVG element, follow the browser's clock. With
7088
+ * `useManualTiming` on, Motion runs those in JS instead, except for
7089
+ * `motion/mini`, view transitions and optimized appear animations, and
7090
+ * colours only the browser can mix, like `oklch()`, which jump to their end.
7091
+ *
7092
+ * Ignored when called inside a frame.
7093
+ */
7094
+ const processFrame = (timestamp = time.now()) => {
7095
+ if (frameData.isProcessing)
7096
+ return;
7097
+ const delta = timestamp - frameData.timestamp;
7098
+ // Clamp like the requestAnimationFrame loop, unless the timeline is ours
7099
+ frameData.delta = MotionGlobalConfig.useManualTiming
7100
+ ? delta
7101
+ : Math.max(Math.min(delta, 40), 1);
7102
+ time.set((frameData.timestamp = timestamp));
7103
+ frameData.isProcessing = true;
7104
+ // frameSteps' keys are in stepsOrder
7105
+ for (const step in frameSteps) {
7106
+ frameSteps[step].process(frameData);
7107
+ }
7108
+ frameData.isProcessing = false;
7109
+ };
7110
+
7047
7111
  function animateSingleValue(value, keyframes, options) {
7048
7112
  const motionValue$1 = isMotionValue(value) ? value : motionValue(value);
7049
7113
  motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options));
@@ -7503,10 +7567,9 @@
7503
7567
  };
7504
7568
 
7505
7569
  const defaultScaleCorrectors = {
7506
- borderRadius: {
7507
- ...correctBorderRadius,
7508
- applyTo: [...cornerRadiusProps],
7509
- },
7570
+ borderRadius: /* @__PURE__ */ Object.assign({}, correctBorderRadius, {
7571
+ applyTo: /* @__PURE__ */ cornerRadiusProps.slice(),
7572
+ }),
7510
7573
  borderTopLeftRadius: correctBorderRadius,
7511
7574
  borderTopRightRadius: correctBorderRadius,
7512
7575
  borderBottomLeftRadius: correctBorderRadius,
@@ -7611,14 +7674,6 @@
7611
7674
  hasEverUpdated: false,
7612
7675
  };
7613
7676
 
7614
- /**
7615
- * Correctors registered via addScaleCorrector before projection loaded
7616
- * (e.g. with an async LazyMotion bundle) take precedence over the defaults.
7617
- */
7618
- Object.assign(scaleCorrectors, {
7619
- ...defaultScaleCorrectors,
7620
- ...scaleCorrectors,
7621
- });
7622
7677
  const metrics = {
7623
7678
  nodes: 0,
7624
7679
  calculatedTargetDeltas: 0,
@@ -7661,6 +7716,16 @@
7661
7716
  }
7662
7717
  }
7663
7718
  function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
7719
+ /**
7720
+ * Correctors registered via addScaleCorrector before projection loaded
7721
+ * (e.g. with an async LazyMotion bundle) take precedence over the defaults.
7722
+ * Applied here rather than at module scope so bundlers can drop projection
7723
+ * when it's unused.
7724
+ */
7725
+ Object.assign(scaleCorrectors, {
7726
+ ...defaultScaleCorrectors,
7727
+ ...scaleCorrectors,
7728
+ });
7664
7729
  return class ProjectionNode {
7665
7730
  constructor(latestValues = {}, parent = defaultParent?.()) {
7666
7731
  /**
@@ -8317,7 +8382,7 @@
8317
8382
  }
8318
8383
  return boxWithoutScroll;
8319
8384
  }
8320
- applyTransform(box, transformOnly = false, output) {
8385
+ applyTransform(box, transformOnly = false, output, skipOwnTransform) {
8321
8386
  const withTransforms = output || createBox();
8322
8387
  copyBoxInto(withTransforms, box);
8323
8388
  for (let i = 0; i < this.path.length; i++) {
@@ -8333,7 +8398,7 @@
8333
8398
  continue;
8334
8399
  transformBox(withTransforms, node.latestValues, node.layout?.layoutBox);
8335
8400
  }
8336
- if (hasTransform(this.latestValues)) {
8401
+ if (!skipOwnTransform && hasTransform(this.latestValues)) {
8337
8402
  transformBox(withTransforms, this.latestValues, this.layout?.layoutBox);
8338
8403
  }
8339
8404
  return withTransforms;
@@ -8468,7 +8533,11 @@
8468
8533
  }
8469
8534
  else if (this.targetDelta) {
8470
8535
  if (Boolean(this.resumingFrom)) {
8471
- this.applyTransform(this.layout.layoutBox, false, this.target);
8536
+ /**
8537
+ * Own transforms are applied later, in applyTransformsToTarget,
8538
+ * so only apply ancestor transforms here or they'd be doubled.
8539
+ */
8540
+ this.applyTransform(this.layout.layoutBox, false, this.target, true);
8472
8541
  }
8473
8542
  else {
8474
8543
  copyBoxInto(this.target, this.layout.layoutBox);
@@ -9237,10 +9306,13 @@
9237
9306
  * A dragged node doesn't layout animate, so it jumps to its new layout. Shift
9238
9307
  * its descendants' snapshots by the same amount, so they only animate layout
9239
9308
  * changes relative to it, rather than lagging behind.
9309
+ *
9310
+ * If its drag ended since the snapshot (e.g. drag disabled from onDrag), it
9311
+ * layout animates again, so its descendants mustn't follow its jump.
9240
9312
  */
9241
9313
  function followDraggedAncestor(node) {
9242
9314
  const { snapshot, dragAncestor } = node;
9243
- const to = dragAncestor?.layout;
9315
+ const to = dragAncestor?.isAnimationBlocked && dragAncestor.layout;
9244
9316
  if (!snapshot || !to || node.resumeFrom || node.isAnimationBlocked)
9245
9317
  return;
9246
9318
  const from = dragAncestor.dragSnapshot;
@@ -9354,11 +9426,16 @@
9354
9426
  return () => target.removeEventListener(eventName, handler, options);
9355
9427
  }
9356
9428
 
9357
- const DocumentProjectionNode = createProjectionNode({
9429
+ const DocumentProjectionNode =
9430
+ /* @__PURE__ */ createProjectionNode({
9358
9431
  attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
9359
9432
  measureScroll: () => ({
9360
- x: document.documentElement.scrollLeft || document.body?.scrollLeft || 0,
9361
- y: document.documentElement.scrollTop || document.body?.scrollTop || 0,
9433
+ x: document.documentElement.scrollLeft ||
9434
+ document.body?.scrollLeft ||
9435
+ 0,
9436
+ y: document.documentElement.scrollTop ||
9437
+ document.body?.scrollTop ||
9438
+ 0,
9362
9439
  }),
9363
9440
  checkIsScrollRoot: () => true,
9364
9441
  });
@@ -9366,7 +9443,9 @@
9366
9443
  const rootProjectionNode = {
9367
9444
  current: undefined,
9368
9445
  };
9369
- const HTMLProjectionNode = createProjectionNode({
9446
+
9447
+ const HTMLProjectionNode =
9448
+ /* @__PURE__ */ createProjectionNode({
9370
9449
  measureScroll: (instance) => ({
9371
9450
  x: instance.scrollLeft,
9372
9451
  y: instance.scrollTop,
@@ -9410,7 +9489,7 @@
9410
9489
  "whileDrag",
9411
9490
  "exit",
9412
9491
  ];
9413
- const variantProps = ["initial", ...variantPriorityOrder];
9492
+ const variantProps = /* @__PURE__ */ ["initial"].concat(variantPriorityOrder);
9414
9493
 
9415
9494
  function isControllingVariants(props) {
9416
9495
  if (isAnimationControls(props.animate))
@@ -11263,7 +11342,11 @@
11263
11342
  element: this.visualElement.current,
11264
11343
  });
11265
11344
  }
11266
- stop(event, panInfo) {
11345
+ /**
11346
+ * @param dragAxes - The axes to animate on release, if not the `drag`
11347
+ * prop's, e.g. when drag was disabled mid-gesture.
11348
+ */
11349
+ stop(event, panInfo, dragAxes) {
11267
11350
  const finalEvent = event || this.latestPointerEvent;
11268
11351
  const finalPanInfo = panInfo || this.latestPanInfo;
11269
11352
  const isDragging = this.isDragging;
@@ -11271,7 +11354,7 @@
11271
11354
  if (!isDragging || !finalPanInfo || !finalEvent)
11272
11355
  return;
11273
11356
  const { velocity } = finalPanInfo;
11274
- this.startAnimation(velocity);
11357
+ this.startAnimation(velocity, dragAxes);
11275
11358
  const { onDragEnd } = this.getProps();
11276
11359
  if (onDragEnd) {
11277
11360
  frame.postRender(() => onDragEnd(finalEvent, finalPanInfo));
@@ -11392,11 +11475,11 @@
11392
11475
  }
11393
11476
  return measuredConstraints;
11394
11477
  }
11395
- startAnimation(velocity) {
11478
+ startAnimation(velocity, dragAxes) {
11396
11479
  const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps();
11397
11480
  const constraints = this.constraints || {};
11398
11481
  const momentumAnimations = eachAxis((axis) => {
11399
- if (!shouldDrag(axis, drag, this.currentDirection)) {
11482
+ if (!shouldDrag(axis, dragAxes || drag, this.currentDirection)) {
11400
11483
  return;
11401
11484
  }
11402
11485
  let transition = (constraints && constraints[axis]) || {};
@@ -11599,9 +11682,15 @@
11599
11682
  const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
11600
11683
  if (this.isDragging && hasLayoutChanged) {
11601
11684
  eachAxis((axis) => {
11602
- const motionValue = this.getAxisMotionValue(axis);
11603
- if (!motionValue)
11685
+ /**
11686
+ * Only compensate axes this gesture drags. A
11687
+ * non-drag axis follows its layout instead, as
11688
+ * nothing would ever move it back (e.g. snapping
11689
+ * to origin only animates drag axes).
11690
+ */
11691
+ if (!shouldDrag(axis, this.getProps().drag, this.currentDirection))
11604
11692
  return;
11693
+ const motionValue = this.getAxisMotionValue(axis);
11605
11694
  this.originPoint[axis] += delta[axis].translate;
11606
11695
  motionValue.set(motionValue.get() + delta[axis].translate);
11607
11696
  });
@@ -14076,8 +14165,7 @@
14076
14165
  const valueHasChanged = Array.isArray(next) && Array.isArray(prev)
14077
14166
  ? !shallowCompare(next, prev) || variantDidChange
14078
14167
  : next !== prev;
14079
- if (valueHasChanged &&
14080
- (next === undefined || next === null)) {
14168
+ if (valueHasChanged && (next === undefined || next === null)) {
14081
14169
  removedKeys.add(key);
14082
14170
  }
14083
14171
  else if (valueHasChanged ||
@@ -14864,7 +14952,8 @@
14864
14952
  }
14865
14953
  return scopedAnimate;
14866
14954
  }
14867
- const animate = Object.assign(createScopedAnimate(), {
14955
+ const animate = /* @__PURE__ */ Object.assign(
14956
+ /* @__PURE__ */ createScopedAnimate(), {
14868
14957
  /**
14869
14958
  * Register an effect so `animate()` can animate the subjects it
14870
14959
  * claims, for instance `animate.addEffect(threeEffect)`. The most
@@ -15890,6 +15979,7 @@
15890
15979
  exports.positionalKeys = positionalKeys;
15891
15980
  exports.prefersReducedMotion = prefersReducedMotion;
15892
15981
  exports.press = press;
15982
+ exports.processFrame = processFrame;
15893
15983
  exports.progress = progress;
15894
15984
  exports.progressPercentage = progressPercentage;
15895
15985
  exports.propEffect = propEffect;