framer-motion 12.7.3 → 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 (155) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/client.js +1 -1
  3. package/dist/cjs/{create-DwAwaNot.js → create-C7kXmWbI.js} +99 -2828
  4. package/dist/cjs/dom-mini.js +82 -66
  5. package/dist/cjs/dom.js +264 -3000
  6. package/dist/cjs/index.js +163 -218
  7. package/dist/cjs/m.js +13 -170
  8. package/dist/cjs/mini.js +77 -9
  9. package/dist/dom-mini.js +1 -1
  10. package/dist/dom.d.ts +5 -94
  11. package/dist/dom.js +1 -1
  12. package/dist/es/animation/animate/sequence.mjs +1 -1
  13. package/dist/es/animation/animators/waapi/animate-elements.mjs +78 -10
  14. package/dist/es/animation/interfaces/motion-value.mjs +11 -30
  15. package/dist/es/animation/interfaces/visual-element-target.mjs +1 -2
  16. package/dist/es/animation/optimized-appear/store-id.mjs +1 -1
  17. package/dist/es/animation/sequence/create.mjs +2 -5
  18. package/dist/es/animation/sequence/utils/edit.mjs +2 -3
  19. package/dist/es/animation/utils/default-transitions.mjs +1 -1
  20. package/dist/es/animation/utils/stagger.mjs +1 -1
  21. package/dist/es/components/AnimatePresence/PresenceChild.mjs +26 -23
  22. package/dist/es/components/Reorder/utils/check-reorder.mjs +1 -1
  23. package/dist/es/dom.mjs +2 -18
  24. package/dist/es/gestures/drag/VisualElementDragControls.mjs +1 -3
  25. package/dist/es/gestures/drag/utils/constraints.mjs +2 -3
  26. package/dist/es/gestures/focus.mjs +1 -1
  27. package/dist/es/gestures/pan/PanSession.mjs +1 -2
  28. package/dist/es/index.mjs +3 -24
  29. package/dist/es/motion/utils/is-forced-motion-value.mjs +1 -1
  30. package/dist/es/projection/animation/mix-values.mjs +2 -4
  31. package/dist/es/projection/geometry/delta-apply.mjs +1 -1
  32. package/dist/es/projection/geometry/delta-calc.mjs +1 -1
  33. package/dist/es/projection/geometry/delta-remove.mjs +1 -2
  34. package/dist/es/projection/node/create-projection-node.mjs +3 -7
  35. package/dist/es/projection/styles/scale-border-radius.mjs +1 -1
  36. package/dist/es/projection/styles/scale-box-shadow.mjs +1 -2
  37. package/dist/es/projection/styles/scale-correction.mjs +1 -1
  38. package/dist/es/projection.mjs +1 -3
  39. package/dist/es/render/VisualElement.mjs +2 -9
  40. package/dist/es/render/dom/DOMVisualElement.mjs +1 -1
  41. package/dist/es/render/dom/scroll/attach-animation.mjs +17 -0
  42. package/dist/es/render/dom/scroll/attach-function.mjs +23 -0
  43. package/dist/es/render/dom/scroll/index.mjs +6 -82
  44. package/dist/es/render/dom/scroll/offsets/index.mjs +2 -3
  45. package/dist/es/render/dom/scroll/utils/get-timeline.mjs +29 -0
  46. package/dist/es/render/html/HTMLVisualElement.mjs +1 -3
  47. package/dist/es/render/html/utils/build-styles.mjs +1 -4
  48. package/dist/es/render/html/utils/build-transform.mjs +1 -3
  49. package/dist/es/render/svg/SVGVisualElement.mjs +1 -3
  50. package/dist/es/render/svg/config-motion.mjs +1 -2
  51. package/dist/es/render/svg/utils/path.mjs +1 -1
  52. package/dist/es/render/svg/utils/scrape-motion-values.mjs +1 -1
  53. package/dist/es/render/svg/utils/transform-origin.mjs +1 -1
  54. package/dist/es/render/utils/motion-values.mjs +1 -1
  55. package/dist/es/utils/delay.mjs +1 -1
  56. package/dist/es/utils/transform.mjs +1 -1
  57. package/dist/es/utils/use-cycle.mjs +1 -1
  58. package/dist/es/utils/use-instant-transition.mjs +4 -4
  59. package/dist/es/value/use-spring.mjs +2 -3
  60. package/dist/es/value/use-will-change/get-will-change-name.mjs +1 -2
  61. package/dist/framer-motion.dev.js +3881 -3419
  62. package/dist/framer-motion.js +1 -1
  63. package/dist/m.d.ts +3 -50
  64. package/dist/mini.js +1 -1
  65. package/dist/size-rollup-animate.js +1 -1
  66. package/dist/size-rollup-dom-animation-assets.js +1 -1
  67. package/dist/size-rollup-dom-animation.js +1 -1
  68. package/dist/size-rollup-dom-max-assets.js +1 -1
  69. package/dist/size-rollup-dom-max.js +1 -1
  70. package/dist/size-rollup-m.js +1 -1
  71. package/dist/size-rollup-motion.js +1 -1
  72. package/dist/size-rollup-scroll.js +1 -1
  73. package/dist/size-rollup-waapi-animate.js +1 -1
  74. package/dist/types/client.d.ts +4 -3
  75. package/dist/types/index.d.ts +56 -351
  76. package/dist/{types.d-B50aGbjN.d.ts → types.d-B1Voffvi.d.ts} +3 -138
  77. package/package.json +9 -9
  78. package/dist/es/animation/animators/AcceleratedAnimation.mjs +0 -319
  79. package/dist/es/animation/animators/BaseAnimation.mjs +0 -120
  80. package/dist/es/animation/animators/MainThreadAnimation.mjs +0 -394
  81. package/dist/es/animation/animators/drivers/driver-frameloop.mjs +0 -16
  82. package/dist/es/animation/animators/utils/accelerated-values.mjs +0 -14
  83. package/dist/es/animation/animators/utils/can-animate.mjs +0 -42
  84. package/dist/es/animation/animators/waapi/utils/supports-waapi.mjs +0 -5
  85. package/dist/es/animation/generators/inertia.mjs +0 -87
  86. package/dist/es/animation/generators/keyframes.mjs +0 -51
  87. package/dist/es/animation/generators/spring/defaults.mjs +0 -27
  88. package/dist/es/animation/generators/spring/find.mjs +0 -85
  89. package/dist/es/animation/generators/spring/index.mjs +0 -174
  90. package/dist/es/animation/generators/utils/velocity.mjs +0 -9
  91. package/dist/es/animation/utils/is-animatable.mjs +0 -30
  92. package/dist/es/animation/utils/is-none.mjs +0 -15
  93. package/dist/es/easing/anticipate.mjs +0 -5
  94. package/dist/es/easing/back.mjs +0 -9
  95. package/dist/es/easing/circ.mjs +0 -8
  96. package/dist/es/easing/cubic-bezier.mjs +0 -51
  97. package/dist/es/easing/ease.mjs +0 -7
  98. package/dist/es/easing/modifiers/mirror.mjs +0 -5
  99. package/dist/es/easing/modifiers/reverse.mjs +0 -5
  100. package/dist/es/easing/steps.mjs +0 -15
  101. package/dist/es/easing/utils/get-easing-for-segment.mjs +0 -8
  102. package/dist/es/easing/utils/is-easing-array.mjs +0 -5
  103. package/dist/es/easing/utils/map.mjs +0 -37
  104. package/dist/es/render/dom/DOMKeyframesResolver.mjs +0 -130
  105. package/dist/es/render/dom/scroll/observe.mjs +0 -18
  106. package/dist/es/render/dom/utils/css-variables-conversion.mjs +0 -42
  107. package/dist/es/render/dom/utils/is-css-variable.mjs +0 -15
  108. package/dist/es/render/dom/utils/unit-conversion.mjs +0 -36
  109. package/dist/es/render/dom/value-types/animatable-none.mjs +0 -15
  110. package/dist/es/render/dom/value-types/defaults.mjs +0 -30
  111. package/dist/es/render/dom/value-types/dimensions.mjs +0 -15
  112. package/dist/es/render/dom/value-types/find.mjs +0 -15
  113. package/dist/es/render/dom/value-types/get-as-type.mjs +0 -10
  114. package/dist/es/render/dom/value-types/number-browser.mjs +0 -41
  115. package/dist/es/render/dom/value-types/number.mjs +0 -18
  116. package/dist/es/render/dom/value-types/test.mjs +0 -6
  117. package/dist/es/render/dom/value-types/transform.mjs +0 -31
  118. package/dist/es/render/dom/value-types/type-auto.mjs +0 -9
  119. package/dist/es/render/dom/value-types/type-int.mjs +0 -8
  120. package/dist/es/render/html/utils/keys-position.mjs +0 -13
  121. package/dist/es/render/html/utils/keys-transform.mjs +0 -28
  122. package/dist/es/render/html/utils/make-none-animatable.mjs +0 -30
  123. package/dist/es/render/html/utils/parse-transform.mjs +0 -83
  124. package/dist/es/render/utils/KeyframesResolver.mjs +0 -163
  125. package/dist/es/utils/clamp.mjs +0 -9
  126. package/dist/es/utils/hsla-to-rgba.mjs +0 -42
  127. package/dist/es/utils/interpolate.mjs +0 -76
  128. package/dist/es/utils/is-numerical-string.mjs +0 -6
  129. package/dist/es/utils/is-zero-value-string.mjs +0 -6
  130. package/dist/es/utils/mix/color.mjs +0 -47
  131. package/dist/es/utils/mix/complex.mjs +0 -93
  132. package/dist/es/utils/mix/immediate.mjs +0 -5
  133. package/dist/es/utils/mix/index.mjs +0 -14
  134. package/dist/es/utils/mix/number.mjs +0 -26
  135. package/dist/es/utils/mix/visibility.mjs +0 -16
  136. package/dist/es/utils/offsets/default.mjs +0 -9
  137. package/dist/es/utils/offsets/fill.mjs +0 -12
  138. package/dist/es/utils/offsets/time.mjs +0 -5
  139. package/dist/es/utils/pipe.mjs +0 -11
  140. package/dist/es/utils/use-instant-transition-state.mjs +0 -5
  141. package/dist/es/utils/wrap.mjs +0 -6
  142. package/dist/es/value/types/color/hex.mjs +0 -40
  143. package/dist/es/value/types/color/hsla.mjs +0 -22
  144. package/dist/es/value/types/color/index.mjs +0 -27
  145. package/dist/es/value/types/color/rgba.mjs +0 -25
  146. package/dist/es/value/types/color/utils.mjs +0 -29
  147. package/dist/es/value/types/complex/filter.mjs +0 -30
  148. package/dist/es/value/types/complex/index.mjs +0 -91
  149. package/dist/es/value/types/numbers/index.mjs +0 -17
  150. package/dist/es/value/types/numbers/units.mjs +0 -17
  151. package/dist/es/value/types/utils/color-regex.mjs +0 -3
  152. package/dist/es/value/types/utils/float-regex.mjs +0 -3
  153. package/dist/es/value/types/utils/is-nullish.mjs +0 -5
  154. package/dist/es/value/types/utils/sanitize.mjs +0 -5
  155. package/dist/es/value/types/utils/single-color-regex.mjs +0 -3
package/dist/cjs/dom.js CHANGED
@@ -5,344 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var motionDom = require('motion-dom');
6
6
  var motionUtils = require('motion-utils');
7
7
 
8
- const clamp = (min, max, v) => {
9
- if (v > max)
10
- return max;
11
- if (v < min)
12
- return min;
13
- return v;
14
- };
15
-
16
- const velocitySampleDuration = 5; // ms
17
- function calcGeneratorVelocity(resolveValue, t, current) {
18
- const prevT = Math.max(t - velocitySampleDuration, 0);
19
- return motionUtils.velocityPerSecond(current - resolveValue(prevT), t - prevT);
20
- }
21
-
22
- const springDefaults = {
23
- // Default spring physics
24
- stiffness: 100,
25
- damping: 10,
26
- mass: 1.0,
27
- velocity: 0.0,
28
- // Default duration/bounce-based options
29
- duration: 800, // in ms
30
- bounce: 0.3,
31
- visualDuration: 0.3, // in seconds
32
- // Rest thresholds
33
- restSpeed: {
34
- granular: 0.01,
35
- default: 2,
36
- },
37
- restDelta: {
38
- granular: 0.005,
39
- default: 0.5,
40
- },
41
- // Limits
42
- minDuration: 0.01, // in seconds
43
- maxDuration: 10.0, // in seconds
44
- minDamping: 0.05,
45
- maxDamping: 1,
46
- };
47
-
48
- const safeMin = 0.001;
49
- function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
50
- let envelope;
51
- let derivative;
52
- motionUtils.warning(duration <= motionUtils.secondsToMilliseconds(springDefaults.maxDuration), "Spring duration must be 10 seconds or less");
53
- let dampingRatio = 1 - bounce;
54
- /**
55
- * Restrict dampingRatio and duration to within acceptable ranges.
56
- */
57
- dampingRatio = clamp(springDefaults.minDamping, springDefaults.maxDamping, dampingRatio);
58
- duration = clamp(springDefaults.minDuration, springDefaults.maxDuration, motionUtils.millisecondsToSeconds(duration));
59
- if (dampingRatio < 1) {
60
- /**
61
- * Underdamped spring
62
- */
63
- envelope = (undampedFreq) => {
64
- const exponentialDecay = undampedFreq * dampingRatio;
65
- const delta = exponentialDecay * duration;
66
- const a = exponentialDecay - velocity;
67
- const b = calcAngularFreq(undampedFreq, dampingRatio);
68
- const c = Math.exp(-delta);
69
- return safeMin - (a / b) * c;
70
- };
71
- derivative = (undampedFreq) => {
72
- const exponentialDecay = undampedFreq * dampingRatio;
73
- const delta = exponentialDecay * duration;
74
- const d = delta * velocity + velocity;
75
- const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
76
- const f = Math.exp(-delta);
77
- const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
78
- const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
79
- return (factor * ((d - e) * f)) / g;
80
- };
81
- }
82
- else {
83
- /**
84
- * Critically-damped spring
85
- */
86
- envelope = (undampedFreq) => {
87
- const a = Math.exp(-undampedFreq * duration);
88
- const b = (undampedFreq - velocity) * duration + 1;
89
- return -safeMin + a * b;
90
- };
91
- derivative = (undampedFreq) => {
92
- const a = Math.exp(-undampedFreq * duration);
93
- const b = (velocity - undampedFreq) * (duration * duration);
94
- return a * b;
95
- };
96
- }
97
- const initialGuess = 5 / duration;
98
- const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
99
- duration = motionUtils.secondsToMilliseconds(duration);
100
- if (isNaN(undampedFreq)) {
101
- return {
102
- stiffness: springDefaults.stiffness,
103
- damping: springDefaults.damping,
104
- duration,
105
- };
106
- }
107
- else {
108
- const stiffness = Math.pow(undampedFreq, 2) * mass;
109
- return {
110
- stiffness,
111
- damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
112
- duration,
113
- };
114
- }
115
- }
116
- const rootIterations = 12;
117
- function approximateRoot(envelope, derivative, initialGuess) {
118
- let result = initialGuess;
119
- for (let i = 1; i < rootIterations; i++) {
120
- result = result - envelope(result) / derivative(result);
121
- }
122
- return result;
123
- }
124
- function calcAngularFreq(undampedFreq, dampingRatio) {
125
- return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
126
- }
127
-
128
- const durationKeys = ["duration", "bounce"];
129
- const physicsKeys = ["stiffness", "damping", "mass"];
130
- function isSpringType(options, keys) {
131
- return keys.some((key) => options[key] !== undefined);
132
- }
133
- function getSpringOptions(options) {
134
- let springOptions = {
135
- velocity: springDefaults.velocity,
136
- stiffness: springDefaults.stiffness,
137
- damping: springDefaults.damping,
138
- mass: springDefaults.mass,
139
- isResolvedFromDuration: false,
140
- ...options,
141
- };
142
- // stiffness/damping/mass overrides duration/bounce
143
- if (!isSpringType(options, physicsKeys) &&
144
- isSpringType(options, durationKeys)) {
145
- if (options.visualDuration) {
146
- const visualDuration = options.visualDuration;
147
- const root = (2 * Math.PI) / (visualDuration * 1.2);
148
- const stiffness = root * root;
149
- const damping = 2 *
150
- clamp(0.05, 1, 1 - (options.bounce || 0)) *
151
- Math.sqrt(stiffness);
152
- springOptions = {
153
- ...springOptions,
154
- mass: springDefaults.mass,
155
- stiffness,
156
- damping,
157
- };
158
- }
159
- else {
160
- const derived = findSpring(options);
161
- springOptions = {
162
- ...springOptions,
163
- ...derived,
164
- mass: springDefaults.mass,
165
- };
166
- springOptions.isResolvedFromDuration = true;
167
- }
168
- }
169
- return springOptions;
170
- }
171
- function spring(optionsOrVisualDuration = springDefaults.visualDuration, bounce = springDefaults.bounce) {
172
- const options = typeof optionsOrVisualDuration !== "object"
173
- ? {
174
- visualDuration: optionsOrVisualDuration,
175
- keyframes: [0, 1],
176
- bounce,
177
- }
178
- : optionsOrVisualDuration;
179
- let { restSpeed, restDelta } = options;
180
- const origin = options.keyframes[0];
181
- const target = options.keyframes[options.keyframes.length - 1];
182
- /**
183
- * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
184
- * to reduce GC during animation.
185
- */
186
- const state = { done: false, value: origin };
187
- const { stiffness, damping, mass, duration, velocity, isResolvedFromDuration, } = getSpringOptions({
188
- ...options,
189
- velocity: -motionUtils.millisecondsToSeconds(options.velocity || 0),
190
- });
191
- const initialVelocity = velocity || 0.0;
192
- const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
193
- const initialDelta = target - origin;
194
- const undampedAngularFreq = motionUtils.millisecondsToSeconds(Math.sqrt(stiffness / mass));
195
- /**
196
- * If we're working on a granular scale, use smaller defaults for determining
197
- * when the spring is finished.
198
- *
199
- * These defaults have been selected emprically based on what strikes a good
200
- * ratio between feeling good and finishing as soon as changes are imperceptible.
201
- */
202
- const isGranularScale = Math.abs(initialDelta) < 5;
203
- restSpeed || (restSpeed = isGranularScale
204
- ? springDefaults.restSpeed.granular
205
- : springDefaults.restSpeed.default);
206
- restDelta || (restDelta = isGranularScale
207
- ? springDefaults.restDelta.granular
208
- : springDefaults.restDelta.default);
209
- let resolveSpring;
210
- if (dampingRatio < 1) {
211
- const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
212
- // Underdamped spring
213
- resolveSpring = (t) => {
214
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
215
- return (target -
216
- envelope *
217
- (((initialVelocity +
218
- dampingRatio * undampedAngularFreq * initialDelta) /
219
- angularFreq) *
220
- Math.sin(angularFreq * t) +
221
- initialDelta * Math.cos(angularFreq * t)));
222
- };
223
- }
224
- else if (dampingRatio === 1) {
225
- // Critically damped spring
226
- resolveSpring = (t) => target -
227
- Math.exp(-undampedAngularFreq * t) *
228
- (initialDelta +
229
- (initialVelocity + undampedAngularFreq * initialDelta) * t);
230
- }
231
- else {
232
- // Overdamped spring
233
- const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
234
- resolveSpring = (t) => {
235
- const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
236
- // When performing sinh or cosh values can hit Infinity so we cap them here
237
- const freqForT = Math.min(dampedAngularFreq * t, 300);
238
- return (target -
239
- (envelope *
240
- ((initialVelocity +
241
- dampingRatio * undampedAngularFreq * initialDelta) *
242
- Math.sinh(freqForT) +
243
- dampedAngularFreq *
244
- initialDelta *
245
- Math.cosh(freqForT))) /
246
- dampedAngularFreq);
247
- };
248
- }
249
- const generator = {
250
- calculatedDuration: isResolvedFromDuration ? duration || null : null,
251
- next: (t) => {
252
- const current = resolveSpring(t);
253
- if (!isResolvedFromDuration) {
254
- let currentVelocity = 0.0;
255
- /**
256
- * We only need to calculate velocity for under-damped springs
257
- * as over- and critically-damped springs can't overshoot, so
258
- * checking only for displacement is enough.
259
- */
260
- if (dampingRatio < 1) {
261
- currentVelocity =
262
- t === 0
263
- ? motionUtils.secondsToMilliseconds(initialVelocity)
264
- : calcGeneratorVelocity(resolveSpring, t, current);
265
- }
266
- const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
267
- const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
268
- state.done =
269
- isBelowVelocityThreshold && isBelowDisplacementThreshold;
270
- }
271
- else {
272
- state.done = t >= duration;
273
- }
274
- state.value = state.done ? target : current;
275
- return state;
276
- },
277
- toString: () => {
278
- const calculatedDuration = Math.min(motionDom.calcGeneratorDuration(generator), motionDom.maxGeneratorDuration);
279
- const easing = motionDom.generateLinearEasing((progress) => generator.next(calculatedDuration * progress).value, calculatedDuration, 30);
280
- return calculatedDuration + "ms " + easing;
281
- },
282
- toTransition: () => { },
283
- };
284
- return generator;
285
- }
286
- spring.applyToOptions = (options) => {
287
- const generatorOptions = motionDom.createGeneratorEasing(options, 100, spring);
288
- options.ease = motionDom.supportsLinearEasing() ? generatorOptions.ease : "easeOut";
289
- options.duration = motionUtils.secondsToMilliseconds(generatorOptions.duration);
290
- options.type = "keyframes";
291
- return options;
292
- };
293
-
294
- const wrap = (min, max, v) => {
295
- const rangeSize = max - min;
296
- return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
297
- };
298
-
299
- const isEasingArray = (ease) => {
300
- return Array.isArray(ease) && typeof ease[0] !== "number";
301
- };
302
-
303
- function getEasingForSegment(easing, i) {
304
- return isEasingArray(easing) ? easing[wrap(0, easing.length, i)] : easing;
305
- }
306
-
307
- /*
308
- Value in range from progress
309
-
310
- Given a lower limit and an upper limit, we return the value within
311
- that range as expressed by progress (usually a number from 0 to 1)
312
-
313
- So progress = 0.5 would change
314
-
315
- from -------- to
316
-
317
- to
318
-
319
- from ---- to
320
-
321
- E.g. from = 10, to = 20, progress = 0.5 => 15
322
-
323
- @param [number]: Lower limit of range
324
- @param [number]: Upper limit of range
325
- @param [number]: The progress between lower and upper limits expressed 0-1
326
- @return [number]: Value as calculated from progress within range (not limited within range)
327
- */
328
- const mixNumber$1 = (from, to, progress) => {
329
- return from + (to - from) * progress;
330
- };
331
-
332
- function fillOffset(offset, remaining) {
333
- const min = offset[offset.length - 1];
334
- for (let i = 1; i <= remaining; i++) {
335
- const offsetProgress = motionUtils.progress(0, remaining, i);
336
- offset.push(mixNumber$1(min, 1, offsetProgress));
337
- }
338
- }
339
-
340
- function defaultOffset$1(arr) {
341
- const offset = [0];
342
- fillOffset(offset, arr.length - 1);
343
- return offset;
344
- }
345
-
346
8
  const isMotionValue = (value) => Boolean(value && value.getVelocity);
