framer-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.
@@ -1,11 +1,10 @@
1
1
  import { buildHTMLStyles } from '../../html/utils/build-styles.mjs';
2
2
  import { buildSVGPath } from './path.mjs';
3
- import { calcSVGTransformOrigin } from './transform-origin.mjs';
4
3
 
5
4
  /**
6
5
  * Build SVG visual attrbutes, like cx and style.transform
7
6
  */
8
- function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
7
+ function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
9
8
  // This is object creation, which we try to avoid per-frame.
10
9
  ...latest }, isSVGTag, transformTemplate) {
11
10
  buildHTMLStyles(state, latest, transformTemplate);
@@ -21,20 +20,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
21
20
  }
22
21
  state.attrs = state.style;
23
22
  state.style = {};
24
- const { attrs, style, dimensions } = state;
23
+ const { attrs, style } = state;
25
24
  /**
26
- * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
27
- * and copy it into style.
25
+ * However, we apply transforms as CSS transforms.
26
+ * So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
28
27
  */
29
28
  if (attrs.transform) {
30
- if (dimensions)
31
- style.transform = attrs.transform;
29
+ style.transform = attrs.transform;
32
30
  delete attrs.transform;
33
31
  }
34
- // Parse transformOrigin
35
- if (dimensions &&
36
- (originX !== undefined || originY !== undefined || style.transform)) {
37
- style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
32
+ if (style.transform || attrs.transformOrigin) {
33
+ style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
34
+ delete attrs.transformOrigin;
35
+ }
36
+ if (style.transform) {
37
+ /**
38
+ * SVG's element transform-origin uses its own median as a reference.
39
+ * Therefore, transformBox becomes a fill-box
40
+ */
41
+ style.transformBox = "fill-box";
42
+ delete attrs.transformBox;
38
43
  }
39
44
  // Render attrX/attrY/attrScale as attributes
40
45
  if (attrX !== undefined)
@@ -17,7 +17,7 @@ function updateMotionValuesFromProps(element, next, prev) {
17
17
  * and warn against mismatches.
18
18
  */
19
19
  if (process.env.NODE_ENV === "development") {
20
- warnOnce(nextValue.version === "12.8.0", `Attempting to mix Motion versions ${nextValue.version} with 12.8.0 may not work as expected.`);
20
+ warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
21
21
  }
22
22
  }
23
23
  else if (isMotionValue(prevValue)) {
@@ -2322,7 +2322,6 @@
2322
2322
  this.holdTime = null;
2323
2323
  }
2324
2324
  finish() {
2325
- this.notifyFinished();
2326
2325
  this.teardown();
2327
2326
  this.state = "finished";
2328
2327
  const { onComplete } = this.options;
@@ -2335,6 +2334,7 @@
2335
2334
  this.teardown();
2336
2335
  }
2337
2336
  teardown() {
2337
+ this.notifyFinished();
2338
2338
  this.state = "idle";
2339
2339
  this.stopDriver();
2340
2340
  this.startTime = this.holdTime = null;
@@ -2787,7 +2787,7 @@
2787
2787
  this.isStopped = false;
2788
2788
  if (!options)
2789
2789
  return;
2790
- const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
2790
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
2791
2791
  this.isPseudoElement = Boolean(pseudoElement);
2792
2792
  this.allowFlatten = allowFlatten;
2793
2793
  this.options = options;
@@ -2813,8 +2813,13 @@
2813
2813
  }
2814
2814
  this.animation.cancel();
2815
2815
  }
2816
+ onComplete?.();
2816
2817
  this.notifyFinished();
2817
2818
  };
2819
+ /**
2820
+ * TODO: In a breaking change, we should replace this with `.notifyCancel()`
2821
+ */
2822
+ this.animation.oncancel = () => this.notifyFinished();
2818
2823
  }
2819
2824
  play() {
2820
2825
  if (this.isStopped)
@@ -3139,7 +3144,7 @@
3139
3144
  }
3140
3145
  onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
3141
3146
  this.keyframeResolver = undefined;
3142
- const { name, type, velocity, delay, isHandoff, onUpdate, onComplete } = options;
3147
+ const { name, type, velocity, delay, isHandoff, onUpdate } = options;
3143
3148
  this.resolvedAt = time.now();
