framer-motion 5.0.0 → 5.1.0-rc.1

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.
Files changed (33) hide show
  1. package/README.md +10 -9
  2. package/dist/es/animation/utils/transitions.mjs +2 -2
  3. package/dist/es/components/AnimatePresence/index.mjs +8 -1
  4. package/dist/es/context/MotionContext/create.mjs +2 -11
  5. package/dist/es/gestures/drag/VisualElementDragControls.mjs +15 -1
  6. package/dist/es/motion/index.mjs +1 -5
  7. package/dist/es/motion/utils/use-visual-element.mjs +0 -4
  8. package/dist/es/render/dom/utils/unit-conversion.mjs +33 -26
  9. package/dist/es/render/svg/config-motion.mjs +0 -6
  10. package/dist/es/render/svg/utils/build-attrs.mjs +4 -4
  11. package/dist/es/render/svg/utils/camel-case-attrs.mjs +1 -0
  12. package/dist/es/render/svg/utils/path.mjs +6 -8
  13. package/dist/es/render/utils/animation.mjs +1 -1
  14. package/dist/es/render/utils/variants.mjs +19 -3
  15. package/dist/framer-motion.cjs.js +88 -70
  16. package/dist/framer-motion.dev.js +88 -70
  17. package/dist/framer-motion.js +1 -1
  18. package/dist/projection.dev.js +34 -27
  19. package/dist/size-rollup-dom-animation.js +1 -1
  20. package/dist/size-rollup-dom-max.js +1 -1
  21. package/dist/size-rollup-m.js +1 -1
  22. package/dist/size-webpack-dom-animation.js +1 -1
  23. package/dist/size-webpack-dom-max.js +1 -1
  24. package/dist/size-webpack-m.js +1 -1
  25. package/package.json +5 -5
  26. package/types/components/AnimatePresence/types.d.ts +1 -1
  27. package/types/context/MotionContext/create.d.ts +1 -1
  28. package/types/render/dom/utils/unit-conversion.d.ts +6 -0
  29. package/types/render/svg/types.d.ts +0 -4
  30. package/types/render/svg/utils/path.d.ts +1 -1
  31. package/types/render/utils/lifecycles.d.ts +9 -3
  32. package/types/types.d.ts +1 -1
  33. package/types/value/use-transform.d.ts +1 -1
package/README.md CHANGED
@@ -35,21 +35,22 @@ It looks like this:
35
35
 
36
36
  It does all this:
37
37
 
38
- - Spring animations
39
- - Simple keyframes syntax
38
+ - Springs
39
+ - Keyframes
40
+ - Layout animations
41
+ - Shared layout animations
40
42
  - Gestures (drag/tap/hover)
41
- - Layout and shared layout animations
42
43
  - SVG paths
43
44
  - Exit animations
44
45
  - Server-side rendering
45
- - Variants for orchestrating animations across components
46
+ - Orchestrate animations across components
46
47
  - CSS variables
47
48
 
48
49
  ...and a whole lot more.
49
50
 
50
51
  ## Get started
51
52
 
52
- ### Quick start
53
+ ### 🐇 Quick start
53
54
 
54
55
  ```
55
56
  npm install framer-motion
@@ -63,17 +64,17 @@ export const MyComponent = ({ isVisible }) => (
63
64
  )
64
65
  ```
65
66
 
66
- ### Docs
67
+ ### 📚 Docs
67
68
 
