motion 12.8.0 → 12.9.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.
package/dist/cjs/index.js CHANGED
@@ -1865,7 +1865,6 @@ class JSAnimation extends WithPromise {
1865
1865
  this.holdTime = null;
1866
1866
  }
1867
1867
  finish() {
1868
- this.notifyFinished();
1869
1868
  this.teardown();
1870
1869
  this.state = "finished";
1871
1870
  const { onComplete } = this.options;
@@ -1878,6 +1877,7 @@ class JSAnimation extends WithPromise {
1878
1877
  this.teardown();
1879
1878
  }
1880
1879
  teardown() {
1880
+ this.notifyFinished();
1881
1881
  this.state = "idle";
1882
1882
  this.stopDriver();
1883
1883
  this.startTime = this.holdTime = null;
@@ -2330,7 +2330,7 @@ class NativeAnimation extends WithPromise {
2330
2330
  this.isStopped = false;
2331
2331
  if (!options)
2332
2332
  return;
2333
- const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
2333
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
2334
2334
  this.isPseudoElement = Boolean(pseudoElement);
2335
2335
  this.allowFlatten = allowFlatten;
2336
2336
  this.options = options;
@@ -2356,8 +2356,13 @@ class NativeAnimation extends WithPromise {
2356
2356
  }
2357
2357
  this.animation.cancel();
2358
2358
  }
2359
+ onComplete?.();
2359
2360
  this.notifyFinished();
2360
2361
  };
2362
+ /**
2363
+ * TODO: In a breaking change, we should replace this with `.notifyCancel()`
2364
+ */
2365
+ this.animation.oncancel = () => this.notifyFinished();
2361
2366
  }
2362
2367
  play() {
2363
2368
  if (this.isStopped)
@@ -2682,7 +2687,7 @@ class AsyncMotionValueAnimation extends WithPromise {
2682
2687
  }
2683
2688
  onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
2684
2689
  this.keyframeResolver = undefined;
2685
- const { name, type, velocity, delay, isHandoff, onUpdate, onComplete } = options;
2690
+ const { name, type, velocity, delay, isHandoff, onUpdate } = options;
2686
2691
  this.resolvedAt = time.now();
2687
2692
  /**
2688
2693
  * If we can't animate this value with the resolved keyframes
@@ -2732,12 +2737,7 @@ class AsyncMotionValueAnimation extends WithPromise {
2732
2737
  element: resolvedOptions.motionValue.owner.current,
2733
2738
  })
2734
2739
  : new JSAnimation(resolvedOptions);
2735
- animation.finished
2736
- .then(() => {
2737
- onComplete?.();
2738
- this.notifyFinished();
2739
- })
2740
- .catch(noop);
2740
+ animation.finished.then(() => this.notifyFinished()).catch(noop);
2741
2741
  if (this.pendingTimeline) {
2742
2742
  this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
2743
2743
  this.pendingTimeline = undefined;
@@ -3350,6 +3350,54 @@ const acceleratedValues = new Set([
3350
3350
  // "background-color"
3351
3351
  ]);
3352
3352
 
3353
+ function resolveElements(elementOrSelector, scope, selectorCache) {
3354
+ if (elementOrSelector instanceof EventTarget) {
3355
+ return [elementOrSelector];
3356
+ }
3357
+ else if (typeof elementOrSelector === "string") {
3358
+ let root = document;
3359
+ if (scope) {
3360
+ root = scope.current;
3361
+ }
3362
+ const elements = selectorCache?.[elementOrSelector] ??
3363
+ root.querySelectorAll(elementOrSelector);
3364
+ return elements ? Array.from(elements) : [];
3365
+ }
3366
+ return Array.from(elementOrSelector);
3367
+ }
3368
+
3369
+ function styleEffect(subject, values) {
3370
+ const elements = resolveElements(subject);
3371
+ const subscriptions = [];
3372
+ for (let i = 0; i < elements.length; i++) {
3373
+ const element = elements[i];
3374
+ for (const key in values) {
3375
+ const value = values[key];
3376
+ /**
3377
+ * TODO: Get specific setters for combined props (like x)
3378
+ * or values with default types (like color)
3379
+ *
3380
+ * TODO: CSS variable support
3381
+ */
3382
+ const updateStyle = () => {
3383
+ element.style[key] = value.get();
3384
+ };
3385
+ const scheduleUpdate = () => frame.render(updateStyle);
3386
+ const cancel = value.on("change", scheduleUpdate);
3387
+ scheduleUpdate();
3388
+ subscriptions.push(() => {
3389
+ cancel();
3390
+ cancelFrame(updateStyle);
3391
+ });
3392
+ }
3393
+ }
3394
+ return () => {
3395
+ for (const cancel of subscriptions) {
3396
+ cancel();
3397
+ }
3398
+ };
3399
+ }
3400
+
3353
3401
  const { schedule: microtask, cancel: cancelMicrotask } =