3144
3149
  /**
3145
3150
  * If we can't animate this value with the resolved keyframes
@@ -3189,12 +3194,7 @@
3189
3194
  element: resolvedOptions.motionValue.owner.current,
3190
3195
  })
3191
3196
  : new JSAnimation(resolvedOptions);
3192
- animation.finished
3193
- .then(() => {
3194
- onComplete?.();
3195
- this.notifyFinished();
3196
- })
3197
- .catch(noop);
3197
+ animation.finished.then(() => this.notifyFinished()).catch(noop);
3198
3198
  if (this.pendingTimeline) {
3199
3199
  this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
3200
3200
  this.pendingTimeline = undefined;
@@ -3807,6 +3807,54 @@
3807
3807
  // "background-color"
3808
3808
  ]);
3809
3809
 
3810
+ function resolveElements(elementOrSelector, scope, selectorCache) {
3811
+ if (elementOrSelector instanceof EventTarget) {
3812
+ return [elementOrSelector];
3813
+ }
3814
+ else if (typeof elementOrSelector === "string") {
3815
+ let root = document;
3816
+ if (scope) {
3817
+ root = scope.current;
3818
+ }
3819
+ const elements = selectorCache?.[elementOrSelector] ??
3820
+ root.querySelectorAll(elementOrSelector);
3821
+ return elements ? Array.from(elements) : [];
3822
+ }
3823
+ return Array.from(elementOrSelector);
3824
+ }
3825
+
3826
+ function styleEffect(subject, values) {
3827
+ const elements = resolveElements(subject);
3828
+ const subscriptions = [];
3829
+ for (let i = 0; i < elements.length; i++) {
3830
+ const element = elements[i];
3831
+ for (const key in values) {
3832
+ const value = values[key];
3833
+ /**
3834
+ * TODO: Get specific setters for combined props (like x)
3835
+ * or values with default types (like color)
3836
+ *
3837
+ * TODO: CSS variable support
3838
+ */
3839
+ const updateStyle = () => {
3840
+ element.style[key] = value.get();
3841
+ };
3842
+ const scheduleUpdate = () => frame.render(updateStyle);
3843
+ const cancel = value.on("change", scheduleUpdate);
3844
+ scheduleUpdate();
3845
+ subscriptions.push(() => {
3846
+ cancel();
3847
+ cancelFrame(updateStyle);
3848
+ });
3849
+ }
3850
+ }
3851
+ return () => {
3852
+ for (const cancel of subscriptions) {
3853
+ cancel();
3854
+ }
3855
+ };
3856
+ }
3857
+
3810
3858
  const { schedule: microtask, cancel: cancelMicrotask } =
3811
3859
  /* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
3812
3860
 
@@ -3843,22 +3891,6 @@
3843
3891
  }
3844
3892
  }
3845
3893
 
3846
- function resolveElements(elementOrSelector, scope, selectorCache) {
3847
- if (elementOrSelector instanceof EventTarget) {
3848
- return [elementOrSelector];
3849
- }
3850
- else if (typeof elementOrSelector === "string") {
3851
- let root = document;
3852
- if (scope) {
3853
- root = scope.current;
3854
- }
3855
- const elements = selectorCache?.[elementOrSelector] ??
3856
- root.querySelectorAll(elementOrSelector);
3857
- return elements ? Array.from(elements) : [];
3858
- }
3859
- return Array.from(elementOrSelector);
3860
- }
3861
-
3862
3894
  function setupGesture(elementOrSelector, options) {
3863
3895
  const elements = resolveElements(elementOrSelector);
3864
3896
  const gestureAbortController = new AbortController();
@@ -4233,7 +4265,7 @@
4233
4265
  * This will be replaced by the build step with the latest version number.
4234
4266
  * When MotionValues are provided to motion components, warn if versions are mixed.
4235
4267
  */
4236
- this.version = "12.8.0";
4268
+ this.version = "12.9.0";
4237
4269
  /**
4238
4270
  * Tracks whether this value can output a velocity. Currently this is only true
4239
4271
  * if the value is numerical, but we might be able to widen the scope here and support
@@ -4259,12 +4291,12 @@
4259
4291
  this.prev = this.current;
4260
4292
  this.setCurrent(v);
4261
4293
  // Update update subscribers
4262
- if (this.current !== this.prev && this.events.change) {
4263
- this.events.change.notify(this.current);
4294
+ if (this.current !== this.prev) {
4295
+ this.events.change?.notify(this.current);
4264
4296
  }
4265
4297
  // Update render subscribers
4266
- if (render && this.events.renderRequest) {
4267
- this.events.renderRequest.notify(this.current);
4298
+ if (render) {
4299
+ this.events.renderRequest?.notify(this.current);
4268
4300
  }
4269
4301
  };
4270
4302
  this.hasAnimated = false;
@@ -4377,8 +4409,6 @@
4377
4409
  * @public
4378
4410
  */
