motion 12.7.4 → 12.7.5-alpha.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.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/debug.js +22 -14
  3. package/dist/cjs/index.js +4113 -3624
  4. package/dist/cjs/mini.js +403 -324
  5. package/dist/cjs/react-client.js +3151 -3245
  6. package/dist/cjs/react-m.js +169 -166
  7. package/dist/cjs/react-mini.js +330 -251
  8. package/dist/es/framer-motion/dist/es/animation/animate/sequence.mjs +1 -1
  9. package/dist/es/framer-motion/dist/es/animation/animators/waapi/animate-elements.mjs +81 -9
  10. package/dist/es/framer-motion/dist/es/animation/interfaces/motion-value.mjs +11 -30
  11. package/dist/es/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs +1 -1
  12. package/dist/es/framer-motion/dist/es/animation/optimized-appear/store-id.mjs +1 -1
  13. package/dist/es/framer-motion/dist/es/animation/sequence/create.mjs +3 -3
  14. package/dist/es/framer-motion/dist/es/animation/sequence/utils/edit.mjs +2 -2
  15. package/dist/es/framer-motion/dist/es/animation/utils/default-transitions.mjs +1 -1
  16. package/dist/es/framer-motion/dist/es/animation/utils/stagger.mjs +1 -1
  17. package/dist/es/framer-motion/dist/es/components/Reorder/utils/check-reorder.mjs +1 -1
  18. package/dist/es/framer-motion/dist/es/gestures/drag/VisualElementDragControls.mjs +2 -2
  19. package/dist/es/framer-motion/dist/es/gestures/drag/utils/constraints.mjs +2 -2
  20. package/dist/es/framer-motion/dist/es/gestures/focus.mjs +1 -1
  21. package/dist/es/framer-motion/dist/es/gestures/pan/PanSession.mjs +1 -1
  22. package/dist/es/framer-motion/dist/es/motion/utils/is-forced-motion-value.mjs +1 -1
  23. package/dist/es/framer-motion/dist/es/projection/animation/mix-values.mjs +3 -3
  24. package/dist/es/framer-motion/dist/es/projection/geometry/delta-apply.mjs +1 -1
  25. package/dist/es/framer-motion/dist/es/projection/geometry/delta-calc.mjs +1 -1
  26. package/dist/es/framer-motion/dist/es/projection/geometry/delta-remove.mjs +2 -2
  27. package/dist/es/framer-motion/dist/es/projection/node/create-projection-node.mjs +3 -5
  28. package/dist/es/framer-motion/dist/es/projection/styles/scale-border-radius.mjs +1 -1
  29. package/dist/es/framer-motion/dist/es/projection/styles/scale-box-shadow.mjs +2 -2
  30. package/dist/es/framer-motion/dist/es/projection/styles/scale-correction.mjs +1 -1
  31. package/dist/es/framer-motion/dist/es/render/VisualElement.mjs +7 -7
  32. package/dist/es/framer-motion/dist/es/render/dom/DOMVisualElement.mjs +1 -1
  33. package/dist/es/framer-motion/dist/es/render/dom/scroll/attach-animation.mjs +17 -0
  34. package/dist/es/framer-motion/dist/es/render/dom/scroll/attach-function.mjs +23 -0
  35. package/dist/es/framer-motion/dist/es/render/dom/scroll/index.mjs +6 -82
  36. package/dist/es/framer-motion/dist/es/render/dom/scroll/offsets/index.mjs +3 -3
  37. package/dist/es/framer-motion/dist/es/render/dom/scroll/utils/get-timeline.mjs +29 -0
  38. package/dist/es/framer-motion/dist/es/render/html/HTMLVisualElement.mjs +3 -3
  39. package/dist/es/framer-motion/dist/es/render/html/utils/build-styles.mjs +4 -4
  40. package/dist/es/framer-motion/dist/es/render/html/utils/build-transform.mjs +3 -3
  41. package/dist/es/framer-motion/dist/es/render/svg/SVGVisualElement.mjs +2 -2
  42. package/dist/es/framer-motion/dist/es/render/svg/config-motion.mjs +1 -1
  43. package/dist/es/framer-motion/dist/es/render/svg/utils/path.mjs +1 -1
  44. package/dist/es/framer-motion/dist/es/render/svg/utils/scrape-motion-values.mjs +1 -1
  45. package/dist/es/framer-motion/dist/es/render/svg/utils/transform-origin.mjs +1 -1
  46. package/dist/es/framer-motion/dist/es/render/utils/motion-values.mjs +1 -1
  47. package/dist/es/framer-motion/dist/es/utils/delay.mjs +1 -1
  48. package/dist/es/framer-motion/dist/es/utils/transform.mjs +1 -1
  49. package/dist/es/framer-motion/dist/es/utils/use-cycle.mjs +1 -1
  50. package/dist/es/framer-motion/dist/es/utils/use-instant-transition.mjs +4 -4
  51. package/dist/es/framer-motion/dist/es/value/use-spring.mjs +2 -2
  52. package/dist/es/framer-motion/dist/es/value/use-will-change/get-will-change-name.mjs +2 -2
  53. package/dist/es/motion/lib/index.mjs +109 -26
  54. package/dist/es/motion/lib/react.mjs +108 -32
  55. package/dist/es/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs +179 -0
  56. package/dist/es/motion-dom/dist/es/animation/GroupAnimation.mjs +6 -15
  57. package/dist/es/{framer-motion/dist/es/animation/animators/MainThreadAnimation.mjs → motion-dom/dist/es/animation/JSAnimation.mjs} +108 -156
  58. package/dist/es/motion-dom/dist/es/animation/NativeAnimation.mjs +64 -67
  59. package/dist/es/motion-dom/dist/es/animation/NativeAnimationExtended.mjs +65 -0
  60. package/dist/es/motion-dom/dist/es/animation/NativeAnimationWrapper.mjs +14 -0
  61. package/dist/es/{framer-motion/dist/es/animation/animators → motion-dom/dist/es/animation}/drivers/driver-frameloop.mjs +2 -2
  62. package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/keyframes.mjs +5 -5
  63. package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/spring/find.mjs +1 -1
  64. package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/spring/index.mjs +5 -6
  65. package/dist/es/{framer-motion/dist/es/render/dom → motion-dom/dist/es/animation/keyframes}/DOMKeyframesResolver.mjs +9 -8
  66. package/dist/es/{framer-motion/dist/es/render/utils → motion-dom/dist/es/animation/keyframes}/KeyframesResolver.mjs +28 -35
  67. package/dist/es/motion-dom/dist/es/animation/keyframes/get-final.mjs +3 -4
  68. package/dist/es/{framer-motion/dist/es/utils → motion-dom/dist/es/animation/keyframes}/offsets/fill.mjs +2 -2
  69. package/dist/es/motion-dom/dist/es/animation/keyframes/utils/apply-px-defaults.mjs +11 -0
  70. package/dist/es/motion-dom/dist/es/animation/keyframes/utils/fill-wildcards.mjs +7 -0
  71. package/dist/es/{framer-motion/dist/es/animation → motion-dom/dist/es/animation/keyframes}/utils/is-none.mjs +1 -1
  72. package/dist/es/{framer-motion/dist/es/render/html → motion-dom/dist/es/animation/keyframes}/utils/make-none-animatable.mjs +1 -1
  73. package/dist/es/{framer-motion/dist/es/render/dom → motion-dom/dist/es/animation/keyframes}/utils/unit-conversion.mjs +2 -2
  74. package/dist/es/motion-dom/dist/es/animation/utils/WithPromise.mjs +28 -0
  75. package/dist/es/motion-dom/dist/es/animation/utils/active-animations.mjs +9 -0
  76. package/dist/es/{framer-motion/dist/es/animation/animators → motion-dom/dist/es/animation}/utils/can-animate.mjs +3 -3
  77. package/dist/es/{framer-motion/dist/es/render/dom → motion-dom/dist/es/animation}/utils/css-variables-conversion.mjs +2 -2
  78. package/dist/es/motion-dom/dist/es/animation/utils/replace-transition-type.mjs +18 -0
  79. package/dist/es/motion-dom/dist/es/animation/waapi/easing/is-supported.mjs +1 -1
  80. package/dist/es/motion-dom/dist/es/animation/waapi/easing/map-easing.mjs +5 -3
  81. package/dist/es/motion-dom/dist/es/animation/waapi/start-waapi-animation.mjs +6 -4
  82. package/dist/es/motion-dom/dist/es/animation/waapi/supports/waapi.mjs +39 -0
  83. package/dist/es/motion-dom/dist/es/animation/waapi/utils/apply-generator.mjs +2 -1
  84. package/dist/es/motion-dom/dist/es/animation/waapi/utils/unsupported-easing.mjs +20 -0
  85. package/dist/es/motion-dom/dist/es/frameloop/batcher.mjs +2 -1
  86. package/dist/es/motion-dom/dist/es/frameloop/order.mjs +1 -0
  87. package/dist/es/motion-dom/dist/es/render/dom/is-css-var.mjs +3 -0
  88. package/dist/es/motion-dom/dist/es/render/dom/style-computed.mjs +10 -0
  89. package/dist/es/motion-dom/dist/es/render/dom/style-set.mjs +9 -0
  90. package/dist/es/{framer-motion/dist/es/render/html → motion-dom/dist/es/render}/utils/keys-transform.mjs +1 -1
  91. package/dist/es/{framer-motion/dist/es/render/dom → motion-dom/dist/es}/scroll/observe.mjs +1 -1
  92. package/dist/es/motion-dom/dist/es/stats/index.mjs +2 -0
  93. package/dist/es/{framer-motion → motion-dom}/dist/es/utils/interpolate.mjs +4 -3
  94. package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/color.mjs +3 -3
  95. package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/complex.mjs +5 -5
  96. package/dist/es/motion-dom/dist/es/value/index.mjs +3 -1
  97. package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/color/rgba.mjs +2 -2
  98. package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types}/dimensions.mjs +3 -3
  99. package/dist/es/{framer-motion/dist/es/render/dom/value-types/type-int.mjs → motion-dom/dist/es/value/types/int.mjs} +1 -1
  100. package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types/maps}/defaults.mjs +2 -2
  101. package/dist/es/{framer-motion/dist/es/render/dom/value-types/number-browser.mjs → motion-dom/dist/es/value/types/maps/number.mjs} +13 -3
  102. package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types/maps}/transform.mjs +2 -2
  103. package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/numbers/index.mjs +1 -1
  104. package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/numbers/units.mjs +3 -2
  105. package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types/utils}/animatable-none.mjs +4 -4
  106. package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types/utils}/find.mjs +4 -4
  107. package/dist/es/motion-dom/dist/es/view/index.mjs +64 -0
  108. package/dist/es/motion-dom/dist/es/view/queue.mjs +52 -0
  109. package/dist/es/motion-dom/dist/es/view/start.mjs +155 -0
  110. package/dist/es/motion-dom/dist/es/view/utils/choose-layer-type.mjs +11 -0
  111. package/dist/es/motion-dom/dist/es/view/utils/css.mjs +32 -0
  112. package/dist/es/motion-dom/dist/es/view/utils/get-layer-name.mjs +8 -0
  113. package/dist/es/motion-dom/dist/es/view/utils/get-view-animations.mjs +12 -0
  114. package/dist/es/motion-dom/dist/es/view/utils/has-target.mjs +5 -0
  115. package/dist/es/{framer-motion → motion-utils}/dist/es/easing/cubic-bezier.mjs +1 -1
  116. package/dist/es/{framer-motion → motion-utils}/dist/es/easing/steps.mjs +1 -1
  117. package/dist/es/{framer-motion → motion-utils}/dist/es/easing/utils/get-easing-for-segment.mjs +1 -1
  118. package/dist/es/{framer-motion → motion-utils}/dist/es/easing/utils/map.mjs +7 -4
  119. package/dist/es/motion-utils/dist/es/global-config.mjs +1 -4
  120. package/dist/es/motion-utils/dist/es/warn-once.mjs +4 -1
  121. package/dist/motion.dev.js +4108 -3619
  122. package/dist/motion.js +1 -1
  123. package/package.json +3 -3
  124. package/dist/es/framer-motion/dist/es/animation/animators/AcceleratedAnimation.mjs +0 -324
  125. package/dist/es/framer-motion/dist/es/animation/animators/BaseAnimation.mjs +0 -120
  126. package/dist/es/framer-motion/dist/es/animation/animators/waapi/utils/supports-waapi.mjs +0 -5
  127. package/dist/es/framer-motion/dist/es/render/dom/value-types/number.mjs +0 -18
  128. package/dist/es/framer-motion/dist/es/utils/use-instant-transition-state.mjs +0 -5
  129. package/dist/es/motion-dom/dist/es/animation/keyframes/hydrate.mjs +0 -26
  130. package/dist/es/motion-dom/dist/es/animation/waapi/utils/attach-timeline.mjs +0 -6
  131. package/dist/es/motion-dom/dist/es/render/dom/style.mjs +0 -15
  132. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/inertia.mjs +0 -0
  133. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/spring/defaults.mjs +0 -0
  134. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/generators/utils/velocity.mjs +0 -0
  135. /package/dist/es/{framer-motion/dist/es/utils → motion-dom/dist/es/animation/keyframes}/offsets/default.mjs +0 -0
  136. /package/dist/es/{framer-motion/dist/es/utils → motion-dom/dist/es/animation/keyframes}/offsets/time.mjs +0 -0
  137. /package/dist/es/{framer-motion → motion-dom}/dist/es/animation/utils/is-animatable.mjs +0 -0
  138. /package/dist/es/{framer-motion/dist/es/render/dom → motion-dom/dist/es/animation}/utils/is-css-variable.mjs +0 -0
  139. /package/dist/es/{framer-motion/dist/es/animation/animators → motion-dom/dist/es/animation/waapi}/utils/accelerated-values.mjs +0 -0
  140. /package/dist/es/{framer-motion/dist/es/render/html/utils → motion-dom/dist/es/render/dom}/parse-transform.mjs +0 -0
  141. /package/dist/es/{framer-motion/dist/es/render/html → motion-dom/dist/es/render}/utils/keys-position.mjs +0 -0
  142. /package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/immediate.mjs +0 -0
  143. /package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/index.mjs +0 -0
  144. /package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/number.mjs +0 -0
  145. /package/dist/es/{framer-motion → motion-dom}/dist/es/utils/mix/visibility.mjs +0 -0
  146. /package/dist/es/{framer-motion/dist/es/render/dom/value-types/type-auto.mjs → motion-dom/dist/es/value/types/auto.mjs} +0 -0
  147. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/color/hex.mjs +0 -0
  148. /package/dist/es/{framer-motion/dist/es/utils → motion-dom/dist/es/value/types/color}/hsla-to-rgba.mjs +0 -0
  149. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/color/hsla.mjs +0 -0
  150. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/color/index.mjs +0 -0
  151. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/color/utils.mjs +0 -0
  152. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/complex/filter.mjs +0 -0
  153. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/complex/index.mjs +0 -0
  154. /package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types}/test.mjs +0 -0
  155. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/utils/color-regex.mjs +0 -0
  156. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/utils/float-regex.mjs +0 -0
  157. /package/dist/es/{framer-motion/dist/es/render/dom/value-types → motion-dom/dist/es/value/types/utils}/get-as-type.mjs +0 -0
  158. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/utils/is-nullish.mjs +0 -0
  159. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/utils/sanitize.mjs +0 -0
  160. /package/dist/es/{framer-motion → motion-dom}/dist/es/value/types/utils/single-color-regex.mjs +0 -0
  161. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/clamp.mjs +0 -0
  162. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/anticipate.mjs +0 -0
  163. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/back.mjs +0 -0
  164. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/circ.mjs +0 -0
  165. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/ease.mjs +0 -0
  166. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/modifiers/mirror.mjs +0 -0
  167. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/modifiers/reverse.mjs +0 -0
  168. /package/dist/es/{motion-dom/dist/es → motion-utils/dist/es/easing}/utils/is-bezier-definition.mjs +0 -0
  169. /package/dist/es/{framer-motion → motion-utils}/dist/es/easing/utils/is-easing-array.mjs +0 -0
  170. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/is-numerical-string.mjs +0 -0
  171. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/is-zero-value-string.mjs +0 -0
  172. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/pipe.mjs +0 -0
  173. /package/dist/es/{framer-motion/dist/es/utils → motion-utils/dist/es}/wrap.mjs +0 -0