3354
3402
  /* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
3355
3403
 
@@ -3386,22 +3434,6 @@ function setDragLock(axis) {
3386
3434
  }
3387
3435
  }
3388
3436
 
3389
- function resolveElements(elementOrSelector, scope, selectorCache) {
3390
- if (elementOrSelector instanceof EventTarget) {
3391
- return [elementOrSelector];
3392
- }
3393
- else if (typeof elementOrSelector === "string") {
3394
- let root = document;
3395
- if (scope) {
3396
- root = scope.current;
3397
- }
3398
- const elements = selectorCache?.[elementOrSelector] ??
3399
- root.querySelectorAll(elementOrSelector);
3400
- return elements ? Array.from(elements) : [];
3401
- }
3402
- return Array.from(elementOrSelector);
3403
- }
3404
-
3405
3437
  function setupGesture(elementOrSelector, options) {
3406
3438
  const elements = resolveElements(elementOrSelector);
3407
3439
  const gestureAbortController = new AbortController();
@@ -3776,7 +3808,7 @@ class MotionValue {
3776
3808
  * This will be replaced by the build step with the latest version number.
3777
3809
  * When MotionValues are provided to motion components, warn if versions are mixed.
3778
3810
  */
3779
- this.version = "12.8.0";
3811
+ this.version = "12.9.0";
3780
3812
  /**
3781
3813
  * Tracks whether this value can output a velocity. Currently this is only true
3782
3814
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -3802,12 +3834,12 @@ class MotionValue {
3802
3834
  this.prev = this.current;
3803
3835
  this.setCurrent(v);
3804
3836
  // Update update subscribers
3805
- if (this.current !== this.prev && this.events.change) {
3806
- this.events.change.notify(this.current);
3837
+ if (this.current !== this.prev) {
3838
+ this.events.change?.notify(this.current);
3807
3839
  }
3808
3840
  // Update render subscribers
3809
- if (render && this.events.renderRequest) {
3810
- this.events.renderRequest.notify(this.current);
3841
+ if (render) {
3842
+ this.events.renderRequest?.notify(this.current);
3811
3843
  }
3812
3844
  };
3813
3845
  this.hasAnimated = false;
@@ -3920,8 +3952,6 @@ class MotionValue {
3920
3952
  * @public
3921
3953
  */
3922
3954
  set(v, render = true) {
3923
- if (v === "none")
3924
- console.trace();
3925
3955
  if (!render || !this.passiveEffect) {
3926
3956
  this.updateAndNotify(v, render);
3927
3957
  }
@@ -5100,6 +5130,17 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
5100
5130
  delay,
5101
5131
  ...getValueTransition$1(transition || {}, key),
5102
5132
  };