4379
4411
  set(v, render = true) {
4380
- if (v === "none")
4381
- console.trace();
4382
4412
  if (!render || !this.passiveEffect) {
4383
4413
  this.updateAndNotify(v, render);
4384
4414
  }
@@ -7578,7 +7608,7 @@
7578
7608
  * and warn against mismatches.
7579
7609
  */
7580
7610
  {
7581
- warnOnce(nextValue.version === "12.8.0", `Attempting to mix Motion versions ${nextValue.version} with 12.8.0 may not work as expected.`);
7611
+ warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
7582
7612
  }
7583
7613
  }
7584
7614
  else if (isMotionValue(prevValue)) {
@@ -7746,8 +7776,7 @@
7746
7776
  frame.render(this.render, false, true);
7747
7777
  }
7748
7778
  };
7749
- const { latestValues, renderState, onUpdate } = visualState;
7750
- this.onUpdate = onUpdate;
7779
+ const { latestValues, renderState } = visualState;
7751
7780
  this.latestValues = latestValues;
7752
7781
  this.baseTarget = { ...latestValues };
7753
7782
  this.initialValues = props.initial ? { ...latestValues } : {};
@@ -7949,7 +7978,6 @@
7949
7978
  if (this.handleChildMotionValue) {
7950
7979
  this.handleChildMotionValue();
7951
7980
  }
7952
- this.onUpdate && this.onUpdate(this);
7953
7981
  }
7954
7982
  getProps() {
7955
7983
  return this.props;
@@ -8719,6 +8747,17 @@
8719
8747
  delay,
8720
8748
  ...getValueTransition$1(transition || {}, key),
8721
8749
  };
8750
+ /**
8751
+ * If the value is already at the defined target, skip the animation.
8752
+ */
8753
+ const currentValue = value.get();
8754
+ if (currentValue !== undefined &&
8755
+ !value.isAnimating &&
8756
+ !Array.isArray(valueTarget) &&
8757
+ valueTarget === currentValue &&
8758
+ !valueTransition.velocity) {
8759
+ continue;
8760
+ }
8722
8761
  /**
8723
8762
  * If this is the first time a value is being animated, check
8724
8763
  * to see if we're handling off from an existing animation.
@@ -9541,7 +9580,7 @@
9541
9580
  * Calculate a transform origin relative to the source axis, between 0-1, that results
9542
9581
  * in an asthetically pleasing scale/transform needed to project from source to target.
9543
9582
  */
9544
- function calcOrigin$1(source, target) {
9583
+ function calcOrigin(source, target) {
9545
9584
  let origin = 0.5;
9546
9585
  const sourceLength = calcLength(source);
9547
9586
  const targetLength = calcLength(target);
@@ -9935,7 +9974,7 @@
9935
9974
  const axisValue = this.getAxisMotionValue(axis);
9936
9975
  if (axisValue && this.constraints !== false) {
9937
9976
  const latest = axisValue.get();
9938
- boxProgress[axis] = calcOrigin$1({ min: latest, max: latest }, this.constraints[axis]);
9977
+ boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]);
9939
9978
  }
9940
9979
  });
9941
9980
  /**
@@ -10886,25 +10925,10 @@
10886
10925
  attrs[keys.array] = `${pathLength} ${pathSpacing}`;
10887
10926
  }
10888
10927
 
10889
- function calcOrigin(origin, offset, size) {
10890
- return typeof origin === "string"
10891
- ? origin
10892
- : px.transform(offset + size * origin);
10893
- }
10894
- /**
10895
- * The SVG transform origin defaults are different to CSS and is less intuitive,
10896
- * so we use the measured dimensions of the SVG to reconcile these.
10897
- */
10898
- function calcSVGTransformOrigin(dimensions, originX, originY) {
10899
- const pxOriginX = calcOrigin(originX, dimensions.x, dimensions.width);
10900
- const pxOriginY = calcOrigin(originY, dimensions.y, dimensions.height);
10901
- return `${pxOriginX} ${pxOriginY}`;
10902
- }
10903
-
10904
10928
  /**
10905
10929
  * Build SVG visual attrbutes, like cx and style.transform
10906
10930
  */