68
69
  Check out [our documentation](https://www.framer.com/docs/) for guides and a full API reference.
69
70
 
70
- Or checkout [our examples](https://framer.com/motion) for inspiration.
71
+ Or checkout [our examples](https://www.framer.com/docs/examples/) for inspiration.
71
72
 
72
- ### Contribute
73
+ ### 🛠 Contribute
73
74
 
74
75
  Want to contribute to Framer Motion? Our [contributing guide](https://github.com/framer/motion/blob/master/CONTRIBUTING.md) has you covered.
75
76
 
76
- ### License
77
+ ### 👩🏻‍⚖️ License
77
78
 
78
79
  Framer Motion is MIT licensed.
79
80
 
@@ -75,9 +75,9 @@ function convertTransitionToAnimationOptions(_a) {
75
75
  * Get the delay for a value by checking Transition with decreasing specificity.
76
76
  */
77
77
  function getDelayFromTransition(transition, key) {
78
- var _a;
78
+ var _a, _b;
79
79
  var valueTransition = getValueTransition(transition, key) || {};
80
- return (_a = valueTransition.delay) !== null && _a !== void 0 ? _a : 0;
80
+ return (_b = (_a = valueTransition.delay) !== null && _a !== void 0 ? _a : transition.delay) !== null && _b !== void 0 ? _b : 0;
81
81
  }
82
82
  function hydrateKeyframes(options) {
83
83
  if (Array.isArray(options.to) && options.to[0] === null) {
@@ -1,6 +1,6 @@
1
1
  import { __read, __spreadArray } from 'tslib';
2
2
  import * as React from 'react';
3
- import { useContext, useRef, cloneElement, Children, isValidElement } from 'react';
3
+ import { useContext, useRef, useEffect, cloneElement, Children, isValidElement } from 'react';
4
4
  import { useForceUpdate } from '../../utils/use-force-update.mjs';
5
5
  import { PresenceChild } from './PresenceChild.mjs';
6
6
  import { LayoutGroupContext } from '../../context/LayoutGroupContext.mjs';
@@ -72,6 +72,10 @@ var AnimatePresence = function (_a) {
72
72
  if (forceRenderLayoutGroup)
73
73
  forceRender = forceRenderLayoutGroup;
74
74
  var isInitialRender = useRef(true);
75
+ var isMounted = useRef(true);
76
+ useEffect(function () { return function () {
77
+ isMounted.current = false;
78
+ }; }, []);
75
79
  // Filter out any children that aren't ReactElements. We can only track ReactElements with a props.key
76
80
  var filteredChildren = onlyElements(children);
77
81
  // Keep a living record of the children we're actually rendering so we
@@ -130,6 +134,9 @@ var AnimatePresence = function (_a) {
130
134
  // Defer re-rendering until all exiting children have indeed left
131
135
  if (!exiting.size) {
132
136
  presentChildren.current = filteredChildren;
137
+ if (isMounted.current === false) {
138
+ return;
139
+ }
133
140
  forceRender();
134
141
  onExitComplete && onExitComplete();
135
142
  }
@@ -2,18 +2,9 @@ import { useMemo, useContext } from 'react';
2
2
  import { MotionContext } from './index.mjs';
3
3
  import { getCurrentTreeVariants } from './utils.mjs';
4
4
 
5
- function useCreateMotionContext(props, isStatic) {
5
+ function useCreateMotionContext(props) {
6
6
  var _a = getCurrentTreeVariants(props, useContext(MotionContext)), initial = _a.initial, animate = _a.animate;
7
- return useMemo(function () { return ({ initial: initial, animate: animate }); },
8
- /**
9
- * Only break memoisation in static mode
10
- */
11
- isStatic
12
- ? [
13
- variantLabelsAsDependency(initial),
14
- variantLabelsAsDependency(animate),
15
- ]
16
- : []);
7
+ return useMemo(function () { return ({ initial: initial, animate: animate }); }, [variantLabelsAsDependency(initial), variantLabelsAsDependency(animate)]);
17
8
  }
18
9
  function variantLabelsAsDependency(prop) {
19
10
  return Array.isArray(prop) ? prop.join(" ") : prop;
@@ -14,6 +14,8 @@ import { startAnimation } from '../../animation/utils/transitions.mjs';
14
14
  import { convertBoxToBoundingBox, convertBoundingBoxToBox } from '../../projection/geometry/conversion.mjs';
15
15
  import { addDomEvent } from '../../events/use-dom-event.mjs';
16
16
  import { mix } from 'popmotion';
17
+ import { percent } from 'style-value-types';
18
+ import { calcLength } from '../../projection/geometry/delta-calc.mjs';
17
19
 
18
20
  var elementDragControls = new WeakMap();
19
21
  /**
@@ -79,7 +81,19 @@ var VisualElementDragControls = /** @class */ (function () {
79
81
  * Record gesture origin
80
82
  */
81
83
  eachAxis(function (axis) {
82
- _this.originPoint[axis] = _this.getAxisMotionValue(axis).get();
84
+ var _a, _b;
85
+ var current = _this.getAxisMotionValue(axis).get() || 0;
86
+ /**
87
+ * If the MotionValue is a percentage value convert to px
88
+ */
89
+ if (percent.test(current)) {
90
+ var measuredAxis = (_b = (_a = _this.visualElement.projection) === null || _a === void 0 ? void 0 : _a.layout) === null || _b === void 0 ? void 0 : _b.actual[axis];
91
+ if (measuredAxis) {
92
+ var length_1 = calcLength(measuredAxis);
93
+ current = length_1 * (parseFloat(current) / 100);
94
+ }
95
+ }
96
+ _this.originPoint[axis] = current;
83
97
  });
84
98
  // Fire onDragStart event
85
99
  onDragStart === null || onDragStart === void 0 ? void 0 : onDragStart(event, info);
@@ -39,11 +39,7 @@ function createMotionComponent(_a) {
39
39
  */
40
40
  var config = useContext(MotionConfigContext);
41
41
  var features = null;
42
- /**
43
- * Create the tree context. This is memoized and will only trigger renders
44
- * when the current tree variant changes in static mode.
45
- */
46
- var context = useCreateMotionContext(props, config.isStatic);
42
+ var context = useCreateMotionContext(props);
47
43
  /**
48
44
  * Create a unique projection ID for this component. If a new component is added
49
45
  * during a layout animation we'll use this to query the DOM and hydrate its ref early, allowing
@@ -29,10 +29,6 @@ function useVisualElement(Component, visualState, props, createVisualElement) {
29
29
  });
30
30
  useEffect(function () {
31
31
  var _a;
32
- /**
33
- * In a future refactor we can replace the features-as-components and
34
- * have this loop through them all firing "effect" listeners
35
- */
36
32
  (_a = visualElement === null || visualElement === void 0 ? void 0 : visualElement.animationState) === null || _a === void 0 ? void 0 : _a.animateChanges();
37
33
  });
38
34
  useIsomorphicLayoutEffect(function () { return function () { return visualElement === null || visualElement === void 0 ? void 0 : visualElement.notifyUnmount(); }; }, []);
@@ -40,24 +40,26 @@ var BoundingBoxDimension;
40
40
  var getPosFromMatrix = function (matrix, pos) {
41
41
  return parseFloat(matrix.split(", ")[pos]);
42
42
  };
43
- var getTranslateFromMatrix = function (pos2, pos3) { return function (_bbox, _a) {
44
- var transform = _a.transform;
45
- if (transform === "none" || !transform)
46
- return 0;
47
- var matrix3d = transform.match(/^matrix3d\((.+)\)$/);
48
- if (matrix3d) {
49
- return getPosFromMatrix(matrix3d[1], pos3);
50
- }
51
- else {
52
- var matrix = transform.match(/^matrix\((.+)\)$/);
53
- if (matrix) {
54
- return getPosFromMatrix(matrix[1], pos2);
43
+ var getTranslateFromMatrix = function (pos2, pos3) {
44
+ return function (_bbox, _a) {
45
+ var transform = _a.transform;
46
+ if (transform === "none" || !transform)
47
+ return 0;
48
+ var matrix3d = transform.match(/^matrix3d\((.+)\)$/);
49
+ if (matrix3d) {
50
+ return getPosFromMatrix(matrix3d[1], pos3);
55
51
  }
56
52
  else {
57
- return 0;
53
+ var matrix = transform.match(/^matrix\((.+)\)$/);
54
+ if (matrix) {
55
+ return getPosFromMatrix(matrix[1], pos2);
56
+ }
57
+ else {
58
+ return 0;
59
+ }
58
60
  }
59
- }
60
- }; };
61
+ };
62
+ };
61
63
  var transformKeys = new Set(["x", "y", "z"]);
62
64
  var nonTranslationalTransformKeys = transformProps.filter(function (key) { return !transformKeys.has(key); });
63
65
  function removeNonTranslationalTransform(visualElement) {
@@ -76,13 +78,15 @@ function removeNonTranslationalTransform(visualElement) {
76
78
  }
77
79
  var positionalValues = {
78
80
  // Dimensions
79
- width: function (_a) {
81
+ width: function (_a, _b) {
80
82
  var x = _a.x;
81
- return x.max - x.min;
83
+ var _c = _b.paddingLeft, paddingLeft = _c === void 0 ? "0" : _c, _d = _b.paddingRight, paddingRight = _d === void 0 ? "0" : _d;
84
+ return x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight);
82
85
  },
83
- height: function (_a) {
86
+ height: function (_a, _b) {
84
87
  var y = _a.y;
85
- return y.max - y.min;
88
+ var _c = _b.paddingTop, paddingTop = _c === void 0 ? "0" : _c, _d = _b.paddingBottom, paddingBottom = _d === void 0 ? "0" : _d;
89
+ return y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom);
86
90
  },
87
91
  top: function (_bbox, _a) {
88
92
  var top = _a.top;
@@ -110,8 +114,7 @@ var convertChangedValueTypes = function (target, visualElement, changedKeys) {
110
114
  var originBbox = visualElement.measureViewportBox();
111
115
  var element = visualElement.getInstance();
112
116
  var elementComputedStyle = getComputedStyle(element);
113
- var display = elementComputedStyle.display, top = elementComputedStyle.top, left = elementComputedStyle.left, bottom = elementComputedStyle.bottom, right = elementComputedStyle.right, transform = elementComputedStyle.transform;
114
- var originComputedStyle = { top: top, left: left, bottom: bottom, right: right, transform: transform };
117
+ var display = elementComputedStyle.display;
115
118
  // If the element is currently set to display: "none", make it visible before
116
119
  // measuring the target bounding box
117
120
  if (display === "none") {
@@ -124,7 +127,7 @@ var convertChangedValueTypes = function (target, visualElement, changedKeys) {
124
127
  // Restore styles to their **calculated computed style**, not their actual
125
128
  // originally set style. This allows us to animate between equivalent pixel units.
126
129
  var value = visualElement.getValue(key);
127
- setAndResetVelocity(value, positionalValues[key](originBbox, originComputedStyle));
130
+ setAndResetVelocity(value, positionalValues[key](originBbox, elementComputedStyle));
128
131
  target[key] = positionalValues[key](targetBbox, elementComputedStyle);
129
132
  });
130
133
  return target;
@@ -145,8 +148,8 @@ var checkAndConvertChangedValueTypes = function (visualElement, target, origin,
145
148
  if (!visualElement.hasValue(key))
146
149
  return;
147
150
  var from = origin[key];
148
- var to = target[key];
149
151
  var fromType = findDimensionValueType(from);
152
+ var to = target[key];
150
153
  var toType;
151
154
  // TODO: The current implementation of this basically throws an error
152
155
  // if you try and do value conversion via keyframes. There's probably
@@ -154,7 +157,10 @@ var checkAndConvertChangedValueTypes = function (visualElement, target, origin,
154
157
  // as it'd be doing multiple resize-remeasure operations.
155
158
  if (isKeyframesTarget(to)) {
156
159
  var numKeyframes = to.length;
157
- for (var i = to[0] === null ? 1 : 0; i < numKeyframes; i++) {
160
+ var fromIndex = to[0] === null ? 1 : 0;
161
+ from = to[fromIndex];
162
+ fromType = findDimensionValueType(from);
163
+ for (var i = fromIndex; i < numKeyframes; i++) {
158
164
  if (!toType) {
159
165
  toType = findDimensionValueType(to[i]);
160
166
  invariant(toType === fromType ||
@@ -199,7 +205,8 @@ var checkAndConvertChangedValueTypes = function (visualElement, target, origin,
199
205
  // If we're going to do value conversion via DOM measurements, we first
200
206
  // need to remove non-positional transform values that could affect the bbox measurements.
201
207
  if (!hasAttemptedToRemoveTransformValues) {
202
- removedTransformValues = removeNonTranslationalTransform(visualElement);
208
+ removedTransformValues =
209
+ removeNonTranslationalTransform(visualElement);
203
210
  hasAttemptedToRemoveTransformValues = true;
204
211
  }
205
212
  changedValueTypeKeys.push(key);
@@ -241,4 +248,4 @@ function unitConversion(visualElement, target, origin, transitionEnd) {
241
248
  : { target: target, transitionEnd: transitionEnd };
242
249
  }
243
250
 
244
- export { BoundingBoxDimension, unitConversion };
251
+ export { BoundingBoxDimension, positionalValues, unitConversion };
@@ -26,17 +26,11 @@ var svgMotionConfig = {
26
26
  height: 0,
27
27
  };
28
28
  }
29
- if (isPath(instance)) {
30
- renderState.totalPathLength = instance.getTotalLength();
31
- }
32
29
  buildSVGAttrs(renderState, latestValues, { enableHardwareAcceleration: false }, props.transformTemplate);
33
30
  // TODO: Replace with direct assignment
34
31
  renderSVG(instance, renderState);
35
32
  },
36
33
  }),
37
34
  };
38
- function isPath(element) {
39
- return element.tagName === "path";
40
- }
41
35
 
42
36
  export { svgMotionConfig };
@@ -13,7 +13,7 @@ function buildSVGAttrs(state, _a, options, transformTemplate) {
13
13
  buildHTMLStyles(state, latest, options, transformTemplate);
14
14
  state.attrs = state.style;
15
15
  state.style = {};
16
- var attrs = state.attrs, style = state.style, dimensions = state.dimensions, totalPathLength = state.totalPathLength;
16
+ var attrs = state.attrs, style = state.style, dimensions = state.dimensions;
17
17
  /**
18
18
  * However, we apply transforms as CSS transforms. So if we detect a transform we take it from attrs
19
19
  * and copy it into style.
@@ -33,9 +33,9 @@ function buildSVGAttrs(state, _a, options, transformTemplate) {
33
33
  attrs.x = attrX;
34
34
  if (attrY !== undefined)
35
35
  attrs.y = attrY;
36
- // Build SVG path if one has been measured
37
- if (totalPathLength !== undefined && pathLength !== undefined) {
38
- buildSVGPath(attrs, totalPathLength, pathLength, pathSpacing, pathOffset, false);
36
+ // Build SVG path if one has been defined
37
+ if (pathLength !== undefined) {
38
+ buildSVGPath(attrs, pathLength, pathSpacing, pathOffset, false);
39
39
  }
40
40
  }
41
41
 
@@ -21,6 +21,7 @@ var camelCaseAttributes = new Set([
21
21
  "tableValues",
22
22
  "viewBox",
23
23
  "gradientTransform",
24
+ "pathLength",
24
25
  ]);
25
26
 
26
27
  export { camelCaseAttributes };
@@ -1,9 +1,5 @@
1
1
  import { px } from 'style-value-types';
2
2
 
3
- // Convert a progress 0-1 to a pixels value based on the provided length
4
- var progressToPixels = function (progress, length) {
5
- return px.transform(progress * length);
6
- };
7
3
  var dashKeys = {
8
4
  offset: "stroke-dashoffset",
9
5
  array: "stroke-dasharray",
@@ -19,18 +15,20 @@ var camelKeys = {
19
15
  *
20
16
  * This function is mutative to reduce per-frame GC.
21
17
  */
22
- function buildSVGPath(attrs, totalLength, length, spacing, offset, useDashCase) {
18
+ function buildSVGPath(attrs, length, spacing, offset, useDashCase) {
23
19
  if (spacing === void 0) { spacing = 1; }
24
20
  if (offset === void 0) { offset = 0; }
25
21
  if (useDashCase === void 0) { useDashCase = true; }
22
+ // Normalise path length by setting SVG attribute pathLength to 1
23
+ attrs.pathLength = 1;
26
24
  // We use dash case when setting attributes directly to the DOM node and camel case
27
25
  // when defining props on a React component.
28
26
  var keys = useDashCase ? dashKeys : camelKeys;
29
27
  // Build the dash offset
30
- attrs[keys.offset] = progressToPixels(-offset, totalLength);
28
+ attrs[keys.offset] = px.transform(-offset);
31
29
  // Build the dash array
32
- var pathLength = progressToPixels(length, totalLength);
33
- var pathSpacing = progressToPixels(spacing, totalLength);
30
+ var pathLength = px.transform(length);
31
+ var pathSpacing = px.transform(spacing);
34
32
  attrs[keys.array] = pathLength + " " + pathSpacing;
35
33
  }
36
34
 
@@ -8,7 +8,7 @@ import { resolveVariant } from './variants.mjs';
8
8
  */
9
9
  function animateVisualElement(visualElement, definition, options) {
10
10
  if (options === void 0) { options = {}; }
11
- visualElement.notifyAnimationStart();
11
+ visualElement.notifyAnimationStart(definition);
12
12
  var animation;
13
13
  if (Array.isArray(definition)) {
14
14
  var animations = definition.map(function (variant) {
@@ -30,12 +30,28 @@ function resolveVariantFromProps(props, definition, custom, currentValues, curre
30
30
  var _a;
31
31
  if (currentValues === void 0) { currentValues = {}; }
32
32
  if (currentVelocity === void 0) { currentVelocity = {}; }
33
+ /**
34
+ * If the variant definition is a function, resolve.
35
+ */
36
+ if (typeof definition === "function") {
37
+ definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity);
38
+ }
39
+ /**
40
+ * If the variant definition is a variant label, or
41
+ * the function returned a variant label, resolve.
42
+ */
33
43
  if (typeof definition === "string") {
34
44
  definition = (_a = props.variants) === null || _a === void 0 ? void 0 : _a[definition];
35
45
  }
36
- return typeof definition === "function"
37
- ? definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity)
38
- : definition;
46
+ /**
47
+ * At this point we've resolved both functions and variant labels,
48
+ * but the resolved variant label might itself have been a function.
49
+ * If so, resolve. This can only have returned a valid target object.
50
+ */
51
+ if (typeof definition === "function") {
52
+ definition = definition(custom !== null && custom !== void 0 ? custom : props.custom, currentValues, currentVelocity);
53
+ }
54
+ return definition;
39
55
  }
40
56
  function resolveVariant(visualElement, definition, custom) {
41
57
  var props = visualElement.getProps();