5133
+ /**
5134
+ * If the value is already at the defined target, skip the animation.
5135
+ */
5136
+ const currentValue = value.get();
5137
+ if (currentValue !== undefined &&
5138
+ !value.isAnimating &&
5139
+ !Array.isArray(valueTarget) &&
5140
+ valueTarget === currentValue &&
5141
+ !valueTransition.velocity) {
5142
+ continue;
5143
+ }
5103
5144
  /**
5104
5145
  * If this is the first time a value is being animated, check
5105
5146
  * to see if we're handling off from an existing animation.
@@ -5239,7 +5280,7 @@ function updateMotionValuesFromProps(element, next, prev) {
5239
5280
  * and warn against mismatches.
5240
5281
  */
5241
5282
  if (process.env.NODE_ENV === "development") {
5242
- warnOnce(nextValue.version === "12.8.0", `Attempting to mix Motion versions ${nextValue.version} with 12.8.0 may not work as expected.`);
5283
+ warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
5243
5284
  }
5244
5285
  }
5245
5286
  else if (isMotionValue(prevValue)) {
@@ -5372,8 +5413,7 @@ class VisualElement {
5372
5413
  frame.render(this.render, false, true);
5373
5414
  }
5374
5415
  };
5375
- const { latestValues, renderState, onUpdate } = visualState;
5376
- this.onUpdate = onUpdate;
5416
+ const { latestValues, renderState } = visualState;
5377
5417
  this.latestValues = latestValues;
5378
5418
  this.baseTarget = { ...latestValues };
5379
5419
  this.initialValues = props.initial ? { ...latestValues } : {};
@@ -5575,7 +5615,6 @@ class VisualElement {
5575
5615
  if (this.handleChildMotionValue) {
5576
5616
  this.handleChildMotionValue();
5577
5617
  }
5578
- this.onUpdate && this.onUpdate(this);
5579
5618
  }
5580
5619
  getProps() {
5581
5620
  return this.props;
@@ -5917,25 +5956,10 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
5917
5956
  attrs[keys.array] = `${pathLength} ${pathSpacing}`;
5918
5957
  }
5919
5958
 
5920
- function calcOrigin(origin, offset, size) {
5921
- return typeof origin === "string"
5922
- ? origin
5923
- : px.transform(offset + size * origin);
5924
- }
5925
- /**
5926
- * The SVG transform origin defaults are different to CSS and is less intuitive,
5927
- * so we use the measured dimensions of the SVG to reconcile these.
5928
- */
5929
- function calcSVGTransformOrigin(dimensions, originX, originY) {
5930
- const pxOriginX = calcOrigin(originX, dimensions.x, dimensions.width);
5931
- const pxOriginY = calcOrigin(originY, dimensions.y, dimensions.height);
5932
- return `${pxOriginX} ${pxOriginY}`;
5933
- }
5934
-
5935
5959
  /**
5936
5960
  * Build SVG visual attrbutes, like cx and style.transform
5937
5961
  */
5938
- function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
5962
+ function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
5939
5963
  // This is object creation, which we try to avoid per-frame.
5940
5964
  ...latest }, isSVGTag, transformTemplate) {
5941
5965
  buildHTMLStyles(state, latest, transformTemplate);
@@ -5951,20 +5975,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
5951
5975
  }
5952
5976
  state.attrs = state.style;
5953
5977
  state.style = {};
5954
- const { attrs, style, dimensions } = state;
5978
+ const { attrs, style } = state;
5955
5979
  /**
5956
- * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
5957
- * and copy it into style.
5980
+ * However, we apply transforms as CSS transforms.
5981
+ * So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
5958
5982
  */
5959
5983
  if (attrs.transform) {
5960
- if (dimensions)
5961
- style.transform = attrs.transform;
5984
+ style.transform = attrs.transform;
5962
5985
  delete attrs.transform;
5963
5986
  }