347
9
 
348
10
  function isDOMKeyframes(keyframes) {
@@ -407,8 +69,8 @@ function addKeyframes(sequence, keyframes, easing, offset, startTime, endTime) {
407
69
  for (let i = 0; i < keyframes.length; i++) {
408
70
  sequence.push({
409
71
  value: keyframes[i],
410
- at: mixNumber$1(startTime, endTime, offset[i]),
411
- easing: getEasingForSegment(easing, i),
72
+ at: motionDom.mixNumber(startTime, endTime, offset[i]),
73
+ easing: motionUtils.getEasingForSegment(easing, i),
412
74
  });
413
75
  }
414
76
  }
@@ -482,7 +144,7 @@ function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...seq
482
144
  let maxDuration = 0;
483
145
  const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
484
146
  const valueKeyframesAsList = keyframesAsList(valueKeyframes);
485
- const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
147
+ const { delay = 0, times = motionDom.defaultOffset(valueKeyframesAsList), type = "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
486
148
  let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
487
149
  /**
488
150
  * Resolve stagger() if defined.
@@ -530,7 +192,7 @@ function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...seq
530
192
  * Fill out if offset if fewer offsets than keyframes
531
193
  */
532
194
  const remainder = times.length - valueKeyframesAsList.length;
533
- remainder > 0 && fillOffset(times, remainder);
195
+ remainder > 0 && motionDom.fillOffset(times, remainder);
534
196
  /**
535
197
  * If only one value has been set, ie [1], push a null to the start of
536
198
  * the keyframe array. This will let us mark a keyframe at this point
@@ -554,7 +216,7 @@ function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...seq
554
216
  times.push(originalTimes[keyframeIndex] + (repeatIndex + 1));
555
217
  ease.push(keyframeIndex === 0
556
218
  ? "linear"
557
- : getEasingForSegment(originalEase, keyframeIndex - 1));
219
+ : motionUtils.getEasingForSegment(originalEase, keyframeIndex - 1));
558
220
  }
559
221
  }
560
222
  normalizeTimes(times, repeat);
@@ -675,2446 +337,124 @@ function getValueTransition(transition, key) {
675
337
  : { ...transition };
676
338
  }
677
339
  const isNumber = (keyframe) => typeof keyframe === "number";
678
- const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
679
-
680
- const visualElementStore = new WeakMap();
681
-
682
- /**
683
- * Generate a list of every possible transform key.
684
- */
685
- const transformPropOrder = [
686
- "transformPerspective",
687
- "x",
688
- "y",
689
- "z",
690
- "translateX",
691
- "translateY",
692
- "translateZ",
693
- "scale",
694
- "scaleX",
695
- "scaleY",
696
- "rotate",
697
- "rotateX",
698
- "rotateY",
699
- "rotateZ",
700
- "skew",
701
- "skewX",
702
- "skewY",
703
- ];
704
- /**
705
- * A quick lookup for transform props.
706
- */
707
- const transformProps = new Set(transformPropOrder);
708
-
709
- const positionalKeys = new Set([
710
- "width",
711
- "height",
712
- "top",
713
- "left",
714
- "right",
715
- "bottom",
716
- ...transformPropOrder,
717
- ]);
718
-
719
- const isKeyframesTarget = (v) => {
720
- return Array.isArray(v);
721
- };
722
-
723
- const resolveFinalValueInKeyframes = (v) => {
724
- // TODO maybe throw if v.length - 1 is placeholder token?
725
- return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
726
- };
727
-
728
- function getValueState(visualElement) {
729
- const state = [{}, {}];
730
- visualElement?.values.forEach((value, key) => {
731
- state[0][key] = value.get();
732
- state[1][key] = value.getVelocity();
733
- });
734
- return state;
735
- }
736
- function resolveVariantFromProps(props, definition, custom, visualElement) {
737
- /**
738
- * If the variant definition is a function, resolve.
739
- */
740
- if (typeof definition === "function") {
741
- const [current, velocity] = getValueState(visualElement);
742
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
743
- }
744
- /**
745
- * If the variant definition is a variant label, or
746
- * the function returned a variant label, resolve.
747
- */
748
- if (typeof definition === "string") {
749
- definition = props.variants && props.variants[definition];
750
- }
751
- /**
752
- * At this point we've resolved both functions and variant labels,
753
- * but the resolved variant label might itself have been a function.
754
- * If so, resolve. This can only have returned a valid target object.
755
- */
756
- if (typeof definition === "function") {
757
- const [current, velocity] = getValueState(visualElement);
758
- definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
759
- }
760
- return definition;
761
- }
762
-
763
- function resolveVariant(visualElement, definition, custom) {
764
- const props = visualElement.getProps();
765
- return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
766
- }
767
-
768
- /**
769
- * Set VisualElement's MotionValue, creating a new MotionValue for it if
770
- * it doesn't exist.
771
- */
772
- function setMotionValue(visualElement, key, value) {
773
- if (visualElement.hasValue(key)) {
774
- visualElement.getValue(key).set(value);
775
- }
776
- else {
777
- visualElement.addValue(key, motionDom.motionValue(value));
778
- }
779
- }
780
- function setTarget(visualElement, definition) {
781
- const resolved = resolveVariant(visualElement, definition);
782
- let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
783
- target = { ...target, ...transitionEnd };
784
- for (const key in target) {
785
- const value = resolveFinalValueInKeyframes(target[key]);
786
- setMotionValue(visualElement, key, value);
787
- }
788
- }
789
-
790
- function isWillChangeMotionValue(value) {
791
- return Boolean(isMotionValue(value) && value.add);
792
- }
793
-
794
- function addValueToWillChange(visualElement, key) {
795
- const willChange = visualElement.getValue("willChange");
796
- /**
797
- * It could be that a user has set willChange to a regular MotionValue,
798
- * in which case we can't add the value to it.
799
- */
800
- if (isWillChangeMotionValue(willChange)) {
801
- return willChange.add(key);
802
- }
803
- else if (!willChange && motionUtils.MotionGlobalConfig.WillChange) {
804
- const newWillChange = new motionUtils.MotionGlobalConfig.WillChange("auto");
805
- visualElement.addValue("willChange", newWillChange);
806
- newWillChange.add(key);
807
- }
808
- }
809
-
810
- /**
811
- * Convert camelCase to dash-case properties.
812
- */
813
- const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
814
-
815
- const optimizedAppearDataId = "framerAppearId";
816
- const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
817
-
818
- function getOptimisedAppearId(visualElement) {
819
- return visualElement.props[optimizedAppearDataAttribute];
820
- }
821
-
822
- /*
823
- Bezier function generator
824
- This has been modified from Gaëtan Renaudeau's BezierEasing
825
- https://github.com/gre/bezier-easing/blob/master/src/index.js
826
- https://github.com/gre/bezier-easing/blob/master/LICENSE
827
-
828
- I've removed the newtonRaphsonIterate algo because in benchmarking it
829
- wasn't noticiably faster than binarySubdivision, indeed removing it
830
- usually improved times, depending on the curve.
831
- I also removed the lookup table, as for the added bundle size and loop we're
832
- only cutting ~4 or so subdivision iterations. I bumped the max iterations up
833
- to 12 to compensate and this still tended to be faster for no perceivable
834
- loss in accuracy.
835
- Usage
836
- const easeOut = cubicBezier(.17,.67,.83,.67);
837
- const x = easeOut(0.5); // returns 0.627...
838
- */
839
- // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
840
- const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
841
- t;
842
- const subdivisionPrecision = 0.0000001;
843
- const subdivisionMaxIterations = 12;
844
- function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
845
- let currentX;
846
- let currentT;
847
- let i = 0;
848
- do {
849
- currentT = lowerBound + (upperBound - lowerBound) / 2.0;
850
- currentX = calcBezier(currentT, mX1, mX2) - x;
851
- if (currentX > 0.0) {
852
- upperBound = currentT;
853
- }
854
- else {
855
- lowerBound = currentT;
856
- }
857
- } while (Math.abs(currentX) > subdivisionPrecision &&
858
- ++i < subdivisionMaxIterations);
859
- return currentT;
860
- }
861
- function cubicBezier(mX1, mY1, mX2, mY2) {
862
- // If this is a linear gradient, return linear easing
863
- if (mX1 === mY1 && mX2 === mY2)
864
- return motionUtils.noop;
865
- const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
866
- // If animation is at start/end, return t without easing
867
- return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
868
- }
869
-
870
- // Accepts an easing function and returns a new one that outputs mirrored values for
871
- // the second half of the animation. Turns easeIn into easeInOut.
872
- const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
873
-
874
- // Accepts an easing function and returns a new one that outputs reversed values.
875
- // Turns easeIn into easeOut.
876
- const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
877
-
878
- const backOut = /*@__PURE__*/ cubicBezier(0.33, 1.53, 0.69, 0.99);
879
- const backIn = /*@__PURE__*/ reverseEasing(backOut);
880
- const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
881
-
882
- const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
883
-
884
- const circIn = (p) => 1 - Math.sin(Math.acos(p));
885
- const circOut = reverseEasing(circIn);
886
- const circInOut = mirrorEasing(circIn);
887
-
888
- /**
889
- * Check if the value is a zero value string like "0px" or "0%"
890
- */
891
- const isZeroValueString = (v) => /^0[^.\s]+$/u.test(v);
892
-
893
- function isNone(value) {
894
- if (typeof value === "number") {
895
- return value === 0;
896
- }
897
- else if (value !== null) {
898
- return value === "none" || value === "0" || isZeroValueString(value);
899
- }
900
- else {
901
- return true;
902
- }
903
- }
904
-
905
- const number = {
906
- test: (v) => typeof v === "number",
907
- parse: parseFloat,
908
- transform: (v) => v,
909
- };
910
- const alpha = {
911
- ...number,
912
- transform: (v) => clamp(0, 1, v),
913
- };
914
- const scale = {
915
- ...number,
916
- default: 1,
917
- };
918
-
919
- // If this number is a decimal, make it just five decimal places
920
- // to avoid exponents
921
- const sanitize = (v) => Math.round(v * 100000) / 100000;
922
-
923
- const floatRegex = /-?(?:\d+(?:\.\d+)?|\.\d+)/gu;
924
-
925
- function isNullish(v) {
926
- return v == null;
927
- }
928
-
929
- const singleColorRegex = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu;
930
-
931
- /**
932
- * Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
933
- * but false if a number or multiple colors
934
- */
935
- const isColorString = (type, testProp) => (v) => {
936
- return Boolean((typeof v === "string" &&
937
- singleColorRegex.test(v) &&
938
- v.startsWith(type)) ||
939
- (testProp &&
940
- !isNullish(v) &&
941
- Object.prototype.hasOwnProperty.call(v, testProp)));
942
- };
943
- const splitColor = (aName, bName, cName) => (v) => {
944
- if (typeof v !== "string")
945
- return v;
946
- const [a, b, c, alpha] = v.match(floatRegex);
947
- return {
948
- [aName]: parseFloat(a),
949
- [bName]: parseFloat(b),
950
- [cName]: parseFloat(c),
951
- alpha: alpha !== undefined ? parseFloat(alpha) : 1,
952
- };
953
- };
954
-
955
- const clampRgbUnit = (v) => clamp(0, 255, v);
956
- const rgbUnit = {
957
- ...number,
958
- transform: (v) => Math.round(clampRgbUnit(v)),
959
- };
960
- const rgba = {
961
- test: /*@__PURE__*/ isColorString("rgb", "red"),
962
- parse: /*@__PURE__*/ splitColor("red", "green", "blue"),
963
- transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
964
- rgbUnit.transform(red) +
965
- ", " +
966
- rgbUnit.transform(green) +
967
- ", " +
968
- rgbUnit.transform(blue) +
969
- ", " +
970
- sanitize(alpha.transform(alpha$1)) +
971
- ")",
972
- };
973
-
974
- function parseHex(v) {
975
- let r = "";
976
- let g = "";
977
- let b = "";
978
- let a = "";
979
- // If we have 6 characters, ie #FF0000
980
- if (v.length > 5) {
981
- r = v.substring(1, 3);
982
- g = v.substring(3, 5);
983
- b = v.substring(5, 7);
984
- a = v.substring(7, 9);
985
- // Or we have 3 characters, ie #F00
986
- }
987
- else {
988
- r = v.substring(1, 2);
989
- g = v.substring(2, 3);
990
- b = v.substring(3, 4);
991
- a = v.substring(4, 5);
992
- r += r;
993
- g += g;
994
- b += b;
995
- a += a;
996
- }
997
- return {
998
- red: parseInt(r, 16),
999
- green: parseInt(g, 16),
1000
- blue: parseInt(b, 16),
1001
- alpha: a ? parseInt(a, 16) / 255 : 1,
1002
- };
1003
- }
1004
- const hex = {
1005
- test: /*@__PURE__*/ isColorString("#"),
1006
- parse: parseHex,
1007
- transform: rgba.transform,
1008
- };
1009
-
1010
- const createUnitType = (unit) => ({
1011
- test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
1012
- parse: parseFloat,
1013
- transform: (v) => `${v}${unit}`,
1014
- });
1015
- const degrees = /*@__PURE__*/ createUnitType("deg");
1016
- const percent = /*@__PURE__*/ createUnitType("%");
1017
- const px = /*@__PURE__*/ createUnitType("px");
1018
- const vh = /*@__PURE__*/ createUnitType("vh");
1019
- const vw = /*@__PURE__*/ createUnitType("vw");
1020
- const progressPercentage = {
1021
- ...percent,
1022
- parse: (v) => percent.parse(v) / 100,
1023
- transform: (v) => percent.transform(v * 100),
1024
- };
1025
-
1026
- const hsla = {
1027
- test: /*@__PURE__*/ isColorString("hsl", "hue"),
1028
- parse: /*@__PURE__*/ splitColor("hue", "saturation", "lightness"),
1029
- transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
1030
- return ("hsla(" +
1031
- Math.round(hue) +
1032
- ", " +
1033
- percent.transform(sanitize(saturation)) +
1034
- ", " +
1035
- percent.transform(sanitize(lightness)) +
1036
- ", " +
1037
- sanitize(alpha.transform(alpha$1)) +
1038
- ")");
1039
- },
1040
- };
1041
-
1042
- const color = {
1043
- test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
1044
- parse: (v) => {
1045
- if (rgba.test(v)) {
1046
- return rgba.parse(v);
1047
- }
1048
- else if (hsla.test(v)) {
1049
- return hsla.parse(v);
1050
- }
1051
- else {
1052
- return hex.parse(v);
1053
- }
1054
- },
1055
- transform: (v) => {
1056
- return typeof v === "string"
1057
- ? v
1058
- : v.hasOwnProperty("red")
1059
- ? rgba.transform(v)
1060
- : hsla.transform(v);
1061
- },
1062
- };
1063
-
1064
- const colorRegex = /(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;
1065
-
1066
- function test(v) {
1067
- return (isNaN(v) &&
1068
- typeof v === "string" &&
1069
- (v.match(floatRegex)?.length || 0) +
1070
- (v.match(colorRegex)?.length || 0) >
1071
- 0);
1072
- }
1073
- const NUMBER_TOKEN = "number";
1074
- const COLOR_TOKEN = "color";
1075
- const VAR_TOKEN = "var";
1076
- const VAR_FUNCTION_TOKEN = "var(";
1077
- const SPLIT_TOKEN = "${}";
1078
- // this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex`
1079
- const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;
1080
- function analyseComplexValue(value) {
1081
- const originalValue = value.toString();
1082
- const values = [];
1083
- const indexes = {
1084
- color: [],
1085
- number: [],
1086
- var: [],
1087
- };
1088
- const types = [];
1089
- let i = 0;
1090
- const tokenised = originalValue.replace(complexRegex, (parsedValue) => {
1091
- if (color.test(parsedValue)) {
1092
- indexes.color.push(i);
1093
- types.push(COLOR_TOKEN);
1094
- values.push(color.parse(parsedValue));
1095
- }
1096
- else if (parsedValue.startsWith(VAR_FUNCTION_TOKEN)) {
1097
- indexes.var.push(i);
1098
- types.push(VAR_TOKEN);
1099
- values.push(parsedValue);
1100
- }
1101
- else {
1102
- indexes.number.push(i);
1103
- types.push(NUMBER_TOKEN);
1104
- values.push(parseFloat(parsedValue));
1105
- }
1106
- ++i;
1107
- return SPLIT_TOKEN;
1108
- });
1109
- const split = tokenised.split(SPLIT_TOKEN);
1110
- return { values, split, indexes, types };
1111
- }
1112
- function parseComplexValue(v) {
1113
- return analyseComplexValue(v).values;
1114
- }
1115
- function createTransformer(source) {
1116
- const { split, types } = analyseComplexValue(source);
1117
- const numSections = split.length;
1118
- return (v) => {
1119
- let output = "";
1120
- for (let i = 0; i < numSections; i++) {
1121
- output += split[i];
1122
- if (v[i] !== undefined) {
1123
- const type = types[i];
1124
- if (type === NUMBER_TOKEN) {
1125
- output += sanitize(v[i]);
1126
- }
1127
- else if (type === COLOR_TOKEN) {
1128
- output += color.transform(v[i]);
1129
- }
1130
- else {
1131
- output += v[i];
1132
- }
1133
- }
1134
- }
1135
- return output;
1136
- };
1137
- }
1138
- const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
1139
- function getAnimatableNone$1(v) {
1140
- const parsed = parseComplexValue(v);
1141
- const transformer = createTransformer(v);
1142
- return transformer(parsed.map(convertNumbersToZero));
1143
- }
1144
- const complex = {
1145
- test,
1146
- parse: parseComplexValue,
1147
- createTransformer,
1148
- getAnimatableNone: getAnimatableNone$1,
1149
- };
1150
-
1151
- /**
1152
- * Properties that should default to 1 or 100%
1153
- */
1154
- const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
1155
- function applyDefaultFilter(v) {
1156
- const [name, value] = v.slice(0, -1).split("(");
1157
- if (name === "drop-shadow")
1158
- return v;
1159
- const [number] = value.match(floatRegex) || [];
1160
- if (!number)
1161
- return v;
1162
- const unit = value.replace(number, "");
1163
- let defaultValue = maxDefaults.has(name) ? 1 : 0;
1164
- if (number !== value)
1165
- defaultValue *= 100;
1166
- return name + "(" + defaultValue + unit + ")";
1167
- }
1168
- const functionRegex = /\b([a-z-]*)\(.*?\)/gu;
1169
- const filter = {
1170
- ...complex,
1171
- getAnimatableNone: (v) => {
1172
- const functions = v.match(functionRegex);
1173
- return functions ? functions.map(applyDefaultFilter).join(" ") : v;
1174
- },
1175
- };
1176
-
1177
- const browserNumberValueTypes = {
1178
- // Border props
1179
- borderWidth: px,
1180
- borderTopWidth: px,
1181
- borderRightWidth: px,
1182
- borderBottomWidth: px,
1183
- borderLeftWidth: px,
1184
- borderRadius: px,
1185
- radius: px,
1186
- borderTopLeftRadius: px,
1187
- borderTopRightRadius: px,
1188
- borderBottomRightRadius: px,
1189
- borderBottomLeftRadius: px,
1190
- // Positioning props
1191
- width: px,
1192
- maxWidth: px,
1193
- height: px,
1194
- maxHeight: px,
1195
- top: px,
1196
- right: px,
1197
- bottom: px,
1198
- left: px,
1199
- // Spacing props
1200
- padding: px,
1201
- paddingTop: px,
1202
- paddingRight: px,
1203
- paddingBottom: px,
1204
- paddingLeft: px,
1205
- margin: px,
1206
- marginTop: px,
1207
- marginRight: px,
1208
- marginBottom: px,
1209
- marginLeft: px,
1210
- // Misc
1211
- backgroundPositionX: px,
1212
- backgroundPositionY: px,
1213
- };
1214
-
1215
- const transformValueTypes = {
1216
- rotate: degrees,
1217
- rotateX: degrees,
1218
- rotateY: degrees,
1219
- rotateZ: degrees,
1220
- scale,
1221
- scaleX: scale,
1222
- scaleY: scale,
1223
- scaleZ: scale,
1224
- skew: degrees,
1225
- skewX: degrees,
1226
- skewY: degrees,
1227
- distance: px,
1228
- translateX: px,
1229
- translateY: px,
1230
- translateZ: px,
1231
- x: px,
1232
- y: px,
1233
- z: px,
1234
- perspective: px,
1235
- transformPerspective: px,
1236
- opacity: alpha,
1237
- originX: progressPercentage,
1238
- originY: progressPercentage,
1239
- originZ: px,
1240
- };
1241
-
1242
- const int = {
1243
- ...number,
1244
- transform: Math.round,
1245
- };
1246
-
1247
- const numberValueTypes = {
1248
- ...browserNumberValueTypes,
1249
- ...transformValueTypes,
1250
- zIndex: int,
1251
- size: px,
1252
- // SVG
1253
- fillOpacity: alpha,
1254
- strokeOpacity: alpha,
1255
- numOctaves: int,
1256
- };
1257
-
1258
- /**
1259
- * A map of default value types for common values
1260
- */
1261
- const defaultValueTypes = {
1262
- ...numberValueTypes,
1263
- // Color props
1264
- color,
1265
- backgroundColor: color,
1266
- outlineColor: color,
1267
- fill: color,
1268
- stroke: color,
1269
- // Border props
1270
- borderColor: color,
1271
- borderTopColor: color,
1272
- borderRightColor: color,
1273
- borderBottomColor: color,
1274
- borderLeftColor: color,
1275
- filter,
1276
- WebkitFilter: filter,
1277
- };
1278
- /**
1279
- * Gets the default ValueType for the provided value key
1280
- */
1281
- const getDefaultValueType = (key) => defaultValueTypes[key];
1282
-
1283
- function getAnimatableNone(key, value) {
1284
- let defaultValueType = getDefaultValueType(key);
1285
- if (defaultValueType !== filter)
1286
- defaultValueType = complex;
1287
- // If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
1288
- return defaultValueType.getAnimatableNone
1289
- ? defaultValueType.getAnimatableNone(value)
1290
- : undefined;
1291
- }
1292
-
1293
- /**
1294
- * If we encounter keyframes like "none" or "0" and we also have keyframes like
1295
- * "#fff" or "200px 200px" we want to find a keyframe to serve as a template for
1296
- * the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
1297
- * zero equivalents, i.e. "#fff0" or "0px 0px".
1298
- */
1299
- const invalidTemplates = new Set(["auto", "none", "0"]);
1300
- function makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name) {
1301
- let i = 0;
1302
- let animatableTemplate = undefined;
1303
- while (i < unresolvedKeyframes.length && !animatableTemplate) {
1304
- const keyframe = unresolvedKeyframes[i];
1305
- if (typeof keyframe === "string" &&
1306
- !invalidTemplates.has(keyframe) &&
1307
- analyseComplexValue(keyframe).values.length) {
1308
- animatableTemplate = unresolvedKeyframes[i];
1309
- }
1310
- i++;
1311
- }
1312
- if (animatableTemplate && name) {
1313
- for (const noneIndex of noneKeyframeIndexes) {
1314
- unresolvedKeyframes[noneIndex] = getAnimatableNone(name, animatableTemplate);
1315
- }
1316
- }
1317
- }
1318
-
1319
- const radToDeg = (rad) => (rad * 180) / Math.PI;
1320
- const rotate = (v) => {
1321
- const angle = radToDeg(Math.atan2(v[1], v[0]));
1322
- return rebaseAngle(angle);
1323
- };
1324
- const matrix2dParsers = {
1325
- x: 4,
1326
- y: 5,
1327
- translateX: 4,
1328
- translateY: 5,
1329
- scaleX: 0,
1330
- scaleY: 3,
1331
- scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
1332
- rotate,
1333
- rotateZ: rotate,
1334
- skewX: (v) => radToDeg(Math.atan(v[1])),
1335
- skewY: (v) => radToDeg(Math.atan(v[2])),
1336
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[2])) / 2,
1337
- };
1338
- const rebaseAngle = (angle) => {
1339
- angle = angle % 360;
1340
- if (angle < 0)
1341
- angle += 360;
1342
- return angle;
1343
- };
1344
- const rotateZ = rotate;
1345
- const scaleX = (v) => Math.sqrt(v[0] * v[0] + v[1] * v[1]);
1346
- const scaleY = (v) => Math.sqrt(v[4] * v[4] + v[5] * v[5]);
1347
- const matrix3dParsers = {
1348
- x: 12,
1349
- y: 13,
1350
- z: 14,
1351
- translateX: 12,
1352
- translateY: 13,
1353
- translateZ: 14,
1354
- scaleX,
1355
- scaleY,
1356
- scale: (v) => (scaleX(v) + scaleY(v)) / 2,
1357
- rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
1358
- rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
1359
- rotateZ,
1360
- rotate: rotateZ,
1361
- skewX: (v) => radToDeg(Math.atan(v[4])),
1362
- skewY: (v) => radToDeg(Math.atan(v[1])),
1363
- skew: (v) => (Math.abs(v[1]) + Math.abs(v[4])) / 2,
1364
- };
1365
- function defaultTransformValue(name) {
1366
- return name.includes("scale") ? 1 : 0;
1367
- }
1368
- function parseValueFromTransform(transform, name) {
1369
- if (!transform || transform === "none") {
1370
- return defaultTransformValue(name);
1371
- }
1372
- const matrix3dMatch = transform.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);
1373
- let parsers;
1374
- let match;
1375
- if (matrix3dMatch) {
1376
- parsers = matrix3dParsers;
1377
- match = matrix3dMatch;
1378
- }
1379
- else {
1380
- const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
1381
- parsers = matrix2dParsers;
1382
- match = matrix2dMatch;
1383
- }
1384
- if (!match) {
1385
- return defaultTransformValue(name);
1386
- }
1387
- const valueParser = parsers[name];
1388
- const values = match[1].split(",").map(convertTransformToNumber);
1389
- return typeof valueParser === "function"
1390
- ? valueParser(values)
1391
- : values[valueParser];
1392
- }
1393
- const readTransformValue = (instance, name) => {
1394
- const { transform = "none" } = getComputedStyle(instance);
1395
- return parseValueFromTransform(transform, name);
1396
- };
1397
- function convertTransformToNumber(value) {
1398
- return parseFloat(value.trim());
1399
- }
1400
-
1401
- const isNumOrPxType = (v) => v === number || v === px;
1402
- const transformKeys = new Set(["x", "y", "z"]);
1403
- const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
1404
- function removeNonTranslationalTransform(visualElement) {
1405
- const removedTransforms = [];
1406
- nonTranslationalTransformKeys.forEach((key) => {
1407
- const value = visualElement.getValue(key);
1408
- if (value !== undefined) {
1409
- removedTransforms.push([key, value.get()]);
1410
- value.set(key.startsWith("scale") ? 1 : 0);
1411
- }
1412
- });
1413
- return removedTransforms;
1414
- }
1415
- const positionalValues = {
1416
- // Dimensions
1417
- width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) => x.max - x.min - parseFloat(paddingLeft) - parseFloat(paddingRight),
1418
- height: ({ y }, { paddingTop = "0", paddingBottom = "0" }) => y.max - y.min - parseFloat(paddingTop) - parseFloat(paddingBottom),
1419
- top: (_bbox, { top }) => parseFloat(top),
1420
- left: (_bbox, { left }) => parseFloat(left),
1421
- bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
1422
- right: ({ x }, { left }) => parseFloat(left) + (x.max - x.min),
1423
- // Transform
1424
- x: (_bbox, { transform }) => parseValueFromTransform(transform, "x"),
1425
- y: (_bbox, { transform }) => parseValueFromTransform(transform, "y"),
1426
- };
1427
- // Alias translate longform names
1428
- positionalValues.translateX = positionalValues.x;
1429
- positionalValues.translateY = positionalValues.y;
1430
-
1431
- const toResolve = new Set();
1432
- let isScheduled = false;
1433
- let anyNeedsMeasurement = false;
1434
- function measureAllKeyframes() {
1435
- if (anyNeedsMeasurement) {
1436
- const resolversToMeasure = Array.from(toResolve).filter((resolver) => resolver.needsMeasurement);
1437
- const elementsToMeasure = new Set(resolversToMeasure.map((resolver) => resolver.element));
1438
- const transformsToRestore = new Map();
1439
- /**
1440
- * Write pass
1441
- * If we're measuring elements we want to remove bounding box-changing transforms.
1442
- */
1443
- elementsToMeasure.forEach((element) => {
1444
- const removedTransforms = removeNonTranslationalTransform(element);
1445
- if (!removedTransforms.length)
1446
- return;
1447
- transformsToRestore.set(element, removedTransforms);
1448
- element.render();
1449
- });
1450
- // Read
1451
- resolversToMeasure.forEach((resolver) => resolver.measureInitialState());
1452
- // Write
1453
- elementsToMeasure.forEach((element) => {
1454
- element.render();
1455
- const restore = transformsToRestore.get(element);
1456
- if (restore) {
1457
- restore.forEach(([key, value]) => {
1458
- element.getValue(key)?.set(value);
1459
- });
1460
- }
1461
- });
1462
- // Read
1463
- resolversToMeasure.forEach((resolver) => resolver.measureEndState());
1464
- // Write
1465
- resolversToMeasure.forEach((resolver) => {
1466
- if (resolver.suspendedScrollY !== undefined) {
1467
- window.scrollTo(0, resolver.suspendedScrollY);
1468
- }
1469
- });
1470
- }
1471
- anyNeedsMeasurement = false;
1472
- isScheduled = false;
1473
- toResolve.forEach((resolver) => resolver.complete());
1474
- toResolve.clear();
1475
- }
1476
- function readAllKeyframes() {
1477
- toResolve.forEach((resolver) => {
1478
- resolver.readKeyframes();
1479
- if (resolver.needsMeasurement) {
1480
- anyNeedsMeasurement = true;
1481
- }
1482
- });
1483
- }
1484
- function flushKeyframeResolvers() {
1485
- readAllKeyframes();
1486
- measureAllKeyframes();
1487
- }
1488
- class KeyframeResolver {
1489
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element, isAsync = false) {
1490
- /**
1491
- * Track whether this resolver has completed. Once complete, it never
1492
- * needs to attempt keyframe resolution again.
1493
- */
1494
- this.isComplete = false;
1495
- /**
1496
- * Track whether this resolver is async. If it is, it'll be added to the
1497
- * resolver queue and flushed in the next frame. Resolvers that aren't going
1498
- * to trigger read/write thrashing don't need to be async.
1499
- */
1500
- this.isAsync = false;
1501
- /**
1502
- * Track whether this resolver needs to perform a measurement
1503
- * to resolve its keyframes.
1504
- */
1505
- this.needsMeasurement = false;
1506
- /**
1507
- * Track whether this resolver is currently scheduled to resolve
1508
- * to allow it to be cancelled and resumed externally.
1509
- */
1510
- this.isScheduled = false;
1511
- this.unresolvedKeyframes = [...unresolvedKeyframes];
1512
- this.onComplete = onComplete;
1513
- this.name = name;
1514
- this.motionValue = motionValue;
1515
- this.element = element;
1516
- this.isAsync = isAsync;
1517
- }
1518
- scheduleResolve() {
1519
- this.isScheduled = true;
1520
- if (this.isAsync) {
1521
- toResolve.add(this);
1522
- if (!isScheduled) {
1523
- isScheduled = true;
1524
- motionDom.frame.read(readAllKeyframes);
1525
- motionDom.frame.resolveKeyframes(measureAllKeyframes);
1526
- }
1527
- }
1528
- else {
1529
- this.readKeyframes();
1530
- this.complete();
1531
- }
1532
- }
1533
- readKeyframes() {
1534
- const { unresolvedKeyframes, name, element, motionValue } = this;
1535
- /**
1536
- * If a keyframe is null, we hydrate it either by reading it from
1537
- * the instance, or propagating from previous keyframes.
1538
- */
1539
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
1540
- if (unresolvedKeyframes[i] === null) {
1541
- /**
1542
- * If the first keyframe is null, we need to find its value by sampling the element
1543
- */
1544
- if (i === 0) {
1545
- const currentValue = motionValue?.get();
1546
- const finalKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
1547
- if (currentValue !== undefined) {
1548
- unresolvedKeyframes[0] = currentValue;
1549
- }
1550
- else if (element && name) {
1551
- const valueAsRead = element.readValue(name, finalKeyframe);
1552
- if (valueAsRead !== undefined && valueAsRead !== null) {
1553
- unresolvedKeyframes[0] = valueAsRead;
1554
- }
1555
- }
1556
- if (unresolvedKeyframes[0] === undefined) {
1557
- unresolvedKeyframes[0] = finalKeyframe;
1558
- }
1559
- if (motionValue && currentValue === undefined) {
1560
- motionValue.set(unresolvedKeyframes[0]);
1561
- }
1562
- }
1563
- else {
1564
- unresolvedKeyframes[i] = unresolvedKeyframes[i - 1];
1565
- }
1566
- }
1567
- }
1568
- }
1569
- setFinalKeyframe() { }
1570
- measureInitialState() { }
1571
- renderEndStyles() { }
1572
- measureEndState() { }
1573
- complete() {
1574
- this.isComplete = true;
1575
- this.onComplete(this.unresolvedKeyframes, this.finalKeyframe);
1576
- toResolve.delete(this);
1577
- }
1578
- cancel() {
1579
- if (!this.isComplete) {
1580
- this.isScheduled = false;
1581
- toResolve.delete(this);
1582
- }
1583
- }
1584
- resume() {
1585
- if (!this.isComplete)
1586
- this.scheduleResolve();
1587
- }
1588
- }
1589
-
1590
- /**
1591
- * Check if value is a numerical string, ie a string that is purely a number eg "100" or "-100.1"
1592
- */
1593
- const isNumericalString = (v) => /^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(v);
1594
-
1595
- const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
1596
- const isCSSVariableName =
1597
- /*@__PURE__*/ checkStringStartsWith("--");
1598
- const startsAsVariableToken =
1599
- /*@__PURE__*/ checkStringStartsWith("var(--");
1600
- const isCSSVariableToken = (value) => {
1601
- const startsWithToken = startsAsVariableToken(value);
1602
- if (!startsWithToken)
1603
- return false;
1604
- // Ensure any comments are stripped from the value as this can harm performance of the regex.
1605
- return singleCssVariableRegex.test(value.split("/*")[0].trim());
1606
- };
1607
- const singleCssVariableRegex = /var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;
1608
-
1609
- /**
1610
- * Parse Framer's special CSS variable format into a CSS token and a fallback.
1611
- *
1612
- * ```
1613
- * `var(--foo, #fff)` => [`--foo`, '#fff']
1614
- * ```
1615
- *
1616
- * @param current
1617
- */
1618
- const splitCSSVariableRegex =
1619
- // eslint-disable-next-line redos-detector/no-unsafe-regex -- false positive, as it can match a lot of words
1620
- /^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;
1621
- function parseCSSVariable(current) {
1622
- const match = splitCSSVariableRegex.exec(current);
1623
- if (!match)
1624
- return [,];
1625
- const [, token1, token2, fallback] = match;
1626
- return [`--${token1 ?? token2}`, fallback];
1627
- }
1628
- const maxDepth = 4;
1629
- function getVariableValue(current, element, depth = 1) {
1630
- motionUtils.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
1631
- const [token, fallback] = parseCSSVariable(current);
1632
- // No CSS variable detected
1633
- if (!token)
1634
- return;
1635
- // Attempt to read this CSS variable off the element
1636
- const resolved = window.getComputedStyle(element).getPropertyValue(token);
1637
- if (resolved) {
1638
- const trimmed = resolved.trim();
1639
- return isNumericalString(trimmed) ? parseFloat(trimmed) : trimmed;
1640
- }
1641
- return isCSSVariableToken(fallback)
1642
- ? getVariableValue(fallback, element, depth + 1)
1643
- : fallback;
1644
- }
1645
-
1646
- /**
1647
- * Tests a provided value against a ValueType
1648
- */
1649
- const testValueType = (v) => (type) => type.test(v);
1650
-
1651
- /**
1652
- * ValueType for "auto"
1653
- */
1654
- const auto = {
1655
- test: (v) => v === "auto",
1656
- parse: (v) => v,
1657
- };
1658
-
1659
- /**
1660
- * A list of value types commonly used for dimensions
1661
- */
1662
- const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
1663
- /**
1664
- * Tests a dimensional value against the list of dimension ValueTypes
1665
- */
1666
- const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
1667
-
1668
- class DOMKeyframesResolver extends KeyframeResolver {
1669
- constructor(unresolvedKeyframes, onComplete, name, motionValue, element) {
1670
- super(unresolvedKeyframes, onComplete, name, motionValue, element, true);
1671
- }
1672
- readKeyframes() {
1673
- const { unresolvedKeyframes, element, name } = this;
1674
- if (!element || !element.current)
1675
- return;
1676
- super.readKeyframes();
1677
- /**
1678
- * If any keyframe is a CSS variable, we need to find its value by sampling the element
1679
- */
1680
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
1681
- let keyframe = unresolvedKeyframes[i];
1682
- if (typeof keyframe === "string") {
1683
- keyframe = keyframe.trim();
1684
- if (isCSSVariableToken(keyframe)) {
1685
- const resolved = getVariableValue(keyframe, element.current);
1686
- if (resolved !== undefined) {
1687
- unresolvedKeyframes[i] = resolved;
1688
- }
1689
- if (i === unresolvedKeyframes.length - 1) {
1690
- this.finalKeyframe = keyframe;
1691
- }
1692
- }
1693
- }
1694
- }
1695
- /**
1696
- * Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
1697
- * This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
1698
- * have a far bigger performance impact.
1699
- */
1700
- this.resolveNoneKeyframes();
1701
- /**
1702
- * Check to see if unit type has changed. If so schedule jobs that will
1703
- * temporarily set styles to the destination keyframes.
1704
- * Skip if we have more than two keyframes or this isn't a positional value.
1705
- * TODO: We can throw if there are multiple keyframes and the value type changes.
1706
- */
1707
- if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
1708
- return;
1709
- }
1710
- const [origin, target] = unresolvedKeyframes;
1711
- const originType = findDimensionValueType(origin);
1712
- const targetType = findDimensionValueType(target);
1713
- /**
1714
- * Either we don't recognise these value types or we can animate between them.
1715
- */
1716
- if (originType === targetType)
1717
- return;
1718
- /**
1719
- * If both values are numbers or pixels, we can animate between them by
1720
- * converting them to numbers.
1721
- */
1722
- if (isNumOrPxType(originType) && isNumOrPxType(targetType)) {
1723
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
1724
- const value = unresolvedKeyframes[i];
1725
- if (typeof value === "string") {
1726
- unresolvedKeyframes[i] = parseFloat(value);
1727
- }
1728
- }
1729
- }
1730
- else {
1731
- /**
1732
- * Else, the only way to resolve this is by measuring the element.
1733
- */
1734
- this.needsMeasurement = true;
1735
- }
1736
- }
1737
- resolveNoneKeyframes() {
1738
- const { unresolvedKeyframes, name } = this;
1739
- const noneKeyframeIndexes = [];
1740
- for (let i = 0; i < unresolvedKeyframes.length; i++) {
1741
- if (isNone(unresolvedKeyframes[i])) {
1742
- noneKeyframeIndexes.push(i);
1743
- }
1744
- }
1745
- if (noneKeyframeIndexes.length) {
1746
- makeNoneKeyframesAnimatable(unresolvedKeyframes, noneKeyframeIndexes, name);
1747
- }
1748
- }
1749
- measureInitialState() {
1750
- const { element, unresolvedKeyframes, name } = this;
1751
- if (!element || !element.current)
1752
- return;
1753
- if (name === "height") {
1754
- this.suspendedScrollY = window.pageYOffset;
1755
- }
1756
- this.measuredOrigin = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
1757
- unresolvedKeyframes[0] = this.measuredOrigin;
1758
- // Set final key frame to measure after next render
1759
- const measureKeyframe = unresolvedKeyframes[unresolvedKeyframes.length - 1];
1760
- if (measureKeyframe !== undefined) {
1761
- element.getValue(name, measureKeyframe).jump(measureKeyframe, false);
1762
- }
1763
- }
1764
- measureEndState() {
1765
- const { element, name, unresolvedKeyframes } = this;
1766
- if (!element || !element.current)
1767
- return;
1768
- const value = element.getValue(name);
1769
- value && value.jump(this.measuredOrigin, false);
1770
- const finalKeyframeIndex = unresolvedKeyframes.length - 1;
1771
- const finalKeyframe = unresolvedKeyframes[finalKeyframeIndex];
1772
- unresolvedKeyframes[finalKeyframeIndex] = positionalValues[name](element.measureViewportBox(), window.getComputedStyle(element.current));
1773
- if (finalKeyframe !== null && this.finalKeyframe === undefined) {
1774
- this.finalKeyframe = finalKeyframe;
1775
- }
1776
- // If we removed transform values, reapply them before the next render
1777
- if (this.removedTransforms?.length) {
1778
- this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
1779
- element
1780
- .getValue(unsetTransformName)
1781
- .set(unsetTransformValue);
1782
- });
1783
- }
1784
- this.resolveNoneKeyframes();
1785
- }
1786
- }
1787
-
1788
- /**
1789
- * Check if a value is animatable. Examples:
1790
- *
1791
- * ✅: 100, "100px", "#fff"
1792
- * ❌: "block", "url(2.jpg)"
1793
- * @param value
1794
- *
1795
- * @internal
1796
- */
1797
- const isAnimatable = (value, name) => {
1798
- // If the list of keys tat might be non-animatable grows, replace with Set
1799
- if (name === "zIndex")
1800
- return false;
1801
- // If it's a number or a keyframes array, we can animate it. We might at some point
1802
- // need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
1803
- // but for now lets leave it like this for performance reasons
1804
- if (typeof value === "number" || Array.isArray(value))
1805
- return true;
1806
- if (typeof value === "string" && // It's animatable if we have a string
1807
- (complex.test(value) || value === "0") && // And it contains numbers and/or colors
1808
- !value.startsWith("url(") // Unless it starts with "url("
1809
- ) {
1810
- return true;
1811
- }
1812
- return false;
1813
- };
1814
-
1815
- function hasKeyframesChanged(keyframes) {
1816
- const current = keyframes[0];
1817
- if (keyframes.length === 1)
1818
- return true;
1819
- for (let i = 0; i < keyframes.length; i++) {
1820
- if (keyframes[i] !== current)
1821
- return true;
1822
- }
1823
- }
1824
- function canAnimate(keyframes, name, type, velocity) {
1825
- /**
1826
- * Check if we're able to animate between the start and end keyframes,
1827
- * and throw a warning if we're attempting to animate between one that's
1828
- * animatable and another that isn't.
1829
- */
1830
- const originKeyframe = keyframes[0];
1831
- if (originKeyframe === null)
1832
- return false;
1833
- /**
1834
- * These aren't traditionally animatable but we do support them.
1835
- * In future we could look into making this more generic or replacing
1836
- * this function with mix() === mixImmediate
1837
- */
1838
- if (name === "display" || name === "visibility")
1839
- return true;
1840
- const targetKeyframe = keyframes[keyframes.length - 1];
1841
- const isOriginAnimatable = isAnimatable(originKeyframe, name);
1842
- const isTargetAnimatable = isAnimatable(targetKeyframe, name);
1843
- motionUtils.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
1844
- // Always skip if any of these are true
1845
- if (!isOriginAnimatable || !isTargetAnimatable) {
1846
- return false;
1847
- }
1848
- return (hasKeyframesChanged(keyframes) ||
1849
- ((type === "spring" || motionDom.isGenerator(type)) && velocity));
1850
- }
1851
-
1852
- const isNotNull = (value) => value !== null;
1853
- function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
1854
- const resolvedKeyframes = keyframes.filter(isNotNull);
1855
- const index = repeat && repeatType !== "loop" && repeat % 2 === 1
1856
- ? 0
1857
- : resolvedKeyframes.length - 1;
1858
- return !index || finalKeyframe === undefined
1859
- ? resolvedKeyframes[index]
1860
- : finalKeyframe;
1861
- }
1862
-
1863
- /**
1864
- * Maximum time allowed between an animation being created and it being
1865
- * resolved for us to use the latter as the start time.
1866
- *
1867
- * This is to ensure that while we prefer to "start" an animation as soon
1868
- * as it's triggered, we also want to avoid a visual jump if there's a big delay
1869
- * between these two moments.
1870
- */
1871
- const MAX_RESOLVE_DELAY = 40;
1872
- class BaseAnimation {
1873
- constructor({ autoplay = true, delay = 0, type = "keyframes", repeat = 0, repeatDelay = 0, repeatType = "loop", ...options }) {
1874
- // Track whether the animation has been stopped. Stopped animations won't restart.
1875
- this.isStopped = false;
1876
- this.hasAttemptedResolve = false;
1877
- this.createdAt = motionDom.time.now();
1878
- this.options = {
1879
- autoplay,
1880
- delay,
1881
- type,
1882
- repeat,
1883
- repeatDelay,
1884
- repeatType,
1885
- ...options,
1886
- };
1887
- this.updateFinishedPromise();
1888
- }
1889
- /**
1890
- * This method uses the createdAt and resolvedAt to calculate the
1891
- * animation startTime. *Ideally*, we would use the createdAt time as t=0
1892
- * as the following frame would then be the first frame of the animation in
1893
- * progress, which would feel snappier.
1894
- *
1895
- * However, if there's a delay (main thread work) between the creation of
1896
- * the animation and the first commited frame, we prefer to use resolvedAt
1897
- * to avoid a sudden jump into the animation.
1898
- */
1899
- calcStartTime() {
1900
- if (!this.resolvedAt)
1901
- return this.createdAt;
1902
- return this.resolvedAt - this.createdAt > MAX_RESOLVE_DELAY
1903
- ? this.resolvedAt
1904
- : this.createdAt;
1905
- }
1906
- /**
1907
- * A getter for resolved data. If keyframes are not yet resolved, accessing
1908
- * this.resolved will synchronously flush all pending keyframe resolvers.
1909
- * This is a deoptimisation, but at its worst still batches read/writes.
1910
- */
1911
- get resolved() {
1912
- if (!this._resolved && !this.hasAttemptedResolve) {
1913
- flushKeyframeResolvers();
1914
- }
1915
- return this._resolved;
1916
- }
1917
- /**
1918
- * A method to be called when the keyframes resolver completes. This method
1919
- * will check if its possible to run the animation and, if not, skip it.
1920
- * Otherwise, it will call initPlayback on the implementing class.
1921
- */
1922
- onKeyframesResolved(keyframes, finalKeyframe) {
1923
- this.resolvedAt = motionDom.time.now();
1924
- this.hasAttemptedResolve = true;
1925
- const { name, type, velocity, delay, onComplete, onUpdate, isGenerator, } = this.options;
1926
- /**
1927
- * If we can't animate this value with the resolved keyframes
1928
- * then we should complete it immediately.
1929
- */
1930
- if (!isGenerator && !canAnimate(keyframes, name, type, velocity)) {
1931
- // Finish immediately
1932
- if (!delay) {
1933
- onUpdate &&
1934
- onUpdate(getFinalKeyframe(keyframes, this.options, finalKeyframe));
1935
- onComplete && onComplete();
1936
- this.resolveFinishedPromise();
1937
- return;
1938
- }
1939
- // Finish after a delay
1940
- else {
1941
- this.options.duration = 0;
1942
- }
1943
- }
1944
- const resolvedAnimation = this.initPlayback(keyframes, finalKeyframe);
1945
- if (resolvedAnimation === false)
1946
- return;
1947
- this._resolved = {
1948
- keyframes,
1949
- finalKeyframe,
1950
- ...resolvedAnimation,
1951
- };
1952
- this.onPostResolved();
1953
- }
1954
- onPostResolved() { }
1955
- /**
1956
- * Allows the returned animation to be awaited or promise-chained. Currently
1957
- * resolves when the animation finishes at all but in a future update could/should
1958
- * reject if its cancels.
1959
- */
1960
- then(resolve, reject) {
1961
- return this.currentFinishedPromise.then(resolve, reject);
1962
- }
1963
- flatten() {
1964
- if (!this.options.allowFlatten)
1965
- return;
1966
- this.options.type = "keyframes";
1967
- this.options.ease = "linear";
1968
- }
1969
- updateFinishedPromise() {
1970
- this.currentFinishedPromise = new Promise((resolve) => {
1971
- this.resolveFinishedPromise = resolve;
1972
- });
1973
- }
1974
- }
1975
-
1976
- // Adapted from https://gist.github.com/mjackson/5311256
1977
- function hueToRgb(p, q, t) {
1978
- if (t < 0)
1979
- t += 1;
1980
- if (t > 1)
1981
- t -= 1;
1982
- if (t < 1 / 6)
1983
- return p + (q - p) * 6 * t;
1984
- if (t < 1 / 2)
1985
- return q;
1986
- if (t < 2 / 3)
1987
- return p + (q - p) * (2 / 3 - t) * 6;
1988
- return p;
1989
- }
1990
- function hslaToRgba({ hue, saturation, lightness, alpha }) {
1991
- hue /= 360;
1992
- saturation /= 100;
1993
- lightness /= 100;
1994
- let red = 0;
1995
- let green = 0;
1996
- let blue = 0;
1997
- if (!saturation) {
1998
- red = green = blue = lightness;
1999
- }
2000
- else {
2001
- const q = lightness < 0.5
2002
- ? lightness * (1 + saturation)
2003
- : lightness + saturation - lightness * saturation;
2004
- const p = 2 * lightness - q;
2005
- red = hueToRgb(p, q, hue + 1 / 3);
2006
- green = hueToRgb(p, q, hue);
2007
- blue = hueToRgb(p, q, hue - 1 / 3);
2008
- }
2009
- return {
2010
- red: Math.round(red * 255),
2011
- green: Math.round(green * 255),
2012
- blue: Math.round(blue * 255),
2013
- alpha,
2014
- };
2015
- }
2016
-
2017
- function mixImmediate(a, b) {
2018
- return (p) => (p > 0 ? b : a);
2019
- }
2020
-
2021
- // Linear color space blending
2022
- // Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
2023
- // Demonstrated http://codepen.io/osublake/pen/xGVVaN
2024
- const mixLinearColor = (from, to, v) => {
2025
- const fromExpo = from * from;
2026
- const expo = v * (to * to - fromExpo) + fromExpo;
2027
- return expo < 0 ? 0 : Math.sqrt(expo);
2028
- };
2029
- const colorTypes = [hex, rgba, hsla];
2030
- const getColorType = (v) => colorTypes.find((type) => type.test(v));
2031
- function asRGBA(color) {
2032
- const type = getColorType(color);
2033
- motionUtils.warning(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
2034
- if (!Boolean(type))
2035
- return false;
2036
- let model = type.parse(color);
2037
- if (type === hsla) {
2038
- // TODO Remove this cast - needed since Motion's stricter typing
2039
- model = hslaToRgba(model);
2040
- }
2041
- return model;
2042
- }
2043
- const mixColor = (from, to) => {
2044
- const fromRGBA = asRGBA(from);
2045
- const toRGBA = asRGBA(to);
2046
- if (!fromRGBA || !toRGBA) {
2047
- return mixImmediate(from, to);
2048
- }
2049
- const blended = { ...fromRGBA };
2050
- return (v) => {
2051
- blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
2052
- blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
2053
- blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
2054
- blended.alpha = mixNumber$1(fromRGBA.alpha, toRGBA.alpha, v);
2055
- return rgba.transform(blended);
2056
- };
2057
- };
2058
-
2059
- /**
2060
- * Pipe
2061
- * Compose other transformers to run linearily
2062
- * pipe(min(20), max(40))
2063
- * @param {...functions} transformers
2064
- * @return {function}
2065
- */
2066
- const combineFunctions = (a, b) => (v) => b(a(v));
2067
- const pipe = (...transformers) => transformers.reduce(combineFunctions);
2068
-
2069
- const invisibleValues = new Set(["none", "hidden"]);
2070
- /**
2071
- * Returns a function that, when provided a progress value between 0 and 1,
2072
- * will return the "none" or "hidden" string only when the progress is that of
2073
- * the origin or target.
2074
- */
2075
- function mixVisibility(origin, target) {
2076
- if (invisibleValues.has(origin)) {
2077
- return (p) => (p <= 0 ? origin : target);
2078
- }
2079
- else {
2080
- return (p) => (p >= 1 ? target : origin);
2081
- }
2082
- }
2083
-
2084
- function mixNumber(a, b) {
2085
- return (p) => mixNumber$1(a, b, p);
2086
- }
2087
- function getMixer$1(a) {
2088
- if (typeof a === "number") {
2089
- return mixNumber;
2090
- }
2091
- else if (typeof a === "string") {
2092
- return isCSSVariableToken(a)
2093
- ? mixImmediate
2094
- : color.test(a)
2095
- ? mixColor
2096
- : mixComplex;
2097
- }
2098
- else if (Array.isArray(a)) {
2099
- return mixArray;
2100
- }
2101
- else if (typeof a === "object") {
2102
- return color.test(a) ? mixColor : mixObject;
2103
- }
2104
- return mixImmediate;
2105
- }
2106
- function mixArray(a, b) {
2107
- const output = [...a];
2108
- const numValues = output.length;
2109
- const blendValue = a.map((v, i) => getMixer$1(v)(v, b[i]));
2110
- return (p) => {
2111
- for (let i = 0; i < numValues; i++) {
2112
- output[i] = blendValue[i](p);
2113
- }
2114
- return output;
2115
- };
2116
- }
2117
- function mixObject(a, b) {
2118
- const output = { ...a, ...b };
2119
- const blendValue = {};
2120
- for (const key in output) {
2121
- if (a[key] !== undefined && b[key] !== undefined) {
2122
- blendValue[key] = getMixer$1(a[key])(a[key], b[key]);
2123
- }
2124
- }
2125
- return (v) => {
2126
- for (const key in blendValue) {
2127
- output[key] = blendValue[key](v);
2128
- }
2129
- return output;
2130
- };
2131
- }
2132
- function matchOrder(origin, target) {
2133
- const orderedOrigin = [];
2134
- const pointers = { color: 0, var: 0, number: 0 };
2135
- for (let i = 0; i < target.values.length; i++) {
2136
- const type = target.types[i];
2137
- const originIndex = origin.indexes[type][pointers[type]];
2138
- const originValue = origin.values[originIndex] ?? 0;
2139
- orderedOrigin[i] = originValue;
2140
- pointers[type]++;
2141
- }
2142
- return orderedOrigin;
2143
- }
2144
- const mixComplex = (origin, target) => {
2145
- const template = complex.createTransformer(target);
2146
- const originStats = analyseComplexValue(origin);
2147
- const targetStats = analyseComplexValue(target);
2148
- const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length &&
2149
- originStats.indexes.color.length === targetStats.indexes.color.length &&
2150
- originStats.indexes.number.length >= targetStats.indexes.number.length;
2151
- if (canInterpolate) {
2152
- if ((invisibleValues.has(origin) &&
2153
- !targetStats.values.length) ||
2154
- (invisibleValues.has(target) &&
2155
- !originStats.values.length)) {
2156
- return mixVisibility(origin, target);
2157
- }
2158
- return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), template);
2159
- }
2160
- else {
2161
- motionUtils.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
2162
- return mixImmediate(origin, target);
2163
- }
2164
- };
2165
-
2166
- function mix(from, to, p) {
2167
- if (typeof from === "number" &&
2168
- typeof to === "number" &&
2169
- typeof p === "number") {
2170
- return mixNumber$1(from, to, p);
2171
- }
2172
- const mixer = getMixer$1(from);
2173
- return mixer(from, to);
2174
- }
2175
-
2176
- function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
2177
- const origin = keyframes[0];
2178
- const state = {
2179
- done: false,
2180
- value: origin,
2181
- };
2182
- const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
2183
- const nearestBoundary = (v) => {
2184
- if (min === undefined)
2185
- return max;
2186
- if (max === undefined)
2187
- return min;
2188
- return Math.abs(min - v) < Math.abs(max - v) ? min : max;
2189
- };
2190
- let amplitude = power * velocity;
2191
- const ideal = origin + amplitude;
2192
- const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
2193
- /**
2194
- * If the target has changed we need to re-calculate the amplitude, otherwise
2195
- * the animation will start from the wrong position.
2196
- */
2197
- if (target !== ideal)
2198
- amplitude = target - origin;
2199
- const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
2200
- const calcLatest = (t) => target + calcDelta(t);
2201
- const applyFriction = (t) => {
2202
- const delta = calcDelta(t);
2203
- const latest = calcLatest(t);
2204
- state.done = Math.abs(delta) <= restDelta;
2205
- state.value = state.done ? target : latest;
2206
- };
2207
- /**
2208
- * Ideally this would resolve for t in a stateless way, we could
2209
- * do that by always precalculating the animation but as we know
2210
- * this will be done anyway we can assume that spring will
2211
- * be discovered during that.
2212
- */
2213
- let timeReachedBoundary;
2214
- let spring$1;
2215
- const checkCatchBoundary = (t) => {
2216
- if (!isOutOfBounds(state.value))
2217
- return;
2218
- timeReachedBoundary = t;
2219
- spring$1 = spring({
2220
- keyframes: [state.value, nearestBoundary(state.value)],
2221
- velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
2222
- damping: bounceDamping,
2223
- stiffness: bounceStiffness,
2224
- restDelta,
2225
- restSpeed,
2226
- });
2227
- };
2228
- checkCatchBoundary(0);
2229
- return {
2230
- calculatedDuration: null,
2231
- next: (t) => {
2232
- /**
2233
- * We need to resolve the friction to figure out if we need a
2234
- * spring but we don't want to do this twice per frame. So here
2235
- * we flag if we updated for this frame and later if we did
2236
- * we can skip doing it again.
2237
- */
2238
- let hasUpdatedFrame = false;
2239
- if (!spring$1 && timeReachedBoundary === undefined) {
2240
- hasUpdatedFrame = true;
2241
- applyFriction(t);
2242
- checkCatchBoundary(t);
2243
- }
2244
- /**
2245
- * If we have a spring and the provided t is beyond the moment the friction
2246
- * animation crossed the min/max boundary, use the spring.
2247
- */
2248
- if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
2249
- return spring$1.next(t - timeReachedBoundary);
2250
- }
2251
- else {
2252
- !hasUpdatedFrame && applyFriction(t);
2253
- return state;
2254
- }
2255
- },
2256
- };
2257
- }
2258
-
2259
- const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
2260
- const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
2261
- const easeInOut = /*@__PURE__*/ cubicBezier(0.42, 0, 0.58, 1);
2262
-
2263
- const easingLookup = {
2264
- linear: motionUtils.noop,
2265
- easeIn,
2266
- easeInOut,
2267
- easeOut,
2268
- circIn,
2269
- circInOut,
2270
- circOut,
2271
- backIn,
2272
- backInOut,
2273
- backOut,
2274
- anticipate,
2275
- };
2276
- const easingDefinitionToFunction = (definition) => {
2277
- if (motionDom.isBezierDefinition(definition)) {
2278
- // If cubic bezier definition, create bezier curve
2279
- motionUtils.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
2280
- const [x1, y1, x2, y2] = definition;
2281
- return cubicBezier(x1, y1, x2, y2);
2282
- }
2283
- else if (typeof definition === "string") {
2284
- // Else lookup from table
2285
- motionUtils.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
2286
- return easingLookup[definition];
2287
- }
2288
- return definition;
2289
- };
2290
-
2291
- function createMixers(output, ease, customMixer) {
2292
- const mixers = [];
2293
- const mixerFactory = customMixer || mix;
2294
- const numMixers = output.length - 1;
2295
- for (let i = 0; i < numMixers; i++) {
2296
- let mixer = mixerFactory(output[i], output[i + 1]);
2297
- if (ease) {
2298
- const easingFunction = Array.isArray(ease) ? ease[i] || motionUtils.noop : ease;
2299
- mixer = pipe(easingFunction, mixer);
2300
- }
2301
- mixers.push(mixer);
2302
- }
2303
- return mixers;
2304
- }
2305
- /**
2306
- * Create a function that maps from a numerical input array to a generic output array.
2307
- *
2308
- * Accepts:
2309
- * - Numbers
2310
- * - Colors (hex, hsl, hsla, rgb, rgba)
2311
- * - Complex (combinations of one or more numbers or strings)
2312
- *
2313
- * ```jsx
2314
- * const mixColor = interpolate([0, 1], ['#fff', '#000'])
2315
- *
2316
- * mixColor(0.5) // 'rgba(128, 128, 128, 1)'
2317
- * ```
2318
- *
2319
- * TODO Revist this approach once we've moved to data models for values,
2320
- * probably not needed to pregenerate mixer functions.
2321
- *
2322
- * @public
2323
- */
2324
- function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
2325
- const inputLength = input.length;
2326
- motionUtils.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
2327
- /**
2328
- * If we're only provided a single input, we can just make a function
2329
- * that returns the output.
2330
- */
2331
- if (inputLength === 1)
2332
- return () => output[0];
2333
- if (inputLength === 2 && output[0] === output[1])
2334
- return () => output[1];
2335
- const isZeroDeltaRange = input[0] === input[1];
2336
- // If input runs highest -> lowest, reverse both arrays
2337
- if (input[0] > input[inputLength - 1]) {
2338
- input = [...input].reverse();
2339
- output = [...output].reverse();
2340
- }
2341
- const mixers = createMixers(output, ease, mixer);
2342
- const numMixers = mixers.length;
2343
- const interpolator = (v) => {
2344
- if (isZeroDeltaRange && v < input[0])
2345
- return output[0];
2346
- let i = 0;
2347
- if (numMixers > 1) {
2348
- for (; i < input.length - 2; i++) {
2349
- if (v < input[i + 1])
2350
- break;
2351
- }
2352
- }
2353
- const progressInRange = motionUtils.progress(input[i], input[i + 1], v);
2354
- return mixers[i](progressInRange);
2355
- };
2356
- return isClamp
2357
- ? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
2358
- : interpolator;
2359
- }
2360
-
2361
- function convertOffsetToTimes(offset, duration) {
2362
- return offset.map((o) => o * duration);
2363
- }
2364
-
2365
- function defaultEasing(values, easing) {
2366
- return values.map(() => easing || easeInOut).splice(0, values.length - 1);
2367
- }
2368
- function keyframes({ duration = 300, keyframes: keyframeValues, times, ease = "easeInOut", }) {
2369
- /**
2370
- * Easing functions can be externally defined as strings. Here we convert them
2371
- * into actual functions.
2372
- */
2373
- const easingFunctions = isEasingArray(ease)
2374
- ? ease.map(easingDefinitionToFunction)
2375
- : easingDefinitionToFunction(ease);
2376
- /**
2377
- * This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
2378
- * to reduce GC during animation.
2379
- */
2380
- const state = {
2381
- done: false,
2382
- value: keyframeValues[0],
2383
- };
2384
- /**
2385
- * Create a times array based on the provided 0-1 offsets
2386
- */
2387
- const absoluteTimes = convertOffsetToTimes(
2388
- // Only use the provided offsets if they're the correct length
2389
- // TODO Maybe we should warn here if there's a length mismatch
2390
- times && times.length === keyframeValues.length
2391
- ? times
2392
- : defaultOffset$1(keyframeValues), duration);
2393
- const mapTimeToKeyframe = interpolate(absoluteTimes, keyframeValues, {
2394
- ease: Array.isArray(easingFunctions)
2395
- ? easingFunctions
2396
- : defaultEasing(keyframeValues, easingFunctions),
2397
- });
2398
- return {
2399
- calculatedDuration: duration,
2400
- next: (t) => {
2401
- state.value = mapTimeToKeyframe(t);
2402
- state.done = t >= duration;
2403
- return state;
2404
- },
2405
- };
2406
- }
2407
-
2408
- const frameloopDriver = (update) => {
2409
- const passTimestamp = ({ timestamp }) => update(timestamp);
2410
- return {
2411
- start: () => motionDom.frame.update(passTimestamp, true),
2412
- stop: () => motionDom.cancelFrame(passTimestamp),
2413
- /**
2414
- * If we're processing this frame we can use the
2415
- * framelocked timestamp to keep things in sync.
2416
- */
2417
- now: () => (motionDom.frameData.isProcessing ? motionDom.frameData.timestamp : motionDom.time.now()),
2418
- };
2419
- };
2420
-
2421
- const generators = {
2422
- decay: inertia,
2423
- inertia,
2424
- tween: keyframes,
2425
- keyframes: keyframes,
2426
- spring,
2427
- };
2428
- const percentToProgress = (percent) => percent / 100;
2429
- /**
2430
- * Animation that runs on the main thread. Designed to be WAAPI-spec in the subset of
2431
- * features we expose publically. Mostly the compatibility is to ensure visual identity
2432
- * between both WAAPI and main thread animations.
2433
- */
2434
- class MainThreadAnimation extends BaseAnimation {
2435
- constructor(options) {
2436
- super(options);
2437
- /**
2438
- * The time at which the animation was paused.
2439
- */
2440
- this.holdTime = null;
2441
- /**
2442
- * The time at which the animation was cancelled.
2443
- */
2444
- this.cancelTime = null;
2445
- /**
2446
- * The current time of the animation.
2447
- */
2448
- this.currentTime = 0;
2449
- /**
2450
- * Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
2451
- */
2452
- this.playbackSpeed = 1;
2453
- /**
2454
- * The state of the animation to apply when the animation is resolved. This
2455
- * allows calls to the public API to control the animation before it is resolved,
2456
- * without us having to resolve it first.
2457
- */
2458
- this.pendingPlayState = "running";
2459
- /**
2460
- * The time at which the animation was started.
2461
- */
2462
- this.startTime = null;
2463
- this.state = "idle";
2464
- /**
2465
- * This method is bound to the instance to fix a pattern where
2466
- * animation.stop is returned as a reference from a useEffect.
2467
- */
2468
- this.stop = () => {
2469
- this.resolver.cancel();
2470
- this.isStopped = true;
2471
- if (this.state === "idle")
2472
- return;
2473
- this.teardown();
2474
- const { onStop } = this.options;
2475
- onStop && onStop();
2476
- };
2477
- const { name, motionValue, element, keyframes } = this.options;
2478
- const KeyframeResolver$1 = element?.KeyframeResolver || KeyframeResolver;
2479
- const onResolved = (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe);
2480
- this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
2481
- this.resolver.scheduleResolve();
2482
- }
2483
- flatten() {
2484
- super.flatten();
2485
- // If we've already resolved the animation, re-initialise it
2486
- if (this._resolved) {
2487
- Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
2488
- }
2489
- }
2490
- initPlayback(keyframes$1) {
2491
- const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
2492
- const generatorFactory = motionDom.isGenerator(type)
2493
- ? type
2494
- : generators[type] || keyframes;
2495
- /**
2496
- * If our generator doesn't support mixing numbers, we need to replace keyframes with
2497
- * [0, 100] and then make a function that maps that to the actual keyframes.
2498
- *
2499
- * 100 is chosen instead of 1 as it works nicer with spring animations.
2500
- */
2501
- let mapPercentToKeyframes;
2502
- let mirroredGenerator;
2503
- if (process.env.NODE_ENV !== "production" &&
2504
- generatorFactory !== keyframes) {
2505
- motionUtils.invariant(keyframes$1.length <= 2, `Only two keyframes currently supported with spring and inertia animations. Trying to animate ${keyframes$1}`);
2506
- }
2507
- if (generatorFactory !== keyframes &&
2508
- typeof keyframes$1[0] !== "number") {
2509
- mapPercentToKeyframes = pipe(percentToProgress, mix(keyframes$1[0], keyframes$1[1]));
2510
- keyframes$1 = [0, 100];
2511
- }
2512
- const generator = generatorFactory({ ...this.options, keyframes: keyframes$1 });
2513
- /**
2514
- * If we have a mirror repeat type we need to create a second generator that outputs the
2515
- * mirrored (not reversed) animation and later ping pong between the two generators.
2516
- */
2517
- if (repeatType === "mirror") {
2518
- mirroredGenerator = generatorFactory({
2519
- ...this.options,
2520
- keyframes: [...keyframes$1].reverse(),
2521
- velocity: -velocity,
2522
- });
2523
- }
2524
- /**
2525
- * If duration is undefined and we have repeat options,
2526
- * we need to calculate a duration from the generator.
2527
- *
2528
- * We set it to the generator itself to cache the duration.
2529
- * Any timeline resolver will need to have already precalculated
2530
- * the duration by this step.
2531
- */
2532
- if (generator.calculatedDuration === null) {
2533
- generator.calculatedDuration = motionDom.calcGeneratorDuration(generator);
2534
- }
2535
- const { calculatedDuration } = generator;
2536
- const resolvedDuration = calculatedDuration + repeatDelay;
2537
- const totalDuration = resolvedDuration * (repeat + 1) - repeatDelay;
2538
- return {
2539
- generator,
2540
- mirroredGenerator,
2541
- mapPercentToKeyframes,
2542
- calculatedDuration,
2543
- resolvedDuration,
2544
- totalDuration,
2545
- };
2546
- }
2547
- onPostResolved() {
2548
- const { autoplay = true } = this.options;
2549
- motionDom.activeAnimations.mainThread++;
2550
- this.play();
2551
- if (this.pendingPlayState === "paused" || !autoplay) {
2552
- this.pause();
2553
- }
2554
- else {
2555
- this.state = this.pendingPlayState;
2556
- }
2557
- }
2558
- tick(timestamp, sample = false) {
2559
- const { resolved } = this;
2560
- // If the animations has failed to resolve, return the final keyframe.
2561
- if (!resolved) {
2562
- const { keyframes } = this.options;
2563
- return { done: true, value: keyframes[keyframes.length - 1] };
2564
- }
2565
- const { finalKeyframe, generator, mirroredGenerator, mapPercentToKeyframes, keyframes, calculatedDuration, totalDuration, resolvedDuration, } = resolved;
2566
- if (this.startTime === null)
2567
- return generator.next(0);
2568
- const { delay, repeat, repeatType, repeatDelay, onUpdate } = this.options;
2569
- /**
2570
- * requestAnimationFrame timestamps can come through as lower than
2571
- * the startTime as set by performance.now(). Here we prevent this,
2572
- * though in the future it could be possible to make setting startTime
2573
- * a pending operation that gets resolved here.
2574
- */
2575
- if (this.speed > 0) {
2576
- this.startTime = Math.min(this.startTime, timestamp);
2577
- }
2578
- else if (this.speed < 0) {
2579
- this.startTime = Math.min(timestamp - totalDuration / this.speed, this.startTime);
2580
- }
2581
- // Update currentTime
2582
- if (sample) {
2583
- this.currentTime = timestamp;
2584
- }
2585
- else if (this.holdTime !== null) {
2586
- this.currentTime = this.holdTime;
2587
- }
2588
- else {
2589
- // Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
2590
- // 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
2591
- // example.
2592
- this.currentTime =
2593
- Math.round(timestamp - this.startTime) * this.speed;
2594
- }
2595
- // Rebase on delay
2596
- const timeWithoutDelay = this.currentTime - delay * (this.speed >= 0 ? 1 : -1);
2597
- const isInDelayPhase = this.speed >= 0
2598
- ? timeWithoutDelay < 0
2599
- : timeWithoutDelay > totalDuration;
2600
- this.currentTime = Math.max(timeWithoutDelay, 0);
2601
- // If this animation has finished, set the current time to the total duration.
2602
- if (this.state === "finished" && this.holdTime === null) {
2603
- this.currentTime = totalDuration;
2604
- }
2605
- let elapsed = this.currentTime;
2606
- let frameGenerator = generator;
2607
- if (repeat) {
2608
- /**
2609
- * Get the current progress (0-1) of the animation. If t is >
2610
- * than duration we'll get values like 2.5 (midway through the
2611
- * third iteration)
2612
- */
2613
- const progress = Math.min(this.currentTime, totalDuration) / resolvedDuration;
2614
- /**
2615
- * Get the current iteration (0 indexed). For instance the floor of
2616
- * 2.5 is 2.
2617
- */
2618
- let currentIteration = Math.floor(progress);
2619
- /**
2620
- * Get the current progress of the iteration by taking the remainder
2621
- * so 2.5 is 0.5 through iteration 2
2622
- */
2623
- let iterationProgress = progress % 1.0;
2624
- /**
2625
- * If iteration progress is 1 we count that as the end
2626
- * of the previous iteration.
2627
- */
2628
- if (!iterationProgress && progress >= 1) {
2629
- iterationProgress = 1;
2630
- }
2631
- iterationProgress === 1 && currentIteration--;
2632
- currentIteration = Math.min(currentIteration, repeat + 1);
2633
- /**
2634
- * Reverse progress if we're not running in "normal" direction
2635
- */
2636
- const isOddIteration = Boolean(currentIteration % 2);
2637
- if (isOddIteration) {
2638
- if (repeatType === "reverse") {
2639
- iterationProgress = 1 - iterationProgress;
2640
- if (repeatDelay) {
2641
- iterationProgress -= repeatDelay / resolvedDuration;
2642
- }
2643
- }
2644
- else if (repeatType === "mirror") {
2645
- frameGenerator = mirroredGenerator;
2646
- }
2647
- }
2648
- elapsed = clamp(0, 1, iterationProgress) * resolvedDuration;
2649
- }
2650
- /**
2651
- * If we're in negative time, set state as the initial keyframe.
2652
- * This prevents delay: x, duration: 0 animations from finishing
2653
- * instantly.
2654
- */
2655
- const state = isInDelayPhase
2656
- ? { done: false, value: keyframes[0] }
2657
- : frameGenerator.next(elapsed);
2658
- if (mapPercentToKeyframes) {
2659
- state.value = mapPercentToKeyframes(state.value);
2660
- }
2661
- let { done } = state;
2662
- if (!isInDelayPhase && calculatedDuration !== null) {
2663
- done =
2664
- this.speed >= 0
2665
- ? this.currentTime >= totalDuration
2666
- : this.currentTime <= 0;
2667
- }
2668
- const isAnimationFinished = this.holdTime === null &&
2669
- (this.state === "finished" || (this.state === "running" && done));
2670
- if (isAnimationFinished && finalKeyframe !== undefined) {
2671
- state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe);
2672
- }
2673
- if (onUpdate) {
2674
- onUpdate(state.value);
2675
- }
2676
- if (isAnimationFinished) {
2677
- this.finish();
2678
- }
2679
- return state;
2680
- }
2681
- get duration() {
2682
- const { resolved } = this;
2683
- return resolved ? motionUtils.millisecondsToSeconds(resolved.calculatedDuration) : 0;
2684
- }
2685
- get time() {
2686
- return motionUtils.millisecondsToSeconds(this.currentTime);
2687
- }
2688
- set time(newTime) {
2689
- newTime = motionUtils.secondsToMilliseconds(newTime);
2690
- this.currentTime = newTime;
2691
- if (this.holdTime !== null || this.speed === 0) {
2692
- this.holdTime = newTime;
2693
- }
2694
- else if (this.driver) {
2695
- this.startTime = this.driver.now() - newTime / this.speed;
2696
- }
2697
- }
2698
- get speed() {
2699
- return this.playbackSpeed;
2700
- }
2701
- set speed(newSpeed) {
2702
- const hasChanged = this.playbackSpeed !== newSpeed;
2703
- this.playbackSpeed = newSpeed;
2704
- if (hasChanged) {
2705
- this.time = motionUtils.millisecondsToSeconds(this.currentTime);
2706
- }
2707
- }
2708
- play() {
2709
- if (!this.resolver.isScheduled) {
2710
- this.resolver.resume();
2711
- }
2712
- if (!this._resolved) {
2713
- this.pendingPlayState = "running";
2714
- return;
2715
- }
2716
- if (this.isStopped)
2717
- return;
2718
- const { driver = frameloopDriver, onPlay, startTime } = this.options;
2719
- if (!this.driver) {
2720
- this.driver = driver((timestamp) => this.tick(timestamp));
2721
- }
2722
- onPlay && onPlay();
2723
- const now = this.driver.now();
2724
- if (this.holdTime !== null) {
2725
- this.startTime = now - this.holdTime;
2726
- }
2727
- else if (!this.startTime) {
2728
- this.startTime = startTime ?? this.calcStartTime();
2729
- }
2730
- else if (this.state === "finished") {
2731
- this.startTime = now;
2732
- }
2733
- if (this.state === "finished") {
2734
- this.updateFinishedPromise();
2735
- }
2736
- this.cancelTime = this.startTime;
2737
- this.holdTime = null;
2738
- /**
2739
- * Set playState to running only after we've used it in
2740
- * the previous logic.
2741
- */
2742
- this.state = "running";
2743
- this.driver.start();
2744
- }
2745
- pause() {
2746
- if (!this._resolved) {
2747
- this.pendingPlayState = "paused";
2748
- return;
2749
- }
2750
- this.state = "paused";
2751
- this.holdTime = this.currentTime ?? 0;
2752
- }
2753
- complete() {
2754
- if (this.state !== "running") {
2755
- this.play();
2756
- }
2757
- this.pendingPlayState = this.state = "finished";
2758
- this.holdTime = null;
2759
- }
2760
- finish() {
2761
- this.teardown();
2762
- this.state = "finished";
2763
- const { onComplete } = this.options;
2764
- onComplete && onComplete();
2765
- }
2766
- cancel() {
2767
- if (this.cancelTime !== null) {
2768
- this.tick(this.cancelTime);
2769
- }
2770
- this.teardown();
2771
- this.updateFinishedPromise();
2772
- }
2773
- teardown() {
2774
- this.state = "idle";
2775
- this.stopDriver();
2776
- this.resolveFinishedPromise();
2777
- this.updateFinishedPromise();
2778
- this.startTime = this.cancelTime = null;
2779
- this.resolver.cancel();
2780
- motionDom.activeAnimations.mainThread--;
2781
- }
2782
- stopDriver() {
2783
- if (!this.driver)
2784
- return;
2785
- this.driver.stop();
2786
- this.driver = undefined;
2787
- }
2788
- sample(time) {
2789
- this.startTime = 0;
2790
- return this.tick(time, true);
2791
- }
2792
- get finished() {
2793
- return this.currentFinishedPromise;
2794
- }
2795
- }
340
+ const isNumberKeyframesArray = (keyframes) => keyframes.every(isNumber);
2796
341
 