10907
- function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathLength, pathSpacing = 1, pathOffset = 0,
10931
+ function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
10908
10932
  // This is object creation, which we try to avoid per-frame.
10909
10933
  ...latest }, isSVGTag, transformTemplate) {
10910
10934
  buildHTMLStyles(state, latest, transformTemplate);
@@ -10920,20 +10944,26 @@
10920
10944
  }
10921
10945
  state.attrs = state.style;
10922
10946
  state.style = {};
10923
- const { attrs, style, dimensions } = state;
10947
+ const { attrs, style } = state;
10924
10948
  /**
10925
- * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
10926
- * and copy it into style.
10949
+ * However, we apply transforms as CSS transforms.
10950
+ * So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
10927
10951
  */
10928
10952
  if (attrs.transform) {
10929
- if (dimensions)
10930
- style.transform = attrs.transform;
10953
+ style.transform = attrs.transform;
10931
10954
  delete attrs.transform;
10932
10955
  }
10933
- // Parse transformOrigin
10934
- if (dimensions &&
10935
- (originX !== undefined || originY !== undefined || style.transform)) {
10936
- style.transformOrigin = calcSVGTransformOrigin(dimensions, originX !== undefined ? originX : 0.5, originY !== undefined ? originY : 0.5);
10956
+ if (style.transform || attrs.transformOrigin) {
10957
+ style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
10958
+ delete attrs.transformOrigin;
10959
+ }
10960
+ if (style.transform) {
10961
+ /**
10962
+ * SVG's element transform-origin uses its own median as a reference.
10963
+ * Therefore, transformBox becomes a fill-box
10964
+ */
10965
+ style.transformBox = "fill-box";
10966
+ delete attrs.transformBox;
10937
10967
  }
10938
10968
  // Render attrX/attrY/attrScale as attributes
10939
10969
  if (attrX !== undefined)
@@ -10997,20 +11027,11 @@
10997
11027
  return useRender;
10998
11028
  }
10999
11029
 
11000
- function makeState({ scrapeMotionValuesFromProps, createRenderState, onUpdate, }, props, context, presenceContext) {
11030
+ function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) {
11001
11031
  const state = {
11002
11032
  latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
11003
11033
  renderState: createRenderState(),
11004
11034
  };
11005
- if (onUpdate) {
11006
- /**
11007
- * onMount works without the VisualElement because it could be
11008
- * called before the VisualElement payload has been hydrated.
11009
- * (e.g. if someone is using m components <m.circle />)
11010
- */
11011
- state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
11012
- state.onUpdate = (visualElement) => onUpdate(visualElement);
11013
- }
11014
11035
  return state;
11015
11036
  }
11016
11037
  const makeUseVisualState = (config) => (props, isStatic) => {
@@ -11082,22 +11103,41 @@
11082
11103
  }),
11083
11104
  };
11084
11105
 