5964
- // Parse transformOrigin
5965
- if (dimensions &&
5966
- (originX !== undefined || originY !== undefined || style.transform)) {
5967
- style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
5987
+ if (style.transform || attrs.transformOrigin) {
5988
+ style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
5989
+ delete attrs.transformOrigin;
5990
+ }
5991
+ if (style.transform) {
5992
+ /**
5993
+ * SVG's element transform-origin uses its own median as a reference.
5994
+ * Therefore, transformBox becomes a fill-box
5995
+ */
5996
+ style.transformBox = "fill-box";
5997
+ delete attrs.transformBox;
5968
5998
  }
5969
5999
  // Render attrX/attrY/attrScale as attributes
5970
6000
  if (attrX !== undefined)
@@ -6010,24 +6040,6 @@ const camelCaseAttributes = new Set([
6010
6040
 
6011
6041
  const isSVGTag = (tag) => typeof tag === "string" && tag.toLowerCase() === "svg";
6012
6042
 
6013
- function updateSVGDimensions(instance, renderState) {
6014
- try {
6015
- renderState.dimensions =
6016
- typeof instance.getBBox === "function"
6017
- ? instance.getBBox()
6018
- : instance.getBoundingClientRect();
6019
- }
6020
- catch (e) {
6021
- // Most likely trying to measure an unrendered element under Firefox
6022
- renderState.dimensions = {
6023
- x: 0,
6024
- y: 0,
6025
- width: 0,
6026
- height: 0,
6027
- };
6028
- }
6029
- }
6030
-
6031
6043
  function renderHTML(element, { style, vars }, styleProp, projection) {
6032
6044
  Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
6033
6045
  // Loop over any CSS variables and assign those.
@@ -6087,11 +6099,6 @@ class SVGVisualElement extends DOMVisualElement {
6087
6099
  this.type = "svg";
6088
6100
  this.isSVGTag = false;
6089
6101
  this.measureInstanceViewportBox = createBox;
6090
- this.updateDimensions = () => {
6091
- if (this.current && !this.renderState.dimensions) {
6092
- updateSVGDimensions(this.current, this.renderState);
6093
- }
6094
- };
6095
6102
  }
6096
6103
  getBaseTargetFromProps(props, key) {
6097
6104
  return props[key];
@@ -6107,11 +6114,6 @@ class SVGVisualElement extends DOMVisualElement {
6107
6114
  scrapeMotionValuesFromProps(props, prevProps, visualElement) {
6108
6115
  return scrapeMotionValuesFromProps(props, prevProps, visualElement);
6109
6116
  }
6110
- onBindTransform() {
6111
- if (this.current && !this.renderState.dimensions) {
6112
- frame.postRender(this.updateDimensions);
6113
- }
6114
- }
6115
6117
  build(renderState, latestValues, props) {
6116
6118
  buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
6117
6119
  }
@@ -7214,6 +7216,7 @@ exports.stagger = stagger;
7214
7216
  exports.startWaapiAnimation = startWaapiAnimation;
7215
7217
  exports.statsBuffer = statsBuffer;
7216
7218
  exports.steps = steps;
7219
+ exports.styleEffect = styleEffect;
7217
7220
  exports.supportedWaapiEasing = supportedWaapiEasing;
7218
7221
  exports.supportsBrowserAnimation = supportsBrowserAnimation;
7219
7222
  exports.supportsFlags = supportsFlags;
package/dist/cjs/mini.js CHANGED
@@ -317,7 +317,7 @@ class NativeAnimation extends WithPromise {
317
317
  this.isStopped = false;
318
318
  if (!options)
319
319
  return;
320
- const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
320
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
321
321
  this.isPseudoElement = Boolean(pseudoElement);
322
322
  this.allowFlatten = allowFlatten;
323
323
  this.options = options;
@@ -343,8 +343,13 @@ class NativeAnimation extends WithPromise {
343
343
  }
344
344
  this.animation.cancel();
345
345
  }
346
+ onComplete?.();
346
347
  this.notifyFinished();
347
348
  };
349
+ /**
350
+ * TODO: In a breaking change, we should replace this with `.notifyCancel()`
351
+ */
352
+ this.animation.oncancel = () => this.notifyFinished();
348
353
  }
349
354
  play() {
350
355
  if (this.isStopped)