@@ -56,187 +56,71 @@ const secondsToMilliseconds = (seconds) => seconds * 1000;
56
56
  /*#__NO_SIDE_EFFECTS__*/
57
57
  const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
58
58
 
59
- const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
60
-
61
- class GroupAnimation {
62
- constructor(animations) {
63
- // Bound to accomodate common `return animation.stop` pattern
64
- this.stop = () => this.runAll("stop");
65
- this.animations = animations.filter(Boolean);
66
- }
67
- get finished() {
68
- return Promise.all(this.animations.map((animation) => animation.finished));
69
- }
70
- /**
71
- * TODO: Filter out cancelled or stopped animations before returning
72
- */
73
- getAll(propName) {
74
- return this.animations[0][propName];
75
- }
76
- setAll(propName, newValue) {
77
- for (let i = 0; i < this.animations.length; i++) {
78
- this.animations[i][propName] = newValue;
79
- }
80
- }
81
- attachTimeline(timeline, fallback) {
82
- const subscriptions = this.animations.map((animation) => {
83
- if (supportsScrollTimeline() && animation.attachTimeline) {
84
- return animation.attachTimeline(timeline);
85
- }
86
- else if (typeof fallback === "function") {
87
- return fallback(animation);
88
- }
89
- });
90
- return () => {
91
- subscriptions.forEach((cancel, i) => {
92
- cancel && cancel();
93
- this.animations[i].stop();
94
- });
95
- };
96
- }
97
- get time() {
98
- return this.getAll("time");
99
- }
100
- set time(time) {
101
- this.setAll("time", time);
102
- }
103
- get speed() {
104
- return this.getAll("speed");
105
- }
106
- set speed(speed) {
107
- this.setAll("speed", speed);
108
- }
109
- get startTime() {
110
- return this.getAll("startTime");
111
- }
112
- get duration() {
113
- let max = 0;
114
- for (let i = 0; i < this.animations.length; i++) {
115
- max = Math.max(max, this.animations[i].duration);
116
- }
117
- return max;
118
- }
119
- runAll(methodName) {
120
- this.animations.forEach((controls) => controls[methodName]());
121
- }
122
- flatten() {
123
- this.runAll("flatten");
124
- }
125
- play() {
126
- this.runAll("play");
127
- }
128
- pause() {
129
- this.runAll("pause");
130
- }
131
- cancel() {
132
- this.runAll("cancel");
133
- }
134
- complete() {
135
- this.runAll("complete");
136
- }
137
- }
59
+ const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
138
60
 