11085
- function updateSVGDimensions(instance, renderState) {
11086
- try {
11087
- renderState.dimensions =
11088
- typeof instance.getBBox === "function"
11089
- ? instance.getBBox()
11090
- : instance.getBoundingClientRect();
11106
+ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
11107
+ const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
11108
+ for (const key in props) {
11109
+ if (isMotionValue(props[key]) ||
11110
+ isMotionValue(prevProps[key])) {
11111
+ const targetKey = transformPropOrder.indexOf(key) !== -1
11112
+ ? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
11113
+ : key;
11114
+ newValues[targetKey] = props[key];
11115
+ }
11091
11116
  }
11092
- catch (e) {
11093
- // Most likely trying to measure an unrendered element under Firefox
11094
- renderState.dimensions = {
11095
- x: 0,
11096
- y: 0,
11097
- width: 0,
11098
- height: 0,
11117
+ return newValues;
11118
+ }
11119
+
11120
+ const svgMotionConfig = {
11121
+ useVisualState: makeUseVisualState({
11122
+ scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
11123
+ createRenderState: createSvgRenderState,
11124
+ }),
11125
+ };
11126
+
11127
+ function createMotionComponentFactory(preloadedFeatures, createVisualElement) {
11128
+ return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) {
11129
+ const baseConfig = isSVGComponent(Component)
11130
+ ? svgMotionConfig
11131
+ : htmlMotionConfig;
11132
+ const config = {
11133
+ ...baseConfig,
11134
+ preloadedFeatures,
11135
+ useRender: createUseRender(forwardMotionProps),
11136
+ createVisualElement,
11137
+ Component,
11099
11138
  };
11100
- }
11139
+ return createRendererMotionComponent(config);
11140
+ };
11101
11141
  }
11102
11142
 
11103
11143
  /**
@@ -11136,93 +11176,12 @@
11136
11176
  }
11137
11177
  }
11138
11178
 
11139
- function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
11140
- const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
11141
- for (const key in props) {
11142
- if (isMotionValue(props[key]) ||
11143
- isMotionValue(prevProps[key])) {
11144
- const targetKey = transformPropOrder.indexOf(key) !== -1
11145
- ? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
11146
- : key;
11147
- newValues[targetKey] = props[key];
11148
- }
11149
- }
11150
- return newValues;
11151
- }
11152
-
11153
- const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
11154
- const svgMotionConfig = {
11155
- useVisualState: makeUseVisualState({
11156
- scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
11157
- createRenderState: createSvgRenderState,
11158
- onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
11159
- if (!current)
11160
- return;
11161
- let hasTransform = !!props.drag;
11162
- if (!hasTransform) {
11163
- for (const key in latestValues) {
11164
- if (transformProps.has(key)) {
11165
- hasTransform = true;
11166
- break;
11167
- }
11168
- }
11169
- }
11170
- if (!hasTransform)
11171
- return;
11172
- let needsMeasure = !prevProps;
11173
- if (prevProps) {
11174
- /**
11175
- * Check the layout props for changes, if any are found we need to
11176
- * measure the element again.
11177
- */
11178
- for (let i = 0; i < layoutProps.length; i++) {
11179
- const key = layoutProps[i];
11180
- if (props[key] !==
11181
- prevProps[key]) {
11182
- needsMeasure = true;
11183
- }
11184
- }
11185
- }
11186
- if (!needsMeasure)
11187
- return;
11188
- frame.read(() => {
11189
- updateSVGDimensions(current, renderState);
11190
- frame.render(() => {
11191
- buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
11192
- renderSVG(current, renderState);
11193
- });
11194
- });
11195
- },
11196
- }),
11197
- };
11198
-
11199
- function createMotionComponentFactory(preloadedFeatures, createVisualElement) {
11200
- return function createMotionComponent(Component, { forwardMotionProps } = { forwardMotionProps: false }) {
11201
- const baseConfig = isSVGComponent(Component)
11202
- ? svgMotionConfig
11203
- : htmlMotionConfig;
11204
- const config = {
11205
- ...baseConfig,
11206
- preloadedFeatures,
11207
- useRender: createUseRender(forwardMotionProps),
11208
- createVisualElement,
11209
- Component,
11210
- };
11211
- return createRendererMotionComponent(config);
11212
- };
11213
- }
11214
-
11215
11179
  class SVGVisualElement extends DOMVisualElement {
11216
11180
  constructor() {
11217
11181
  super(...arguments);
11218
11182
  this.type = "svg";
11219
11183
  this.isSVGTag = false;
11220
11184
  this.measureInstanceViewportBox = createBox;
11221
- this.updateDimensions = () => {
11222
- if (this.current && !this.renderState.dimensions) {
11223
- updateSVGDimensions(this.current, this.renderState);
11224
- }
11225
- };
11226
11185
  }
11227
11186
  getBaseTargetFromProps(props, key) {
11228
11187
  return props[key];
@@ -11238,11 +11197,6 @@
11238
11197
  scrapeMotionValuesFromProps(props, prevProps, visualElement) {
11239
11198
  return scrapeMotionValuesFromProps(props, prevProps, visualElement);
11240
11199
  }
11241
- onBindTransform() {
11242
- if (this.current && !this.renderState.dimensions) {
11243
- frame.postRender(this.updateDimensions);
11244
- }
11245
- }
11246
11200
  build(renderState, latestValues, props) {
11247
11201
  buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
11248
11202
  }
@@ -13862,6 +13816,7 @@
13862
13816
  exports.startWaapiAnimation = startWaapiAnimation;
13863
13817
  exports.statsBuffer = statsBuffer;
13864
13818
  exports.steps = steps;
13819
+ exports.styleEffect = styleEffect;
13865
13820
  exports.supportedWaapiEasing = supportedWaapiEasing;
13866
13821
  exports.supportsBrowserAnimation = supportsBrowserAnimation;
13867
13822
  exports.supportsFlags = supportsFlags;