2797
- /**
2798
- * A list of values that can be hardware-accelerated.
2799
- */
2800
- const acceleratedValues = new Set([
2801
- "opacity",
2802
- "clipPath",
2803
- "filter",
2804
- "transform",
2805
- // TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
2806
- // or until we implement support for linear() easing.
2807
- // "background-color"
2808
- ]);
342
+ const visualElementStore = new WeakMap();
2809
343
 
2810
- const supportsWaapi = /*@__PURE__*/ motionUtils.memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
344
+ const isKeyframesTarget = (v) => {
345
+ return Array.isArray(v);
346
+ };
2811
347
 
2812
- /**
2813
- * 10ms is chosen here as it strikes a balance between smooth
2814
- * results (more than one keyframe per frame at 60fps) and
2815
- * keyframe quantity.
2816
- */
2817
- const sampleDelta = 10; //ms
2818
- /**
2819
- * Implement a practical max duration for keyframe generation
2820
- * to prevent infinite loops
2821
- */
2822
- const maxDuration = 20000;
2823
- /**
2824
- * Check if an animation can run natively via WAAPI or requires pregenerated keyframes.
2825
- * WAAPI doesn't support spring or function easings so we run these as JS animation before
2826
- * handing off.
2827
- */
2828
- function requiresPregeneratedKeyframes(options) {
2829
- return (motionDom.isGenerator(options.type) ||
2830
- options.type === "spring" ||
2831
- !motionDom.isWaapiSupportedEasing(options.ease));
348
+ const resolveFinalValueInKeyframes = (v) => {
349
+ // TODO maybe throw if v.length - 1 is placeholder token?
350
+ return isKeyframesTarget(v) ? v[v.length - 1] || 0 : v;
351
+ };
352
+
353
+ function getValueState(visualElement) {
354
+ const state = [{}, {}];
355
+ visualElement?.values.forEach((value, key) => {
356
+ state[0][key] = value.get();
357
+ state[1][key] = value.getVelocity();
358
+ });
359
+ return state;
2832
360
  }
2833
- function pregenerateKeyframes(keyframes, options) {
361
+ function resolveVariantFromProps(props, definition, custom, visualElement) {
2834
362
  /**
2835
- * Create a main-thread animation to pregenerate keyframes.
2836
- * We sample this at regular intervals to generate keyframes that we then
2837
- * linearly interpolate between.
363
+ * If the variant definition is a function, resolve.
2838
364
  */
2839
- const sampleAnimation = new MainThreadAnimation({
2840
- ...options,
2841
- keyframes,
2842
- repeat: 0,
2843
- delay: 0,
2844
- isGenerator: true,
2845
- });
2846
- let state = { done: false, value: keyframes[0] };
2847
- const pregeneratedKeyframes = [];
365
+ if (typeof definition === "function") {
366
+ const [current, velocity] = getValueState(visualElement);
367
+ definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
368
+ }
2848
369
  /**
2849
- * Bail after 20 seconds of pre-generated keyframes as it's likely
2850
- * we're heading for an infinite loop.
370
+ * If the variant definition is a variant label, or
371
+ * the function returned a variant label, resolve.
2851
372
  */
2852
- let t = 0;
2853
- while (!state.done && t < maxDuration) {
2854
- state = sampleAnimation.sample(t);
2855
- pregeneratedKeyframes.push(state.value);
2856
- t += sampleDelta;
373
+ if (typeof definition === "string") {
374
+ definition = props.variants && props.variants[definition];
2857
375
  }
2858
- return {
2859
- times: undefined,
2860
- keyframes: pregeneratedKeyframes,
2861
- duration: t - sampleDelta,
2862
- ease: "linear",
2863
- };
376
+ /**
377
+ * At this point we've resolved both functions and variant labels,
378
+ * but the resolved variant label might itself have been a function.
379
+ * If so, resolve. This can only have returned a valid target object.
380
+ */
381
+ if (typeof definition === "function") {
382
+ const [current, velocity] = getValueState(visualElement);
383
+ definition = definition(custom !== undefined ? custom : props.custom, current, velocity);
384
+ }
385
+ return definition;
2864
386
  }
2865
- const unsupportedEasingFunctions = {
2866
- anticipate,
2867
- backInOut,
2868
- circInOut,
2869
- };
2870
- function isUnsupportedEase(key) {
2871
- return key in unsupportedEasingFunctions;
387
+
388
+ function resolveVariant(visualElement, definition, custom) {
389
+ const props = visualElement.getProps();
390
+ return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, visualElement);
2872
391
  }
2873
- class AcceleratedAnimation extends BaseAnimation {
2874
- constructor(options) {
2875
- super(options);
2876
- const { name, motionValue, element, keyframes } = this.options;
2877
- this.resolver = new DOMKeyframesResolver(keyframes, (resolvedKeyframes, finalKeyframe) => this.onKeyframesResolved(resolvedKeyframes, finalKeyframe), name, motionValue, element);
2878
- this.resolver.scheduleResolve();
2879
- }
2880
- initPlayback(keyframes, finalKeyframe) {
2881
- let { duration = 300, times, ease, type, motionValue, name, startTime, } = this.options;
2882
- /**
2883
- * If element has since been unmounted, return false to indicate
2884
- * the animation failed to initialised.
2885
- */
2886
- if (!motionValue.owner || !motionValue.owner.current) {
2887
- return false;
2888
- }
2889
- /**
2890
- * If the user has provided an easing function name that isn't supported
2891
- * by WAAPI (like "anticipate"), we need to provide the corressponding
2892
- * function. This will later get converted to a linear() easing function.
2893
- */
2894
- if (typeof ease === "string" &&
2895
- motionDom.supportsLinearEasing() &&
2896
- isUnsupportedEase(ease)) {
2897
- ease = unsupportedEasingFunctions[ease];
2898
- }
2899
- /**
2900
- * If this animation needs pre-generated keyframes then generate.
2901
- */
2902
- if (requiresPregeneratedKeyframes(this.options)) {
2903
- const { onComplete, onUpdate, motionValue, element, ...options } = this.options;
2904
- const pregeneratedAnimation = pregenerateKeyframes(keyframes, options);
2905
- keyframes = pregeneratedAnimation.keyframes;
2906
- // If this is a very short animation, ensure we have
2907
- // at least two keyframes to animate between as older browsers
2908
- // can't animate between a single keyframe.
2909
- if (keyframes.length === 1) {
2910
- keyframes[1] = keyframes[0];
2911
- }
2912
- duration = pregeneratedAnimation.duration;
2913
- times = pregeneratedAnimation.times;
2914
- ease = pregeneratedAnimation.ease;
2915
- type = "keyframes";
2916
- }
2917
- const animation = motionDom.startWaapiAnimation(motionValue.owner.current, name, keyframes, { ...this.options, duration, times, ease });
2918
- // Override the browser calculated startTime with one synchronised to other JS
2919
- // and WAAPI animations starting this event loop.
2920
- animation.startTime = startTime ?? this.calcStartTime();
2921
- if (this.pendingTimeline) {
2922
- motionDom.attachTimeline(animation, this.pendingTimeline);
2923
- this.pendingTimeline = undefined;
2924
- }
2925
- else {
2926
- /**
2927
- * Prefer the `onfinish` prop as it's more widely supported than
2928
- * the `finished` promise.
2929
- *
2930
- * Here, we synchronously set the provided MotionValue to the end
2931
- * keyframe. If we didn't, when the WAAPI animation is finished it would
2932
- * be removed from the element which would then revert to its old styles.
2933
- */
2934
- animation.onfinish = () => {
2935
- const { onComplete } = this.options;
2936
- motionValue.set(getFinalKeyframe(keyframes, this.options, finalKeyframe));
2937
- onComplete && onComplete();
2938
- this.cancel();
2939
- this.resolveFinishedPromise();
2940
- };
2941
- }
2942
- return {
2943
- animation,
2944
- duration,
2945
- times,
2946
- type,
2947
- ease,
2948
- keyframes: keyframes,
2949
- };
2950
- }
2951
- get duration() {
2952
- const { resolved } = this;
2953
- if (!resolved)
2954
- return 0;
2955
- const { duration } = resolved;
2956
- return motionUtils.millisecondsToSeconds(duration);
2957
- }
2958
- get time() {
2959
- const { resolved } = this;
2960
- if (!resolved)
2961
- return 0;
2962
- const { animation } = resolved;
2963
- return motionUtils.millisecondsToSeconds(animation.currentTime || 0);
2964
- }
2965
- set time(newTime) {
2966
- const { resolved } = this;
2967
- if (!resolved)
2968
- return;
2969
- const { animation } = resolved;
2970
- animation.currentTime = motionUtils.secondsToMilliseconds(newTime);
2971
- }
2972
- get speed() {
2973
- const { resolved } = this;
2974
- if (!resolved)
2975
- return 1;
2976
- const { animation } = resolved;
2977
- return animation.playbackRate;
392
+
393
+ /**
394
+ * Set VisualElement's MotionValue, creating a new MotionValue for it if
395
+ * it doesn't exist.
396
+ */
397
+ function setMotionValue(visualElement, key, value) {
398
+ if (visualElement.hasValue(key)) {
399
+ visualElement.getValue(key).set(value);
2978
400
  }
2979
- get finished() {
2980
- return this.resolved.animation.finished;
401
+ else {
402
+ visualElement.addValue(key, motionDom.motionValue(value));
2981
403
  }
2982
- set speed(newSpeed) {
2983
- const { resolved } = this;
2984
- if (!resolved)
2985
- return;
2986
- const { animation } = resolved;
2987
- animation.playbackRate = newSpeed;
2988
- }
2989
- get state() {
2990
- const { resolved } = this;
2991
- if (!resolved)
2992
- return "idle";
2993
- const { animation } = resolved;
2994
- return animation.playState;
2995
- }
2996
- get startTime() {
2997
- const { resolved } = this;
2998
- if (!resolved)
2999
- return null;
3000
- const { animation } = resolved;
3001
- // Coerce to number as TypeScript incorrectly types this
3002
- // as CSSNumberish
3003
- return animation.startTime;
404
+ }
405
+ function setTarget(visualElement, definition) {
406
+ const resolved = resolveVariant(visualElement, definition);
407
+ let { transitionEnd = {}, transition = {}, ...target } = resolved || {};
408
+ target = { ...target, ...transitionEnd };
409
+ for (const key in target) {
410
+ const value = resolveFinalValueInKeyframes(target[key]);
411
+ setMotionValue(visualElement, key, value);
3004
412
  }
413
+ }
414
+
415
+ function isWillChangeMotionValue(value) {
416
+ return Boolean(isMotionValue(value) && value.add);
417
+ }
418
+
419
+ function addValueToWillChange(visualElement, key) {
420
+ const willChange = visualElement.getValue("willChange");
3005
421
  /**
3006
- * Replace the default DocumentTimeline with another AnimationTimeline.
3007
- * Currently used for scroll animations.
422
+ * It could be that a user has set willChange to a regular MotionValue,
423
+ * in which case we can't add the value to it.
3008
424
  */
3009
- attachTimeline(timeline) {
3010
- if (!this._resolved) {
3011
- this.pendingTimeline = timeline;
3012
- }
3013
- else {
3014
- const { resolved } = this;
3015
- if (!resolved)
3016
- return motionUtils.noop;
3017
- const { animation } = resolved;
3018
- motionDom.attachTimeline(animation, timeline);
3019
- }
3020
- return motionUtils.noop;
3021
- }
3022
- play() {
3023
- if (this.isStopped)
3024
- return;
3025
- const { resolved } = this;
3026
- if (!resolved)
3027
- return;
3028
- const { animation } = resolved;
3029
- if (animation.playState === "finished") {
3030
- this.updateFinishedPromise();
3031
- }
3032
- animation.play();
3033
- }
3034
- pause() {
3035
- const { resolved } = this;
3036
- if (!resolved)
3037
- return;
3038
- const { animation } = resolved;
3039
- animation.pause();
3040
- }
3041
- stop() {
3042
- this.resolver.cancel();
3043
- this.isStopped = true;
3044
- if (this.state === "idle")
3045
- return;
3046
- this.resolveFinishedPromise();
3047
- this.updateFinishedPromise();
3048
- const { resolved } = this;
3049
- if (!resolved)
3050
- return;
3051
- const { animation, keyframes, duration, type, ease, times } = resolved;
3052
- if (animation.playState === "idle" ||
3053
- animation.playState === "finished") {
3054
- return;
3055
- }
3056
- /**
3057
- * WAAPI doesn't natively have any interruption capabilities.
3058
- *
3059
- * Rather than read commited styles back out of the DOM, we can
3060
- * create a renderless JS animation and sample it twice to calculate
3061
- * its current value, "previous" value, and therefore allow
3062
- * Motion to calculate velocity for any subsequent animation.
3063
- */
3064
- if (this.time) {
3065
- const { motionValue, onUpdate, onComplete, element, ...options } = this.options;
3066
- const sampleAnimation = new MainThreadAnimation({
3067
- ...options,
3068
- keyframes,
3069
- duration,
3070
- type,
3071
- ease,
3072
- times,
3073
- isGenerator: true,
3074
- });
3075
- const sampleTime = motionUtils.secondsToMilliseconds(this.time);
3076
- motionValue.setWithVelocity(sampleAnimation.sample(sampleTime - sampleDelta).value, sampleAnimation.sample(sampleTime).value, sampleDelta);
3077
- }
3078
- const { onStop } = this.options;
3079
- onStop && onStop();
3080
- this.cancel();
3081
- }
3082
- complete() {
3083
- const { resolved } = this;
3084
- if (!resolved)
3085
- return;
3086
- resolved.animation.finish();
425
+ if (isWillChangeMotionValue(willChange)) {
426
+ return willChange.add(key);
3087
427
  }
3088
- cancel() {
3089
- const { resolved } = this;
3090
- if (!resolved)
3091
- return;
3092
- resolved.animation.cancel();
3093
- }
3094
- static supports(options) {
3095
- const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
3096
- if (!motionValue ||
3097
- !motionValue.owner ||
3098
- !(motionValue.owner.current instanceof HTMLElement)) {
3099
- return false;
3100
- }
3101
- const { onUpdate, transformTemplate } = motionValue.owner.getProps();
3102
- return (supportsWaapi() &&
3103
- name &&
3104
- acceleratedValues.has(name) &&
3105
- (name !== "transform" || !transformTemplate) &&
3106
- /**
3107
- * If we're outputting values to onUpdate then we can't use WAAPI as there's
3108
- * no way to read the value from WAAPI every frame.
3109
- */
3110
- !onUpdate &&
3111
- !repeatDelay &&
3112
- repeatType !== "mirror" &&
3113
- damping !== 0 &&
3114
- type !== "inertia");
428
+ else if (!willChange && motionUtils.MotionGlobalConfig.WillChange) {
429
+ const newWillChange = new motionUtils.MotionGlobalConfig.WillChange("auto");
430
+ visualElement.addValue("willChange", newWillChange);
431
+ newWillChange.add(key);
3115
432
  }
3116
433
  }
3117
434
 
435
+ /**
436
+ * Convert camelCase to dash-case properties.
437
+ */
438
+ const camelToDash = (str) => str.replace(/([a-z])([A-Z])/gu, "$1-$2").toLowerCase();
439
+
440
+ const optimizedAppearDataId = "framerAppearId";
441
+ const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
442
+
443
+ function getOptimisedAppearId(visualElement) {
444
+ return visualElement.props[optimizedAppearDataAttribute];
445
+ }
446
+
447
+ const isNotNull = (value) => value !== null;
448
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
449
+ const resolvedKeyframes = keyframes.filter(isNotNull);
450
+ const index = repeat && repeatType !== "loop" && repeat % 2 === 1
451
+ ? 0
452
+ : resolvedKeyframes.length - 1;
453
+ return !index || finalKeyframe === undefined
454
+ ? resolvedKeyframes[index]
455
+ : finalKeyframe;
456
+ }
457
+
3118
458
  const underDampedSpring = {
3119
459
  type: "spring",
3120
460
  stiffness: 500,
@@ -3144,7 +484,7 @@ const getDefaultTransition = (valueKey, { keyframes }) => {
3144
484
  if (keyframes.length > 2) {
3145
485
  return keyframesTransition;
3146
486
  }
3147
- else if (transformProps.has(valueKey)) {
487
+ else if (motionDom.transformProps.has(valueKey)) {
3148
488
  return valueKey.startsWith("scale")
3149
489
  ? criticallyDampedSpring(keyframes[1])
3150
490
  : underDampedSpring;
@@ -3175,7 +515,7 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
3175
515
  */
3176
516
  let { elapsed = 0 } = transition;
3177
517
  elapsed = elapsed - motionUtils.secondsToMilliseconds(delay);
3178
- let options = {
518
+ const options = {
3179
519
  keyframes: Array.isArray(target) ? target : [null, target],
3180
520
  ease: "easeOut",
3181
521
  velocity: value.getVelocity(),
@@ -3198,22 +538,18 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
3198
538
  * unique transition settings for this value.
3199
539
  */
3200
540
  if (!isTransitionDefined(valueTransition)) {
3201
- options = {
3202
- ...options,
3203
- ...getDefaultTransition(name, options),
3204
- };
541
+ Object.assign(options, getDefaultTransition(name, options));
3205
542
  }
3206
543
  /**
3207
544
  * Both WAAPI and our internal animation functions use durations
3208
545
  * as defined by milliseconds, while our external API defines them
3209
546
  * as seconds.
3210
547
  */
3211
- if (options.duration) {
3212
- options.duration = motionUtils.secondsToMilliseconds(options.duration);
3213
- }
3214
- if (options.repeatDelay) {
3215
- options.repeatDelay = motionUtils.secondsToMilliseconds(options.repeatDelay);
3216
- }
548
+ options.duration && (options.duration = motionUtils.secondsToMilliseconds(options.duration));
549
+ options.repeatDelay && (options.repeatDelay = motionUtils.secondsToMilliseconds(options.repeatDelay));
550
+ /**
551
+ * Support deprecated way to set initial value. Prefer keyframe syntax.
552
+ */
3217
553
  if (options.from !== undefined) {
3218
554
  options.keyframes[0] = options.from;
3219
555
  }
@@ -3225,7 +561,8 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
3225
561
  shouldSkip = true;
3226
562
  }
3227
563
  }
3228
- if (motionUtils.MotionGlobalConfig.skipAnimations) {
564
+ if (motionUtils.MotionGlobalConfig.instantAnimations ||
565
+ motionUtils.MotionGlobalConfig.skipAnimations) {
3229
566
  shouldSkip = true;
3230
567
  options.duration = 0;
3231
568
  options.delay = 0;
@@ -3247,22 +584,10 @@ const animateMotionValue = (name, value, target, transition = {}, element, isHan
3247
584
  options.onUpdate(finalKeyframe);
3248
585
  options.onComplete();
3249
586
  });
3250
- // We still want to return some animation controls here rather
3251
- // than returning undefined
3252
- return new motionDom.GroupAnimationWithThen([]);
587
+ return;
3253
588
  }
3254
589
  }
3255
- /**
3256
- * Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
3257
- * WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
3258
- * optimised animation.
3259
- */
3260
- if (!isHandoff && AcceleratedAnimation.supports(options)) {
3261
- return new AcceleratedAnimation(options);
3262
- }
3263
- else {
3264
- return new MainThreadAnimation(options);
3265
- }
590
+ return new motionDom.AsyncMotionValueAnimation(options);
3266
591
  };
3267
592
 
3268
593
  /**
@@ -3312,7 +637,7 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
3312
637
  }
3313
638
  }
3314
639
  addValueToWillChange(visualElement, key);
3315
- value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && positionalKeys.has(key)
640
+ value.start(animateMotionValue(key, value, valueTarget, visualElement.shouldReduceMotion && motionDom.positionalKeys.has(key)
3316
641
  ? { type: false }
3317
642
  : valueTransition, visualElement, isHandoff));
3318
643
  const animation = value.animation;
@@ -3388,15 +713,6 @@ function initPrefersReducedMotion() {
3388
713
  }
3389
714
  }
3390
715
 
3391
- /**
3392
- * A list of all ValueTypes
3393
- */
3394
- const valueTypes = [...dimensionValueTypes, color, complex];
3395
- /**
3396
- * Tests a value against the list of ValueTypes
3397
- */
3398
- const findValueType = (v) => valueTypes.find(testValueType(v));
3399
-
3400
716
  function isAnimationControls(v) {
3401
717
  return (v !== null &&
3402
718
  typeof v === "object" &&
@@ -3444,7 +760,7 @@ function updateMotionValuesFromProps(element, next, prev) {
3444
760
  * and warn against mismatches.
3445
761
  */
3446
762
  if (process.env.NODE_ENV === "development") {
3447
- motionUtils.warnOnce(nextValue.version === "12.7.3", `Attempting to mix Motion versions ${nextValue.version} with 12.7.3 may not work as expected.`);
763
+ motionUtils.warnOnce(nextValue.version === "12.7.5-alpha.0", `Attempting to mix Motion versions ${nextValue.version} with 12.7.5-alpha.0 may not work as expected.`);
3448
764
  }
3449
765
  }
3450
766
  else if (isMotionValue(prevValue)) {
@@ -3536,7 +852,7 @@ class VisualElement {
3536
852
  * value might be provided externally by the component via props.
3537
853
  */
3538
854
  this.values = new Map();
3539
- this.KeyframeResolver = KeyframeResolver;
855
+ this.KeyframeResolver = motionDom.KeyframeResolver;
3540
856
  /**
3541
857
  * Cleanup functions for active features (hover/tap/exit etc)
3542
858
  */
@@ -3664,7 +980,7 @@ class VisualElement {
3664
980
  if (this.valueSubscriptions.has(key)) {
3665
981
  this.valueSubscriptions.get(key)();
3666
982
  }
3667
- const valueIsTransform = transformProps.has(key);
983
+ const valueIsTransform = motionDom.transformProps.has(key);
3668
984
  if (valueIsTransform && this.onBindTransform) {
3669
985
  this.onBindTransform();
3670
986
  }
@@ -3874,12 +1190,12 @@ class VisualElement {
3874
1190
  this.readValueFromInstance(this.current, key, this.options);
3875
1191
  if (value !== undefined && value !== null) {
3876
1192
  if (typeof value === "string" &&
3877
- (isNumericalString(value) || isZeroValueString(value))) {
1193
+ (motionUtils.isNumericalString(value) || motionUtils.isZeroValueString(value))) {
3878
1194
  // If this is a number read as a string, ie "0" or "200", convert it to a number
3879
1195
  value = parseFloat(value);
3880
1196
  }
3881
- else if (!findValueType(value) && complex.test(target)) {
3882
- value = getAnimatableNone(key, target);
1197
+ else if (!motionDom.findValueType(value) && motionDom.complex.test(target)) {
1198
+ value = motionDom.getAnimatableNone(key, target);
3883
1199
  }
3884
1200
  this.setBaseTarget(key, isMotionValue(value) ? value.get() : value);
3885
1201
  }
@@ -3943,7 +1259,7 @@ class VisualElement {
3943
1259
  class DOMVisualElement extends VisualElement {
3944
1260
  constructor() {
3945
1261
  super(...arguments);
3946
- this.KeyframeResolver = DOMKeyframesResolver;
1262
+ this.KeyframeResolver = motionDom.DOMKeyframesResolver;
3947
1263
  }
3948
1264
  sortInstanceNodePosition(a, b) {
3949
1265
  /**
@@ -3978,22 +1294,13 @@ class DOMVisualElement extends VisualElement {
3978
1294
  }
3979
1295
  }
3980
1296
 
3981
- /**
3982
- * Provided a value and a ValueType, returns the value as that value type.
3983
- */
3984
- const getValueAsType = (value, type) => {
3985
- return type && typeof value === "number"
3986
- ? type.transform(value)
3987
- : value;
3988
- };
3989
-
3990
1297
  const translateAlias = {
3991
1298
  x: "translateX",
3992
1299
  y: "translateY",
3993
1300
  z: "translateZ",
3994
1301
  transformPerspective: "perspective",
3995
1302
  };
3996
- const numTransforms = transformPropOrder.length;
1303
+ const numTransforms = motionDom.transformPropOrder.length;
3997
1304
  /**
3998
1305
  * Build a CSS transform style from individual x/y/scale etc properties.
3999
1306
  *
@@ -4009,7 +1316,7 @@ function buildTransform(latestValues, transform, transformTemplate) {
4009
1316
  * are present to the transform string.
4010
1317
  */
4011
1318
  for (let i = 0; i < numTransforms; i++) {
4012
- const key = transformPropOrder[i];
1319
+ const key = motionDom.transformPropOrder[i];
4013
1320
  const value = latestValues[key];
4014
1321
  if (value === undefined)
4015
1322
  continue;
@@ -4021,7 +1328,7 @@ function buildTransform(latestValues, transform, transformTemplate) {
4021
1328
  valueIsDefault = parseFloat(value) === 0;
4022
1329
  }
4023
1330
  if (!valueIsDefault || transformTemplate) {
4024
- const valueAsType = getValueAsType(value, numberValueTypes[key]);
1331
+ const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]);
4025
1332
  if (!valueIsDefault) {
4026
1333
  transformIsDefault = false;
4027
1334
  const transformName = translateAlias[key] || key;
@@ -4057,18 +1364,18 @@ function buildHTMLStyles(state, latestValues, transformTemplate) {
4057
1364
  */
4058
1365
  for (const key in latestValues) {
4059
1366
  const value = latestValues[key];
4060
- if (transformProps.has(key)) {
1367
+ if (motionDom.transformProps.has(key)) {
4061
1368
  // If this is a transform, flag to enable further transform processing
4062
1369
  hasTransform = true;
4063
1370
  continue;
4064
1371
  }
4065
- else if (isCSSVariableName(key)) {
1372
+ else if (motionDom.isCSSVariableName(key)) {
4066
1373
  vars[key] = value;
4067
1374
  continue;
4068
1375
  }
4069
1376
  else {
4070
1377
  // Convert the value to its default value type, ie 0 -> "0px"
4071
- const valueAsType = getValueAsType(value, numberValueTypes[key]);
1378
+ const valueAsType = motionDom.getValueAsType(value, motionDom.numberValueTypes[key]);
4072
1379
  if (key.startsWith("origin")) {
4073
1380
  // If this is a transform origin, flag and enable further transform-origin processing
4074
1381
  hasTransformOrigin = true;
@@ -4124,17 +1431,17 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
4124
1431
  // when defining props on a React component.
4125
1432
  const keys = useDashCase ? dashKeys : camelKeys;
4126
1433
  // Build the dash offset
4127
- attrs[keys.offset] = px.transform(-offset);
1434
+ attrs[keys.offset] = motionDom.px.transform(-offset);
4128
1435
  // Build the dash array
4129
- const pathLength = px.transform(length);
4130
- const pathSpacing = px.transform(spacing);
1436
+ const pathLength = motionDom.px.transform(length);
1437
+ const pathSpacing = motionDom.px.transform(spacing);
4131
1438
  attrs[keys.array] = `${pathLength} ${pathSpacing}`;
4132
1439
  }
4133
1440
 
4134
1441
  function calcOrigin(origin, offset, size) {
4135
1442
  return typeof origin === "string"
4136
1443
  ? origin
4137
- : px.transform(offset + size * origin);
1444
+ : motionDom.px.transform(offset + size * origin);
4138
1445
  }
4139
1446
  /**
4140
1447
  * The SVG transform origin defaults are different to CSS and is less intuitive,
@@ -4260,7 +1567,7 @@ function renderSVG(element, renderState, _styleProp, projection) {
4260
1567
  const scaleCorrectors = {};
4261
1568
 
4262
1569
  function isForcedMotionValue(key, { layout, layoutId }) {
4263
- return (transformProps.has(key) ||
1570
+ return (motionDom.transformProps.has(key) ||
4264
1571
  key.startsWith("origin") ||
4265
1572
  ((layout || layoutId !== undefined) &&
4266
1573
  (!!scaleCorrectors[key] || key === "opacity")));
@@ -4286,7 +1593,7 @@ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
4286
1593
  for (const key in props) {
4287
1594
  if (isMotionValue(props[key]) ||
4288
1595
  isMotionValue(prevProps[key])) {
4289
- const targetKey = transformPropOrder.indexOf(key) !== -1
1596
+ const targetKey = motionDom.transformPropOrder.indexOf(key) !== -1
4290
1597
  ? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
4291
1598
  : key;
4292
1599
  newValues[targetKey] = props[key];
@@ -4311,8 +1618,8 @@ class SVGVisualElement extends DOMVisualElement {
4311
1618
  return props[key];
4312
1619
  }
4313
1620
  readValueFromInstance(instance, key) {
4314
- if (transformProps.has(key)) {
4315
- const defaultType = getDefaultValueType(key);
1621
+ if (motionDom.transformProps.has(key)) {
1622
+ const defaultType = motionDom.getDefaultValueType(key);
4316
1623
  return defaultType ? defaultType.default || 0 : 0;
4317
1624
  }
4318
1625
  key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
@@ -4381,12 +1688,12 @@ class HTMLVisualElement extends DOMVisualElement {
4381
1688
  this.renderInstance = renderHTML;
4382
1689
  }
4383
1690
  readValueFromInstance(instance, key) {
4384
- if (transformProps.has(key)) {
4385
- return readTransformValue(instance, key);
1691
+ if (motionDom.transformProps.has(key)) {
1692
+ return motionDom.readTransformValue(instance, key);
4386
1693
  }
4387
1694
  else {
4388
1695
  const computedStyle = getComputedStyle$1(instance);
4389
- const value = (isCSSVariableName(key)
1696
+ const value = (motionDom.isCSSVariableName(key)
4390
1697
  ? computedStyle.getPropertyValue(key)
4391
1698
  : computedStyle[key]) || 0;
4392
1699
  return typeof value === "string" ? value.trim() : value;
@@ -4527,7 +1834,7 @@ function animateSubject(subject, keyframes, options, scope) {
4527
1834
 
4528
1835
  function animateSequence(sequence, options, scope) {
4529
1836
  const animations = [];
4530
- const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring });
1837
+ const animationDefinitions = createAnimationsFromSequence(sequence, options, scope, { spring: motionDom.spring });
4531
1838
  animationDefinitions.forEach(({ keyframes, transition }, subject) => {
4532
1839
  animations.push(...animateSubject(subject, keyframes, transition));
4533
1840
  });
@@ -4567,7 +1874,27 @@ function animateElements(elementOrSelector, keyframes, options, scope) {
4567
1874
  const elements = motionDom.resolveElements(elementOrSelector, scope);
4568
1875
  const numElements = elements.length;
4569
1876
  motionUtils.invariant(Boolean(numElements), "No valid element provided.");
4570
- const animations = [];
1877
+ /**
1878
+ * WAAPI doesn't support interrupting animations.
1879
+ *
1880
+ * Therefore, starting animations requires a three-step process:
1881
+ * 1. Stop existing animations (write styles to DOM)
1882
+ * 2. Resolve keyframes (read styles from DOM)
1883
+ * 3. Create new animations (write styles to DOM)
1884
+ *
1885
+ * The hybrid `animate()` function uses AsyncAnimation to resolve
1886
+ * keyframes before creating new animations, which removes style
1887
+ * thrashing. Here, we have much stricter filesize constraints.
1888
+ * Therefore we do this in a synchronous way that ensures that
1889
+ * at least within `animate()` calls there is no style thrashing.
1890
+ *
1891
+ * In the motion-native-animate-mini-interrupt benchmark this
1892
+ * was 80% faster than a single loop.
1893
+ */
1894
+ const animationDefinitions = [];
1895
+ /**
1896
+ * Step 1: Build options and stop existing animations (write)
1897
+ */
4571
1898
  for (let i = 0; i < numElements; i++) {
4572
1899
  const element = elements[i];
4573
1900
  const elementTransition = { ...options };
@@ -4578,20 +1905,68 @@ function animateElements(elementOrSelector, keyframes, options, scope) {
4578
1905
  elementTransition.delay = elementTransition.delay(i, numElements);
4579
1906
  }
4580
1907
  for (const valueName in keyframes) {
4581
- const valueKeyframes = keyframes[valueName];
1908
+ let valueKeyframes = keyframes[valueName];
1909
+ if (!Array.isArray(valueKeyframes)) {
1910
+ valueKeyframes = [valueKeyframes];
1911
+ }
4582
1912
  const valueOptions = {
4583
1913
  ...motionDom.getValueTransition(elementTransition, valueName),
4584
1914
  };
4585
1915
  valueOptions.duration && (valueOptions.duration = motionUtils.secondsToMilliseconds(valueOptions.duration));
4586
1916
  valueOptions.delay && (valueOptions.delay = motionUtils.secondsToMilliseconds(valueOptions.delay));
4587
- animations.push(new motionDom.NativeAnimation({
4588
- element,
4589
- name: valueName,
4590
- keyframes: valueKeyframes,
4591
- transition: valueOptions,
4592
- allowFlatten: !elementTransition.type && !elementTransition.ease,
4593
- }));
1917
+ /**
1918
+ * If there's an existing animation playing on this element then stop it
1919
+ * before creating a new one.
1920
+ */
1921
+ const map = motionDom.getAnimationMap(element);
1922
+ const key = motionDom.animationMapKey(valueName, valueOptions.pseudoElement || "");
1923
+ const currentAnimation = map.get(key);
1924
+ currentAnimation && currentAnimation.stop();
1925
+ animationDefinitions.push({
1926
+ map,
1927
+ key,
1928
+ unresolvedKeyframes: valueKeyframes,
1929
+ options: {
1930
+ ...valueOptions,
1931
+ element,
1932
+ name: valueName,
1933
+ allowFlatten: !elementTransition.type && !elementTransition.ease,
1934
+ },
1935
+ });
1936
+ }
1937
+ }
1938
+ /**
1939
+ * Step 2: Resolve keyframes (read)
1940
+ */
1941
+ for (let i = 0; i < animationDefinitions.length; i++) {
1942
+ const { unresolvedKeyframes, options: animationOptions } = animationDefinitions[i];
1943
+ const { element, name, pseudoElement } = animationOptions;
1944
+ if (!pseudoElement && unresolvedKeyframes[0] === null) {
1945
+ unresolvedKeyframes[0] = motionDom.getComputedStyle(element, name);
1946
+ }
1947
+ motionDom.fillWildcards(unresolvedKeyframes);
1948
+ motionDom.applyPxDefaults(unresolvedKeyframes, name);
1949
+ /**
1950
+ * If we only have one keyframe, explicitly read the initial keyframe
1951
+ * from the computed style. This is to ensure consistency with WAAPI behaviour
1952
+ * for restarting animations, for instance .play() after finish, when it
1953
+ * has one vs two keyframes.
1954
+ */
1955
+ if (!pseudoElement && unresolvedKeyframes.length < 2) {
1956
+ unresolvedKeyframes.unshift(motionDom.getComputedStyle(element, name));
4594
1957
  }
1958
+ animationOptions.keyframes = unresolvedKeyframes;
1959
+ }
1960
+ /**
1961
+ * Step 3: Create new animations (write)
1962
+ */
1963
+ const animations = [];
1964
+ for (let i = 0; i < animationDefinitions.length; i++) {
1965
+ const { map, key, options: animationOptions } = animationDefinitions[i];
1966
+ const animation = new motionDom.NativeAnimation(animationOptions);
1967
+ map.set(key, animation);
1968
+ animation.finished.finally(() => map.delete(key));
1969
+ animations.push(animation);
4595
1970
  }
4596
1971
  return animations;
4597
1972
  }
@@ -4604,21 +1979,6 @@ const createScopedWaapiAnimate = (scope) => {
4604
1979
  };
4605
1980
  const animateMini = /*@__PURE__*/ createScopedWaapiAnimate();
4606
1981
 
4607
- function observeTimeline(update, timeline) {
4608
- let prevProgress;
4609
- const onFrame = () => {
4610
- const { currentTime } = timeline;
4611
- const percentage = currentTime === null ? 0 : currentTime.value;
4612
- const progress = percentage / 100;
4613
- if (prevProgress !== progress) {
4614
- update(progress);
4615
- }
4616
- prevProgress = progress;
4617
- };
4618
- motionDom.frame.update(onFrame, true);
4619
- return () => motionDom.cancelFrame(onFrame);
4620
- }
4621
-
4622
1982
  const resizeHandlers = new WeakMap();
4623
1983
  let observer;
4624
1984
  function getElementSize(target, borderBoxSize) {
@@ -4949,10 +2309,10 @@ function resolveOffsets(container, info, options) {
4949
2309
  * to map scroll value into a progress.
4950
2310
  */
4951
2311
  if (hasChanged) {
4952
- info[axis].interpolate = interpolate(info[axis].offset, defaultOffset$1(offsetDefinition), { clamp: false });
2312
+ info[axis].interpolate = motionDom.interpolate(info[axis].offset, motionDom.defaultOffset(offsetDefinition), { clamp: false });
4953
2313
  info[axis].interpolatorOffsets = [...info[axis].offset];
4954
2314
  }
4955
- info[axis].progress = clamp(0, 1, info[axis].interpolate(info[axis].current));
2315
+ info[axis].progress = motionUtils.clamp(0, 1, info[axis].interpolate(info[axis].current));
4956
2316
  }
4957
2317
 
4958
2318
  function measure(container, target = container, info) {
@@ -5075,19 +2435,16 @@ function scrollInfo(onScroll, { container = document.documentElement, ...options
5075
2435
  };
5076
2436
  }
5077
2437
 
5078
- function scrollTimelineFallback({ source, container, axis = "y", }) {
5079
- // Support legacy source argument. Deprecate later.
5080
- if (source)
5081
- container = source;
5082
- // ScrollTimeline records progress as a percentage CSSUnitValue
2438
+ const timelineCache = new Map();
2439
+ function scrollTimelineFallback(options) {
5083
2440
  const currentTime = { value: 0 };
5084
2441
  const cancel = scrollInfo((info) => {
5085
- currentTime.value = info[axis].progress * 100;
5086
- }, { container, axis });
2442
+ currentTime.value = info[options.axis].progress * 100;
2443
+ }, options);
5087
2444
  return { currentTime, cancel };
5088
2445
  }
5089
- const timelineCache = new Map();
5090
- function getTimeline({ source, container = document.documentElement, axis = "y", } = {}) {
2446
+ function getTimeline({ source, container, ...options }) {
2447
+ const { axis } = options;
5091
2448
  // Support legacy source argument. Deprecate later.
5092
2449
  if (source)
5093
2450
  container = source;
@@ -5098,10 +2455,24 @@ function getTimeline({ source, container = document.documentElement, axis = "y",
5098
2455
  if (!elementCache[axis]) {
5099
2456
  elementCache[axis] = motionDom.supportsScrollTimeline()
5100
2457
  ? new ScrollTimeline({ source: container, axis })
5101
- : scrollTimelineFallback({ source: container, axis });
2458
+ : scrollTimelineFallback({ container, ...options });
5102
2459
  }
5103
2460
  return elementCache[axis];
5104
2461
  }
2462
+
2463
+ function attachToAnimation(animation, options) {
2464
+ const timeline = getTimeline(options);
2465
+ return animation.attachTimeline({
2466
+ timeline,
2467
+ observe: (valueAnimation) => {
2468
+ valueAnimation.pause();
2469
+ return motionDom.observeTimeline((progress) => {
2470
+ valueAnimation.time = valueAnimation.duration * progress;
2471
+ }, timeline);
2472
+ },
2473
+ });
2474
+ }
2475
+
5105
2476
  /**
5106
2477
  * If the onScroll function has two arguments, it's expecting
5107
2478
  * more specific information about the scroll from scrollInfo.
@@ -5109,51 +2480,22 @@ function getTimeline({ source, container = document.documentElement, axis = "y",
5109
2480
  function isOnScrollWithInfo(onScroll) {
5110
2481
  return onScroll.length === 2;
5111
2482
  }
5112
- /**
5113
- * Currently, we only support element tracking with `scrollInfo`, though in
5114
- * the future we can also offer ViewTimeline support.
5115
- */
5116
- function needsElementTracking(options) {
5117
- return options && (options.target || options.offset);
5118
- }
5119
- function scrollFunction(onScroll, options) {
5120
- if (isOnScrollWithInfo(onScroll) || needsElementTracking(options)) {
2483
+ function attachToFunction(onScroll, options) {
2484
+ if (isOnScrollWithInfo(onScroll)) {
5121
2485
  return scrollInfo((info) => {
5122
2486
  onScroll(info[options.axis].progress, info);
5123
2487
  }, options);
5124
2488
  }
5125
2489
  else {
5126
- return observeTimeline(onScroll, getTimeline(options));
2490
+ return motionDom.observeTimeline(onScroll, getTimeline(options));
5127
2491
  }
5128
2492
  }
5129
- function scrollAnimation(animation, options) {
5130
- animation.flatten();
5131
- if (needsElementTracking(options)) {
5132
- animation.pause();
5133
- return scrollInfo((info) => {
5134
- animation.time = animation.duration * info[options.axis].progress;
5135
- }, options);
5136
- }
5137
- else {
5138
- const timeline = getTimeline(options);
5139
- if (animation.attachTimeline) {
5140
- return animation.attachTimeline(timeline, (valueAnimation) => {
5141
- valueAnimation.pause();
5142
- return observeTimeline((progress) => {
5143
- valueAnimation.time = valueAnimation.duration * progress;
5144
- }, timeline);
5145
- });
5146
- }
5147
- else {
5148
- return motionUtils.noop;
5149
- }
5150
- }
5151
- }
5152
- function scroll(onScroll, { axis = "y", ...options } = {}) {
5153
- const optionsWithDefaults = { axis, ...options };
2493
+
2494
+ function scroll(onScroll, { axis = "y", container = document.documentElement, ...options } = {}) {
2495
+ const optionsWithDefaults = { axis, container, ...options };
5154
2496
  return typeof onScroll === "function"
5155
- ? scrollFunction(onScroll, optionsWithDefaults)
5156
- : scrollAnimation(onScroll, optionsWithDefaults);
2497
+ ? attachToFunction(onScroll, optionsWithDefaults)
2498
+ : attachToAnimation(onScroll, optionsWithDefaults);
5157
2499
  }
5158
2500
 
5159
2501
  const thresholds = {
@@ -5196,18 +2538,6 @@ function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount =
5196
2538
  return () => observer.disconnect();
5197
2539
  }
5198
2540
 
5199
- function steps(numSteps, direction = "end") {
5200
- return (progress) => {
5201
- progress =
5202
- direction === "end"
5203
- ? Math.min(progress, 0.999)
5204
- : Math.max(progress, 0.001);
5205
- const expanded = progress * numSteps;
5206
- const rounded = direction === "end" ? Math.floor(expanded) : Math.ceil(expanded);
5207
- return clamp(0, 1, rounded / numSteps);
5208
- };
5209
- }
5210
-
5211
2541
  function getOriginIndex(from, total) {
5212
2542
  if (from === "first") {
5213
2543
  return 0;
@@ -5224,7 +2554,7 @@ function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
5224
2554
  let delay = duration * distance;
5225
2555
  if (ease) {
5226
2556
  const maxDelay = total * duration;
5227
- const easingFunction = easingDefinitionToFunction(ease);
2557
+ const easingFunction = motionUtils.easingDefinitionToFunction(ease);
5228
2558
  delay = easingFunction(delay / maxDelay) * maxDelay;
5229
2559
  }
5230
2560
  return startDelay + delay;
@@ -5243,7 +2573,7 @@ function delay(callback, timeout) {
5243
2573
  callback(elapsed - timeout);
5244
2574
  }
5245
2575
  };
5246
- motionDom.frame.read(checkElapsed, true);
2576
+ motionDom.frame.setup(checkElapsed, true);
5247
2577
  return () => motionDom.cancelFrame(checkElapsed);
5248
2578
  }
5249
2579
  function delayInSeconds(callback, timeout) {
@@ -5269,99 +2599,33 @@ function transform(...args) {
5269
2599
  const inputRange = args[1 + argOffset];
5270
2600
  const outputRange = args[2 + argOffset];
5271
2601
  const options = args[3 + argOffset];
5272
- const interpolator = interpolate(inputRange, outputRange, {
2602
+ const interpolator = motionDom.interpolate(inputRange, outputRange, {
5273
2603
  mixer: getMixer(outputRange[0]),
5274
2604
  ...options,
5275
2605
  });
5276
2606
  return useImmediate ? interpolator(inputValue) : interpolator;
5277
2607
  }
5278
2608
 
5279
- Object.defineProperty(exports, "MotionValue", {
5280
- enumerable: true,
5281
- get: function () { return motionDom.MotionValue; }
5282
- });
5283
- Object.defineProperty(exports, "cancelFrame", {
5284
- enumerable: true,
5285
- get: function () { return motionDom.cancelFrame; }
5286
- });
5287
- Object.defineProperty(exports, "cancelSync", {
5288
- enumerable: true,
5289
- get: function () { return motionDom.cancelSync; }
5290
- });
5291
- Object.defineProperty(exports, "frame", {
5292
- enumerable: true,
5293
- get: function () { return motionDom.frame; }
5294
- });
5295
- Object.defineProperty(exports, "frameData", {
5296
- enumerable: true,
5297
- get: function () { return motionDom.frameData; }
5298
- });
5299
- Object.defineProperty(exports, "hover", {
5300
- enumerable: true,
5301
- get: function () { return motionDom.hover; }
5302
- });
5303
- Object.defineProperty(exports, "isDragActive", {
5304
- enumerable: true,
5305
- get: function () { return motionDom.isDragActive; }
5306
- });
5307
- Object.defineProperty(exports, "motionValue", {
5308
- enumerable: true,
5309
- get: function () { return motionDom.motionValue; }
5310
- });
5311
- Object.defineProperty(exports, "press", {
5312
- enumerable: true,
5313
- get: function () { return motionDom.press; }
5314
- });
5315
- Object.defineProperty(exports, "sync", {
5316
- enumerable: true,
5317
- get: function () { return motionDom.sync; }
5318
- });
5319
- Object.defineProperty(exports, "time", {
5320
- enumerable: true,
5321
- get: function () { return motionDom.time; }
5322
- });
5323
- Object.defineProperty(exports, "invariant", {
5324
- enumerable: true,
5325
- get: function () { return motionUtils.invariant; }
5326
- });
5327
- Object.defineProperty(exports, "noop", {
5328
- enumerable: true,
5329
- get: function () { return motionUtils.noop; }
5330
- });
5331
- Object.defineProperty(exports, "progress", {
5332
- enumerable: true,
5333
- get: function () { return motionUtils.progress; }
5334
- });
5335
2609
  exports.animate = animate;
5336
2610
  exports.animateMini = animateMini;
5337
- exports.anticipate = anticipate;
5338
- exports.backIn = backIn;
5339
- exports.backInOut = backInOut;
5340
- exports.backOut = backOut;
5341
- exports.circIn = circIn;
5342
- exports.circInOut = circInOut;
5343
- exports.circOut = circOut;
5344
- exports.clamp = clamp;
5345
2611
  exports.createScopedAnimate = createScopedAnimate;
5346
- exports.cubicBezier = cubicBezier;
5347
2612
  exports.delay = delayInSeconds;
5348
2613
  exports.distance = distance;
5349
2614
  exports.distance2D = distance2D;
5350
- exports.easeIn = easeIn;
5351
- exports.easeInOut = easeInOut;
5352
- exports.easeOut = easeOut;
5353
2615
  exports.inView = inView;
5354
- exports.inertia = inertia;
5355
- exports.interpolate = interpolate;
5356
- exports.keyframes = keyframes;
5357
- exports.mirrorEasing = mirrorEasing;
5358
- exports.mix = mix;
5359
- exports.pipe = pipe;
5360
- exports.reverseEasing = reverseEasing;
5361
2616
  exports.scroll = scroll;
5362
2617
  exports.scrollInfo = scrollInfo;
5363
- exports.spring = spring;
5364
2618
  exports.stagger = stagger;
5365
- exports.steps = steps;
5366
2619
  exports.transform = transform;
5367
- exports.wrap = wrap;
2620
+ Object.keys(motionDom).forEach(function (k) {
2621
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
2622
+ enumerable: true,
2623
+ get: function () { return motionDom[k]; }
2624
+ });
2625
+ });
2626
+ Object.keys(motionUtils).forEach(function (k) {
2627
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
2628
+ enumerable: true,
2629
+ get: function () { return motionUtils[k]; }
2630
+ });
2631
+ });