139
- class GroupAnimationWithThen extends GroupAnimation {
140
- then(onResolve, _onReject) {
141
- return this.finished.finally(onResolve).then(() => { });
61
+ const generateLinearEasing = (easing, duration, // as milliseconds
62
+ resolution = 10 // as milliseconds
63
+ ) => {
64
+ let points = "";
65
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
66
+ for (let i = 0; i < numPoints; i++) {
67
+ points += easing(i / (numPoints - 1)) + ", ";
142
68
  }
143
- }
144
-
145
- const isCSSVar = (name) => name.startsWith("--");
146
- const style = {
147
- set: (element, name, value) => {
148
- isCSSVar(name)
149
- ? element.style.setProperty(name, value)
150
- : (element.style[name] = value);
151
- },
152
- get: (element, name) => {
153
- return isCSSVar(name)
154
- ? element.style.getPropertyValue(name)
155
- : element.style[name];
156
- },
69
+ return `linear(${points.substring(0, points.length - 2)})`;
157
70
  };
158
71
 
159
72
  const isNotNull = (value) => value !== null;
160
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
73
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
161
74
  const resolvedKeyframes = keyframes.filter(isNotNull);
162
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
163
- ? 0
164
- : resolvedKeyframes.length - 1;
75
+ const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
76
+ const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
165
77
  return !index || finalKeyframe === undefined
166
78
  ? resolvedKeyframes[index]
167
79
  : finalKeyframe;
168
80
  }
169
81
 
170
- const supportsPartialKeyframes = /*@__PURE__*/ memo(() => {
171
- try {
172
- document.createElement("div").animate({ opacity: [1] });
82
+ class WithPromise {
83
+ constructor() {
84
+ this.count = 0;
85
+ this.updateFinished();
173
86
  }
174
- catch (e) {
175
- return false;
87
+ get finished() {
88
+ return this._finished;
176
89
  }
177
- return true;
178
- });
179
-
180
- const pxValues = new Set([
181
- // Border props
182
- "borderWidth",
183
- "borderTopWidth",
184
- "borderRightWidth",
185
- "borderBottomWidth",
186
- "borderLeftWidth",
187
- "borderRadius",
188
- "radius",
189
- "borderTopLeftRadius",
190
- "borderTopRightRadius",
191
- "borderBottomRightRadius",
192
- "borderBottomLeftRadius",
193
- // Positioning props
194
- "width",
195
- "maxWidth",
196
- "height",
197
- "maxHeight",
198
- "top",
199
- "right",
200
- "bottom",
201
- "left",
202
- // Spacing props
203
- "padding",
204
- "paddingTop",
205
- "paddingRight",
206
- "paddingBottom",
207
- "paddingLeft",
208
- "margin",
209
- "marginTop",
210
- "marginRight",
211
- "marginBottom",
212
- "marginLeft",
213
- // Misc
214
- "backgroundPositionX",
215
- "backgroundPositionY",
216
- ]);
217
-
218
- function hydrateKeyframes(element, name, keyframes, pseudoElement) {
219
- if (!Array.isArray(keyframes)) {
220
- keyframes = [keyframes];
90
+ updateFinished() {
91
+ this.count++;
92
+ this._finished = new Promise((resolve) => {
93
+ this.resolve = resolve;
94
+ });
221
95
  }
222
- for (let i = 0; i < keyframes.length; i++) {
223
- if (keyframes[i] === null) {
224
- keyframes[i] =
225
- i === 0 && !pseudoElement
226
- ? style.get(element, name)
227
- : keyframes[i - 1];
228
- }
229
- if (typeof keyframes[i] === "number" && pxValues.has(name)) {
230
- keyframes[i] = keyframes[i] + "px";
231
- }
96
+ notifyFinished() {
97
+ this.resolve();
232
98
  }
233
- if (!pseudoElement && !supportsPartialKeyframes() && keyframes.length < 2) {
234
- keyframes.unshift(style.get(element, name));
99
+ /**
100
+ * Allows the animation to be awaited.
101
+ *
102
+ * @deprecated Use `finished` instead.
103
+ */
104
+ then(onResolve, onReject) {
105
+ return this.finished.then(onResolve, onReject);
235
106
  }
236
- return keyframes;
237
107
  }
238
108
 
239
- const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
109
+ function fillWildcards(keyframes) {
110
+ for (let i = 1; i < keyframes.length; i++) {
111
+ keyframes[i] ?? (keyframes[i] = keyframes[i - 1]);
112
+ }
113
+ }
114
+
115
+ const isCSSVar = (name) => name.startsWith("--");
116
+
117
+ function setStyle(element, name, value) {
118
+ isCSSVar(name)
119
+ ? element.style.setProperty(name, value)
120
+ : (element.style[name] = value);
121
+ }
122
+
123
+ const supportsScrollTimeline = /* @__PURE__ */ memo(() => window.ScrollTimeline !== undefined);
240
124
 
241
125
  /**
242
126
  * Add the ability for test suites to manually set support flags
@@ -261,17 +145,6 @@ const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
261
145
  return true;
262
146
  }, "linearEasing");
263
147
 
264
- const generateLinearEasing = (easing, duration, // as milliseconds
265
- resolution = 10 // as milliseconds
266
- ) => {
267
- let points = "";
268
- const numPoints = Math.max(Math.round(duration / resolution), 2);
269
- for (let i = 0; i < numPoints; i++) {
270
- points += easing(i / (numPoints - 1)) + ", ";
271
- }
272
- return `linear(${points.substring(0, points.length - 2)})`;
273
- };
274
-
275
148
  const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
276
149
 
277
150
  const supportedWaapiEasing = {
@@ -290,8 +163,10 @@ function mapEasingToNativeEasing(easing, duration) {
290
163
  if (!easing) {
291
164
  return undefined;
292
165
  }
293
- else if (typeof easing === "function" && supportsLinearEasing()) {
294
- return generateLinearEasing(easing, duration);
166
+ else if (typeof easing === "function") {
167
+ return supportsLinearEasing()
168
+ ? generateLinearEasing(easing, duration)
169
+ : "ease-out";
295
170
  }
296
171
  else if (isBezierDefinition(easing)) {
297
172
  return cubicBezierAsString(easing);
@@ -305,7 +180,7 @@ function mapEasingToNativeEasing(easing, duration) {
305
180
  }
306
181
  }
307
182
 
308
- function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}, pseudoElement = undefined) {
183
+ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeOut", times, } = {}, pseudoElement = undefined) {
309
184
  const keyframeOptions = {
310
185
  [valueName]: keyframes,
311
186
  };
@@ -317,15 +192,17 @@ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duratio
317
192
  */
318
193
  if (Array.isArray(easing))
319
194
  keyframeOptions.easing = easing;
320
- const animation = element.animate(keyframeOptions, {
195
+ const options = {
321
196
  delay,
322
197
  duration,
323
198
  easing: !Array.isArray(easing) ? easing : "linear",
324
199
  fill: "both",
325
200
  iterations: repeat + 1,
326
201
  direction: repeatType === "reverse" ? "alternate" : "normal",
327
- pseudoElement,
328
- });
202
+ };
203
+ if (pseudoElement)
204
+ options.pseudoElement = pseudoElement;
205
+ const animation = element.animate(keyframeOptions, options);
329
206
  return animation;
330
207
  }
331
208
 
@@ -334,7 +211,7 @@ function isGenerator(type) {
334
211
  }
335
212
 
336
213
  function applyGeneratorOptions({ type, ...options }) {
337
- if (isGenerator(type)) {
214
+ if (isGenerator(type) && supportsLinearEasing()) {
338
215
  return type.applyToOptions(options);
339
216
  }
340
217
  else {
@@ -344,86 +221,81 @@ function applyGeneratorOptions({ type, ...options }) {
344
221
  return options;
345
222
  }
346
223
 
347
- const animationMaps = new WeakMap();
348
- const animationMapKey = (name, pseudoElement) => `${name}:${pseudoElement}`;
349
- function getAnimationMap(element) {
350
- const map = animationMaps.get(element) || new Map();
351
- animationMaps.set(element, map);
352
- return map;
353
- }
354
224
  /**
355
225
  * NativeAnimation implements AnimationPlaybackControls for the browser's Web Animations API.
356
226
  */
357
- class NativeAnimation {
227
+ class NativeAnimation extends WithPromise {
358
228
  constructor(options) {
359
- /**
360
- * If we already have an animation, we don't need to instantiate one
361
- * and can just use this as a controls interface.
362
- */
363
- if ("animation" in options) {
364
- this.animation = options.animation;
229
+ super();
230
+ this.finishedTime = null;
231
+ this.isStopped = false;
232
+ if (!options)
365
233
  return;
366
- }
367
- const { element, name, keyframes: unresolvedKeyframes, pseudoElement, allowFlatten = false, } = options;
368
- let { transition } = options;
234
+ const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
369
235
  this.isPseudoElement = Boolean(pseudoElement);
370
236
  this.allowFlatten = allowFlatten;
371
- /**
372
- * Stop any existing animations on the element before reading existing keyframes.
373
- *
374
- * TODO: Check for VisualElement before using animation state. This is a fallback
375
- * for mini animate(). Do this when implementing NativeAnimationExtended.
376
- */
377
- const animationMap = getAnimationMap(element);
378
- const key = animationMapKey(name, pseudoElement || "");
379
- const currentAnimation = animationMap.get(key);
380
- currentAnimation && currentAnimation.stop();
381
- /**
382
- * TODO: If these keyframes aren't correctly hydrated then we want to throw
383
- * run an instant animation.
384
- */
385
- const keyframes = hydrateKeyframes(element, name, unresolvedKeyframes, pseudoElement);
386
- invariant(typeof transition.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
387
- transition = applyGeneratorOptions(transition);
237
+ this.options = options;
238
+ invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "motion"?`);
239
+ const transition = applyGeneratorOptions(options);
388
240
  this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
389
241
  if (transition.autoplay === false) {
390
242
  this.animation.pause();
391
243
  }
392
- this.removeAnimation = () => animationMap.delete(key);
393
244
  this.animation.onfinish = () => {
245
+ this.finishedTime = this.time;
394
246
  if (!pseudoElement) {
395
- style.set(element, name, getFinalKeyframe(keyframes, transition));
396
- this.cancel();
247
+ const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
248
+ if (this.updateMotionValue) {
249
+ this.updateMotionValue(keyframe);
250
+ }
251
+ else {
252
+ /**
253
+ * If we can, we want to commit the final style as set by the user,
254
+ * rather than the computed keyframe value supplied by the animation.
255
+ */
256
+ setStyle(element, name, keyframe);
257
+ }
258
+ this.animation.cancel();
397
259
  }
260
+ this.notifyFinished();
398
261
  };
399
- /**
400
- * TODO: Check for VisualElement before using animation state.
401
- */
402
- animationMap.set(key, this);
403
262
  }
404
263
  play() {
264
+ if (this.isStopped)
265
+ return;
405
266
  this.animation.play();
267
+ if (this.state === "finished") {
268
+ this.updateFinished();
269
+ }
406
270
  }
407
271
  pause() {
408
272
  this.animation.pause();
409
273
  }
410
274
  complete() {
411
- this.animation.finish();
275
+ this.animation.finish?.();
412
276
  }
413
277
  cancel() {
414
278
  try {
415
279
  this.animation.cancel();
416
280
  }
417
281
  catch (e) { }
418
- this.removeAnimation();
419
282
  }
420
283
  stop() {
284
+ if (this.isStopped)
285
+ return;
286
+ this.isStopped = true;
421
287
  const { state } = this;
422
288
  if (state === "idle" || state === "finished") {
423
289
  return;
424
290
  }
425
- this.commitStyles();
426
- this.cancel();
291
+ if (this.updateMotionValue) {
292
+ this.updateMotionValue();
293
+ }
294
+ else {
295
+ this.commitStyles();
296
+ }
297
+ if (!this.isPseudoElement)
298
+ this.cancel();
427
299
  }
428
300
  /**
429
301
  * WAAPI doesn't natively have any interruption capabilities.
@@ -443,13 +315,14 @@ class NativeAnimation {
443
315
  }
444
316
  }
445
317
  get duration() {
446
- const duration = this.animation.effect?.getComputedTiming().duration || 0;
318
+ const duration = this.animation.effect?.getComputedTiming?.().duration || 0;
447
319
  return millisecondsToSeconds(Number(duration));
448
320
  }
449
321
  get time() {
450
322
  return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
451
323
  }
452
324
  set time(newTime) {
325
+ this.finishedTime = null;
453
326
  this.animation.currentTime = secondsToMilliseconds(newTime);
454
327
  }
455
328
  /**
@@ -460,38 +333,123 @@ class NativeAnimation {
460
333
  return this.animation.playbackRate;
461
334
  }
462
335
  set speed(newSpeed) {
336
+ // Allow backwards playback after finishing
337
+ if (newSpeed < 0)
338
+ this.finishedTime = null;
463
339
  this.animation.playbackRate = newSpeed;
464
340
  }
465
341
  get state() {
466
- return this.animation.playState;
342
+ return this.finishedTime !== null
343
+ ? "finished"
344
+ : this.animation.playState;
467
345
  }
468
346
  get startTime() {
469
347
  return Number(this.animation.startTime);
470
348
  }
471
- get finished() {
472
- return this.animation.finished;
349
+ set startTime(newStartTime) {
350
+ this.animation.startTime = newStartTime;
473
351
  }
474
- flatten() {
352
+ /**
353
+ * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
354
+ */
355
+ attachTimeline({ timeline, observe }) {
475
356
  if (this.allowFlatten) {
476
357
  this.animation.effect?.updateTiming({ easing: "linear" });
477
358
  }
359
+ this.animation.onfinish = null;
360
+ if (supportsScrollTimeline()) {
361
+ this.animation.timeline = timeline;
362
+ return noop;
363
+ }
364
+ else {
365
+ return observe(this);
366
+ }
367
+ }
368
+ }
369
+
370
+ class GroupAnimation {
371
+ constructor(animations) {
372
+ // Bound to accomadate common `return animation.stop` pattern
373
+ this.stop = () => this.runAll("stop");
374
+ this.animations = animations.filter(Boolean);
375
+ }
376
+ get finished() {
377
+ return Promise.all(this.animations.map((animation) => animation.finished));
478
378
  }
479
379
  /**
480
- * Attaches a timeline to the animation, for instance the `ScrollTimeline`.
380
+ * TODO: Filter out cancelled or stopped animations before returning
481
381
  */
382
+ getAll(propName) {
383
+ return this.animations[0][propName];
384
+ }
385
+ setAll(propName, newValue) {
386
+ for (let i = 0; i < this.animations.length; i++) {
387
+ this.animations[i][propName] = newValue;
388
+ }
389
+ }
482
390
  attachTimeline(timeline) {
483
- this.animation.timeline = timeline;
484
- this.animation.onfinish = null;
485
- return noop;
391
+ const subscriptions = this.animations.map((animation) => animation.attachTimeline(timeline));
392
+ return () => {
393
+ subscriptions.forEach((cancel, i) => {
394
+ cancel && cancel();
395
+ this.animations[i].stop();
396
+ });
397
+ };
486
398
  }
487
- /**
488
- * Allows the animation to be awaited.
489
- *
490
- * @deprecated Use `finished` instead.
491
- */
492
- then(onResolve, onReject) {
493
- return this.finished.then(onResolve).catch(onReject);
399
+ get time() {
400
+ return this.getAll("time");
401
+ }
402
+ set time(time) {
403
+ this.setAll("time", time);
404
+ }
405
+ get speed() {
406
+ return this.getAll("speed");
407
+ }
408
+ set speed(speed) {
409
+ this.setAll("speed", speed);
410
+ }
411
+ get state() {
412
+ return this.getAll("state");
494
413
  }
414
+ get startTime() {
415
+ return this.getAll("startTime");
416
+ }
417
+ get duration() {
418
+ let max = 0;
419
+ for (let i = 0; i < this.animations.length; i++) {
420
+ max = Math.max(max, this.animations[i].duration);
421
+ }
422
+ return max;
423
+ }
424
+ runAll(methodName) {
425
+ this.animations.forEach((controls) => controls[methodName]());
426
+ }
427
+ play() {
428
+ this.runAll("play");
429
+ }
430
+ pause() {
431
+ this.runAll("pause");
432
+ }
433
+ cancel() {
434
+ this.runAll("cancel");
435
+ }
436
+ complete() {
437
+ this.runAll("complete");
438
+ }
439
+ }
440
+
441
+ class GroupAnimationWithThen extends GroupAnimation {
442
+ then(onResolve, _onReject) {
443
+ return this.finished.finally(onResolve).then(() => { });
444
+ }
445
+ }
446
+
447
+ const animationMaps = new WeakMap();
448
+ const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`;
449
+ function getAnimationMap(element) {
450
+ const map = animationMaps.get(element) || new Map();
451
+ animationMaps.set(element, map);
452
+ return map;
495
453
  }
496
454
 
497
455
  function getValueTransition(transition, key) {
@@ -500,6 +458,52 @@ function getValueTransition(transition, key) {
500
458
  transition);
501
459
  }
502
460
 
461
+ const pxValues = new Set([
462
+ // Border props
463
+ "borderWidth",
464
+ "borderTopWidth",
465
+ "borderRightWidth",
466
+ "borderBottomWidth",
467
+ "borderLeftWidth",
468
+ "borderRadius",
469
+ "radius",
470
+ "borderTopLeftRadius",
471
+ "borderTopRightRadius",
472
+ "borderBottomRightRadius",
473
+ "borderBottomLeftRadius",
474
+ // Positioning props
475
+ "width",
476
+ "maxWidth",
477
+ "height",
478
+ "maxHeight",
479
+ "top",
480
+ "right",
481
+ "bottom",
482
+ "left",
483
+ // Spacing props
484
+ "padding",
485
+ "paddingTop",
486
+ "paddingRight",
487
+ "paddingBottom",
488
+ "paddingLeft",
489
+ "margin",
490
+ "marginTop",
491
+ "marginRight",
492
+ "marginBottom",
493
+ "marginLeft",
494
+ // Misc
495
+ "backgroundPositionX",
496
+ "backgroundPositionY",
497
+ ]);
498
+
499
+ function applyPxDefaults(keyframes, name) {
500
+ for (let i = 0; i < keyframes.length; i++) {
501
+ if (typeof keyframes[i] === "number" && pxValues.has(name)) {
502
+ keyframes[i] = keyframes[i] + "px";
503
+ }
504
+ }
505
+ }
506
+
503
507
  function resolveElements(elementOrSelector, scope, selectorCache) {
504
508
  if (elementOrSelector instanceof EventTarget) {
505
509
  return [elementOrSelector];
@@ -516,11 +520,38 @@ function resolveElements(elementOrSelector, scope, selectorCache) {
516
520
  return Array.from(elementOrSelector);
517
521
  }
518
522
 
523
+ function getComputedStyle(element, name) {
524
+ const computedStyle = window.getComputedStyle(element);
525
+ return isCSSVar(name)
526
+ ? computedStyle.getPropertyValue(name)
527
+ : computedStyle[name];
528
+ }
529
+
519
530
  function animateElements(elementOrSelector, keyframes, options, scope) {
520
531
  const elements = resolveElements(elementOrSelector, scope);
521
532
  const numElements = elements.length;
522
533
  invariant(Boolean(numElements), "No valid element provided.");
523
- const animations = [];
534
+ /**
535
+ * WAAPI doesn't support interrupting animations.
536
+ *
537
+ * Therefore, starting animations requires a three-step process:
538
+ * 1. Stop existing animations (write styles to DOM)
539
+ * 2. Resolve keyframes (read styles from DOM)
540
+ * 3. Create new animations (write styles to DOM)
541
+ *
542
+ * The hybrid `animate()` function uses AsyncAnimation to resolve
543
+ * keyframes before creating new animations, which removes style
544
+ * thrashing. Here, we have much stricter filesize constraints.
545
+ * Therefore we do this in a synchronous way that ensures that
546
+ * at least within `animate()` calls there is no style thrashing.
547
+ *
548
+ * In the motion-native-animate-mini-interrupt benchmark this
549
+ * was 80% faster than a single loop.
550
+ */
551
+ const animationDefinitions = [];
552
+ /**
553
+ * Step 1: Build options and stop existing animations (write)
554
+ */
524
555
  for (let i = 0; i < numElements; i++) {
525
556
  const element = elements[i];
526
557
  const elementTransition = { ...options };
@@ -531,21 +562,69 @@ function animateElements(elementOrSelector, keyframes, options, scope) {
531
562
  elementTransition.delay = elementTransition.delay(i, numElements);
532
563
  }
533
564
  for (const valueName in keyframes) {
534
- const valueKeyframes = keyframes[valueName];
565
+ let valueKeyframes = keyframes[valueName];
566
+ if (!Array.isArray(valueKeyframes)) {
567
+ valueKeyframes = [valueKeyframes];
568
+ }
535
569
  const valueOptions = {
536
570
  ...getValueTransition(elementTransition, valueName),
537
571
  };
538
572
  valueOptions.duration && (valueOptions.duration = secondsToMilliseconds(valueOptions.duration));
539
573
  valueOptions.delay && (valueOptions.delay = secondsToMilliseconds(valueOptions.delay));
540
- animations.push(new NativeAnimation({
541
- element,
542
- name: valueName,
543
- keyframes: valueKeyframes,
544
- transition: valueOptions,
545
- allowFlatten: !elementTransition.type && !elementTransition.ease,
546
- }));
574
+ /**
575
+ * If there's an existing animation playing on this element then stop it
576
+ * before creating a new one.
577
+ */
578
+ const map = getAnimationMap(element);
579
+ const key = animationMapKey(valueName, valueOptions.pseudoElement || "");
580
+ const currentAnimation = map.get(key);
581
+ currentAnimation && currentAnimation.stop();
582
+ animationDefinitions.push({
583
+ map,
584
+ key,
585
+ unresolvedKeyframes: valueKeyframes,
586
+ options: {
587
+ ...valueOptions,
588
+ element,
589
+ name: valueName,
590
+ allowFlatten: !elementTransition.type && !elementTransition.ease,
591
+ },
592
+ });
547
593
  }
548
594
  }
595
+ /**
596
+ * Step 2: Resolve keyframes (read)
597
+ */
598
+ for (let i = 0; i < animationDefinitions.length; i++) {
599
+ const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i];
600
+ const { element, name, pseudoElement } = animationOptions;
601
+ if (!pseudoElement && unresolvedKeyframes[0] === null) {
602
+ unresolvedKeyframes[0] = getComputedStyle(element, name);
603
+ }
604
+ fillWildcards(unresolvedKeyframes);
605
+ applyPxDefaults(unresolvedKeyframes, name);
606
+ /**
607
+ * If we only have one keyframe, explicitly read the initial keyframe
608
+ * from the computed style. This is to ensure consistency with WAAPI behaviour
609
+ * for restarting animations, for instance .play() after finish, when it
610
+ * has one vs two keyframes.
611
+ */
612
+ if (!pseudoElement && unresolvedKeyframes.length < 2) {
613
+ unresolvedKeyframes.unshift(getComputedStyle(element, name));
614
+ }
615
+ animationOptions.keyframes = unresolvedKeyframes;
616
+ }
617
+ /**
618
+ * Step 3: Create new animations (write)
619
+ */
620
+ const animations = [];
621
+ for (let i = 0; i < animationDefinitions.length; i++) {
622
+ const { map, key, options: animationOptions } = animationDefinitions[i];
623
+ const animation = new NativeAnimation(animationOptions);
624
+ map.set(key, animation);
625
+ animation.finished.finally(() => map.delete(key));
626
+ animations.push(animation);
627
+ }
549
628
  return animations;
550
629
  }
551
630