motion 11.11.14 → 11.11.16

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.
@@ -0,0 +1,596 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var react = require('react');
6
+
7
+ /**
8
+ * Creates a constant value over the lifecycle of a component.
9
+ *
10
+ * Even if `useMemo` is provided an empty array as its final argument, it doesn't offer
11
+ * a guarantee that it won't re-run for performance reasons later on. By using `useConstant`
12
+ * you can ensure that initialisers don't execute twice or more.
13
+ */
14
+ function useConstant(init) {
15
+ const ref = react.useRef(null);
16
+ if (ref.current === null) {
17
+ ref.current = init();
18
+ }
19
+ return ref.current;
20
+ }
21
+
22
+ function useUnmountEffect(callback) {
23
+ return react.useEffect(() => () => callback(), []);
24
+ }
25
+
26
+ function memo(callback) {
27
+ let result;
28
+ return () => {
29
+ if (result === undefined)
30
+ result = callback();
31
+ return result;
32
+ };
33
+ }
34
+
35
+ const supportsScrollTimeline = memo(() => window.ScrollTimeline !== undefined);
36
+
37
+ class GroupPlaybackControls {
38
+ constructor(animations) {
39
+ // Bound to accomodate common `return animation.stop` pattern
40
+ this.stop = () => this.runAll("stop");
41
+ this.animations = animations.filter(Boolean);
42
+ }
43
+ then(onResolve, onReject) {
44
+ return Promise.all(this.animations).then(onResolve).catch(onReject);
45
+ }
46
+ /**
47
+ * TODO: Filter out cancelled or stopped animations before returning
48
+ */
49
+ getAll(propName) {
50
+ return this.animations[0][propName];
51
+ }
52
+ setAll(propName, newValue) {
53
+ for (let i = 0; i < this.animations.length; i++) {
54
+ this.animations[i][propName] = newValue;
55
+ }
56
+ }
57
+ attachTimeline(timeline, fallback) {
58
+ const subscriptions = this.animations.map((animation) => {
59
+ if (supportsScrollTimeline() && animation.attachTimeline) {
60
+ return animation.attachTimeline(timeline);
61
+ }
62
+ else {
63
+ return fallback(animation);
64
+ }
65
+ });
66
+ return () => {
67
+ subscriptions.forEach((cancel, i) => {
68
+ cancel && cancel();
69
+ this.animations[i].stop();
70
+ });
71
+ };
72
+ }
73
+ get time() {
74
+ return this.getAll("time");
75
+ }
76
+ set time(time) {
77
+ this.setAll("time", time);
78
+ }
79
+ get speed() {
80
+ return this.getAll("speed");
81
+ }
82
+ set speed(speed) {
83
+ this.setAll("speed", speed);
84
+ }
85
+ get startTime() {
86
+ return this.getAll("startTime");
87
+ }
88
+ get duration() {
89
+ let max = 0;
90
+ for (let i = 0; i < this.animations.length; i++) {
91
+ max = Math.max(max, this.animations[i].duration);
92
+ }
93
+ return max;
94
+ }
95
+ runAll(methodName) {
96
+ this.animations.forEach((controls) => controls[methodName]());
97
+ }
98
+ flatten() {
99
+ this.runAll("flatten");
100
+ }
101
+ play() {
102
+ this.runAll("play");
103
+ }
104
+ pause() {
105
+ this.runAll("pause");
106
+ }
107
+ cancel() {
108
+ this.runAll("cancel");
109
+ }
110
+ complete() {
111
+ this.runAll("complete");
112
+ }
113
+ }
114
+
115
+ const noop = (any) => any;
116
+
117
+ let invariant = noop;
118
+ if (process.env.NODE_ENV !== "production") {
119
+ invariant = (check, message) => {
120
+ if (!check) {
121
+ throw new Error(message);
122
+ }
123
+ };
124
+ }
125
+
126
+ function resolveElements(elements, scope, selectorCache) {
127
+ var _a;
128
+ if (typeof elements === "string") {
129
+ let root = document;
130
+ if (scope) {
131
+ invariant(Boolean(scope.current), "Scope provided, but no element detected.");
132
+ root = scope.current;
133
+ }
134
+ if (selectorCache) {
135
+ (_a = selectorCache[elements]) !== null && _a !== void 0 ? _a : (selectorCache[elements] = root.querySelectorAll(elements));
136
+ elements = selectorCache[elements];
137
+ }
138
+ else {
139
+ elements = root.querySelectorAll(elements);
140
+ }
141
+ }
142
+ else if (elements instanceof Element) {
143
+ elements = [elements];
144
+ }
145
+ /**
146
+ * Return an empty array
147
+ */
148
+ return Array.from(elements || []);
149
+ }
150
+
151
+ /**
152
+ * Converts seconds to milliseconds
153
+ *
154
+ * @param seconds - Time in seconds.
155
+ * @return milliseconds - Converted time in milliseconds.
156
+ */
157
+ const secondsToMilliseconds = (seconds) => seconds * 1000;
158
+ const millisecondsToSeconds = (milliseconds) => milliseconds / 1000;
159
+
160
+ function getValueTransition(transition, key) {
161
+ return transition
162
+ ? transition[key] ||
163
+ transition["default"] ||
164
+ transition
165
+ : undefined;
166
+ }
167
+
168
+ const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
169
+
170
+ /*
171
+ Progress within given range
172
+
173
+ Given a lower limit and an upper limit, we return the progress
174
+ (expressed as a number 0-1) represented by the given value, and
175
+ limit that progress to within 0-1.
176
+
177
+ @param [number]: Lower limit
178
+ @param [number]: Upper limit
179
+ @param [number]: Value to find progress within given range
180
+ @return [number]: Progress of value within range as expressed 0-1
181
+ */
182
+ const progress = (from, to, value) => {
183
+ const toFromDifference = to - from;
184
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
185
+ };
186
+
187
+ // Create a linear easing point for every 10 ms
188
+ const resolution = 10;
189
+ const generateLinearEasing = (easing, duration // as milliseconds
190
+ ) => {
191
+ let points = "";
192
+ const numPoints = Math.max(Math.round(duration / resolution), 2);
193
+ for (let i = 0; i < numPoints; i++) {
194
+ points += easing(progress(0, numPoints - 1, i)) + ", ";
195
+ }
196
+ return `linear(${points.substring(0, points.length - 2)})`;
197
+ };
198
+
199
+ /**
200
+ * Add the ability for test suites to manually set support flags
201
+ * to better test more environments.
202
+ */
203
+ const supportsFlags = {
204
+ linearEasing: undefined,
205
+ };
206
+
207
+ function memoSupports(callback, supportsFlag) {
208
+ const memoized = memo(callback);
209
+ return () => { var _a; return (_a = supportsFlags[supportsFlag]) !== null && _a !== void 0 ? _a : memoized(); };
210
+ }
211
+
212
+ const supportsLinearEasing = /*@__PURE__*/ memoSupports(() => {
213
+ try {
214
+ document
215
+ .createElement("div")
216
+ .animate({ opacity: 0 }, { easing: "linear(0, 1)" });
217
+ }
218
+ catch (e) {
219
+ return false;
220
+ }
221
+ return true;
222
+ }, "linearEasing");
223
+
224
+ const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
225
+ const supportedWaapiEasing = {
226
+ linear: "linear",
227
+ ease: "ease",
228
+ easeIn: "ease-in",
229
+ easeOut: "ease-out",
230
+ easeInOut: "ease-in-out",
231
+ circIn: /*@__PURE__*/ cubicBezierAsString([0, 0.65, 0.55, 1]),
232
+ circOut: /*@__PURE__*/ cubicBezierAsString([0.55, 0, 1, 0.45]),
233
+ backIn: /*@__PURE__*/ cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
234
+ backOut: /*@__PURE__*/ cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
235
+ };
236
+ function mapEasingToNativeEasing(easing, duration) {
237
+ if (!easing) {
238
+ return undefined;
239
+ }
240
+ else if (typeof easing === "function" && supportsLinearEasing()) {
241
+ return generateLinearEasing(easing, duration);
242
+ }
243
+ else if (isBezierDefinition(easing)) {
244
+ return cubicBezierAsString(easing);
245
+ }
246
+ else if (Array.isArray(easing)) {
247
+ return easing.map((segmentEasing) => mapEasingToNativeEasing(segmentEasing, duration) ||
248
+ supportedWaapiEasing.easeOut);
249
+ }
250
+ else {
251
+ return supportedWaapiEasing[easing];
252
+ }
253
+ }
254
+
255
+ function startWaapiAnimation(element, valueName, keyframes, { delay = 0, duration = 300, repeat = 0, repeatType = "loop", ease = "easeInOut", times, } = {}) {
256
+ const keyframeOptions = { [valueName]: keyframes };
257
+ if (times)
258
+ keyframeOptions.offset = times;
259
+ const easing = mapEasingToNativeEasing(ease, duration);
260
+ /**
261
+ * If this is an easing array, apply to keyframes, not animation as a whole
262
+ */
263
+ if (Array.isArray(easing))
264
+ keyframeOptions.easing = easing;
265
+ return element.animate(keyframeOptions, {
266
+ delay,
267
+ duration,
268
+ easing: !Array.isArray(easing) ? easing : "linear",
269
+ fill: "both",
270
+ iterations: repeat + 1,
271
+ direction: repeatType === "reverse" ? "alternate" : "normal",
272
+ });
273
+ }
274
+
275
+ /**
276
+ * Implement a practical max duration for keyframe generation
277
+ * to prevent infinite loops
278
+ */
279
+ const maxGeneratorDuration = 20000;
280
+ function calcGeneratorDuration(generator) {
281
+ let duration = 0;
282
+ const timeStep = 50;
283
+ let state = generator.next(duration);
284
+ while (!state.done && duration < maxGeneratorDuration) {
285
+ duration += timeStep;
286
+ state = generator.next(duration);
287
+ }
288
+ return duration >= maxGeneratorDuration ? Infinity : duration;
289
+ }
290
+
291
+ /**
292
+ * Create a progress => progress easing function from a generator.
293
+ */
294
+ function createGeneratorEasing(options, scale = 100, createGenerator) {
295
+ const generator = createGenerator({ ...options, keyframes: [0, scale] });
296
+ const duration = Math.min(calcGeneratorDuration(generator), maxGeneratorDuration);
297
+ return {
298
+ type: "keyframes",
299
+ ease: (progress) => generator.next(duration * progress).value / scale,
300
+ duration: millisecondsToSeconds(duration),
301
+ };
302
+ }
303
+
304
+ const createUnitType = (unit) => ({
305
+ test: (v) => typeof v === "string" && v.endsWith(unit) && v.split(" ").length === 1,
306
+ parse: parseFloat,
307
+ transform: (v) => `${v}${unit}`,
308
+ });
309
+ const px = /*@__PURE__*/ createUnitType("px");
310
+
311
+ const browserNumberValueTypes = {
312
+ // Border props
313
+ borderWidth: px,
314
+ borderTopWidth: px,
315
+ borderRightWidth: px,
316
+ borderBottomWidth: px,
317
+ borderLeftWidth: px,
318
+ borderRadius: px,
319
+ radius: px,
320
+ borderTopLeftRadius: px,
321
+ borderTopRightRadius: px,
322
+ borderBottomRightRadius: px,
323
+ borderBottomLeftRadius: px,
324
+ // Positioning props
325
+ width: px,
326
+ maxWidth: px,
327
+ height: px,
328
+ maxHeight: px,
329
+ top: px,
330
+ right: px,
331
+ bottom: px,
332
+ left: px,
333
+ // Spacing props
334
+ padding: px,
335
+ paddingTop: px,
336
+ paddingRight: px,
337
+ paddingBottom: px,
338
+ paddingLeft: px,
339
+ margin: px,
340
+ marginTop: px,
341
+ marginRight: px,
342
+ marginBottom: px,
343
+ marginLeft: px,
344
+ // Misc
345
+ backgroundPositionX: px,
346
+ backgroundPositionY: px,
347
+ };
348
+
349
+ function isGenerator(type) {
350
+ return typeof type === "function";
351
+ }
352
+
353
+ function attachTimeline(animation, timeline) {
354
+ animation.timeline = timeline;
355
+ animation.onfinish = null;
356
+ }
357
+
358
+ const isNotNull = (value) => value !== null;
359
+ function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
360
+ const resolvedKeyframes = keyframes.filter(isNotNull);
361
+ const index = repeat && repeatType !== "loop" && repeat % 2 === 1
362
+ ? 0
363
+ : resolvedKeyframes.length - 1;
364
+ return !index || finalKeyframe === undefined
365
+ ? resolvedKeyframes[index]
366
+ : finalKeyframe;
367
+ }
368
+
369
+ function setCSSVar(element, name, value) {
370
+ element.style.setProperty(`--${name}`, value);
371
+ }
372
+ function setStyle(element, name, value) {
373
+ element.style[name] = value;
374
+ }
375
+
376
+ const supportsPartialKeyframes = /*@__PURE__*/ memo(() => {
377
+ try {
378
+ document.createElement("div").animate({ opacity: [1] });
379
+ }
380
+ catch (e) {
381
+ return false;
382
+ }
383
+ return true;
384
+ });
385
+
386
+ const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
387
+
388
+ const state = new WeakMap();
389
+ function hydrateKeyframes(valueName, keyframes, read) {
390
+ for (let i = 0; i < keyframes.length; i++) {
391
+ if (keyframes[i] === null) {
392
+ keyframes[i] = i === 0 ? read() : keyframes[i - 1];
393
+ }
394
+ if (typeof keyframes[i] === "number" &&
395
+ browserNumberValueTypes[valueName]) {
396
+ keyframes[i] = browserNumberValueTypes[valueName].transform(keyframes[i]);
397
+ }
398
+ }
399
+ if (!supportsPartialKeyframes() && keyframes.length < 2) {
400
+ keyframes.unshift(read());
401
+ }
402
+ }
403
+ const defaultEasing = "easeOut";
404
+ function getElementAnimationState(element) {
405
+ const animationState = state.get(element) || new Map();
406
+ state.set(element, animationState);
407
+ return state.get(element);
408
+ }
409
+ class NativeAnimation {
410
+ constructor(element, valueName, valueKeyframes, options) {
411
+ const isCSSVar = valueName.startsWith("--");
412
+ this.setValue = isCSSVar ? setCSSVar : setStyle;
413
+ this.options = options;
414
+ this.updateFinishedPromise();
415
+ invariant(typeof options.type !== "string", `animateMini doesn't support "type" as a string. Did you mean to import { spring } from "framer-motion"?`);
416
+ const existingAnimation = getElementAnimationState(element).get(valueName);
417
+ existingAnimation && existingAnimation.stop();
418
+ const readInitialKeyframe = () => {
419
+ return valueName.startsWith("--")
420
+ ? element.style.getPropertyValue(valueName)
421
+ : window.getComputedStyle(element)[valueName];
422
+ };
423
+ if (!Array.isArray(valueKeyframes)) {
424
+ valueKeyframes = [valueKeyframes];
425
+ }
426
+ hydrateKeyframes(valueName, valueKeyframes, readInitialKeyframe);
427
+ if (isGenerator(options.type)) {
428
+ const generatorOptions = createGeneratorEasing(options, 100, options.type);
429
+ options.ease = supportsLinearEasing()
430
+ ? generatorOptions.ease
431
+ : defaultEasing;
432
+ options.duration = secondsToMilliseconds(generatorOptions.duration);
433
+ options.type = "keyframes";
434
+ }
435
+ else {
436
+ options.ease = options.ease || defaultEasing;
437
+ }
438
+ this.removeAnimation = () => { var _a; return (_a = state.get(element)) === null || _a === void 0 ? void 0 : _a.delete(valueName); };
439
+ const onFinish = () => {
440
+ this.setValue(element, valueName, getFinalKeyframe(valueKeyframes, this.options));
441
+ this.cancel();
442
+ this.resolveFinishedPromise();
443
+ };
444
+ if (!supportsWaapi()) {
445
+ onFinish();
446
+ }
447
+ else {
448
+ this.animation = startWaapiAnimation(element, valueName, valueKeyframes, options);
449
+ if (options.autoplay === false) {
450
+ this.animation.pause();
451
+ }
452
+ this.animation.onfinish = onFinish;
453
+ if (this.pendingTimeline) {
454
+ attachTimeline(this.animation, this.pendingTimeline);
455
+ }
456
+ getElementAnimationState(element).set(valueName, this);
457
+ }
458
+ }
459
+ get duration() {
460
+ return millisecondsToSeconds(this.options.duration || 300);
461
+ }
462
+ get time() {
463
+ var _a;
464
+ if (this.animation) {
465
+ return millisecondsToSeconds(((_a = this.animation) === null || _a === void 0 ? void 0 : _a.currentTime) || 0);
466
+ }
467
+ return 0;
468
+ }
469
+ set time(newTime) {
470
+ if (this.animation) {
471
+ this.animation.currentTime = secondsToMilliseconds(newTime);
472
+ }
473
+ }
474
+ get speed() {
475
+ return this.animation ? this.animation.playbackRate : 1;
476
+ }
477
+ set speed(newSpeed) {
478
+ if (this.animation) {
479
+ this.animation.playbackRate = newSpeed;
480
+ }
481
+ }
482
+ get state() {
483
+ return this.animation ? this.animation.playState : "finished";
484
+ }
485
+ get startTime() {
486
+ return this.animation ? this.animation.startTime : null;
487
+ }
488
+ flatten() {
489
+ var _a;
490
+ if (!this.animation)
491
+ return;
492
+ (_a = this.animation.effect) === null || _a === void 0 ? void 0 : _a.updateTiming({ easing: "linear" });
493
+ }
494
+ play() {
495
+ if (this.state === "finished") {
496
+ this.updateFinishedPromise();
497
+ }
498
+ this.animation && this.animation.play();
499
+ }
500
+ pause() {
501
+ this.animation && this.animation.pause();
502
+ }
503
+ stop() {
504
+ if (!this.animation ||
505
+ this.state === "idle" ||
506
+ this.state === "finished") {
507
+ return;
508
+ }
509
+ if (this.animation.commitStyles) {
510
+ this.animation.commitStyles();
511
+ }
512
+ this.cancel();
513
+ }
514
+ complete() {
515
+ this.animation && this.animation.finish();
516
+ }
517
+ cancel() {
518
+ this.removeAnimation();
519
+ try {
520
+ this.animation && this.animation.cancel();
521
+ }
522
+ catch (e) { }
523
+ }
524
+ /**
525
+ * Allows the returned animation to be awaited or promise-chained. Currently
526
+ * resolves when the animation finishes at all but in a future update could/should
527
+ * reject if its cancels.
528
+ */
529
+ then(resolve, reject) {
530
+ return this.currentFinishedPromise.then(resolve, reject);
531
+ }
532
+ updateFinishedPromise() {
533
+ this.currentFinishedPromise = new Promise((resolve) => {
534
+ this.resolveFinishedPromise = resolve;
535
+ });
536
+ }
537
+ attachTimeline(timeline) {
538
+ if (!this.animation) {
539
+ this.pendingTimeline = timeline;
540
+ }
541
+ else {
542
+ attachTimeline(this.animation, timeline);
543
+ }
544
+ return noop;
545
+ }
546
+ }
547
+
548
+ function animateElements(elementOrSelector, keyframes, options, scope) {
549
+ const elements = resolveElements(elementOrSelector, scope);
550
+ const numElements = elements.length;
551
+ invariant(Boolean(numElements), "No valid element provided.");
552
+ const animations = [];
553
+ for (let i = 0; i < numElements; i++) {
554
+ const element = elements[i];
555
+ const elementTransition = { ...options };
556
+ /**
557
+ * Resolve stagger function if provided.
558
+ */
559
+ if (typeof elementTransition.delay === "function") {
560
+ elementTransition.delay = elementTransition.delay(i, numElements);
561
+ }
562
+ for (const valueName in keyframes) {
563
+ const valueKeyframes = keyframes[valueName];
564
+ const valueOptions = {
565
+ ...getValueTransition(options, valueName),
566
+ };
567
+ valueOptions.duration = valueOptions.duration
568
+ ? secondsToMilliseconds(valueOptions.duration)
569
+ : valueOptions.duration;
570
+ valueOptions.delay = secondsToMilliseconds(valueOptions.delay || 0);
571
+ animations.push(new NativeAnimation(element, valueName, valueKeyframes, valueOptions));
572
+ }
573
+ }
574
+ return animations;
575
+ }
576
+
577
+ const createScopedWaapiAnimate = (scope) => {
578
+ function scopedAnimate(elementOrSelector, keyframes, options) {
579
+ return new GroupPlaybackControls(animateElements(elementOrSelector, keyframes, options, scope));
580
+ }
581
+ return scopedAnimate;
582
+ };
583
+
584
+ function useAnimateMini() {
585
+ const scope = useConstant(() => ({
586
+ current: null, // Will be hydrated by React
587
+ animations: [],
588
+ }));
589
+ const animate = useConstant(() => createScopedWaapiAnimate(scope));
590
+ useUnmountEffect(() => {
591
+ scope.animations.forEach((animation) => animation.stop());
592
+ });
593
+ return [scope, animate];
594
+ }
595
+
596
+ exports.useAnimate = useAnimateMini;
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var framerMotion = require('framer-motion');
6
+
7
+
8
+
9
+ Object.keys(framerMotion).forEach(function (k) {
10
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
11
+ enumerable: true,
12
+ get: function () { return framerMotion[k]; }
13
+ });
14
+ });
@@ -61,6 +61,9 @@ class GroupPlaybackControls {
61
61
  runAll(methodName) {
62
62
  this.animations.forEach((controls) => controls[methodName]());
63
63
  }
64
+ flatten() {
65
+ this.runAll("flatten");
66
+ }
64
67
  play() {
65
68
  this.runAll("play");
66
69
  }
@@ -103,6 +103,10 @@ class BaseAnimation {
103
103
  then(resolve, reject) {
104
104
  return this.currentFinishedPromise.then(resolve, reject);
105
105
  }
106
+ flatten() {
107
+ this.options.type = "keyframes";
108
+ this.options.ease = "linear";
109
+ }
106
110
  updateFinishedPromise() {
107
111
  this.currentFinishedPromise = new Promise((resolve) => {
108
112
  this.resolveFinishedPromise = resolve;
@@ -75,6 +75,13 @@ class MainThreadAnimation extends BaseAnimation {
75
75
  this.resolver = new KeyframeResolver$1(keyframes, onResolved, name, motionValue, element);
76
76
  this.resolver.scheduleResolve();
77
77
  }
78
+ flatten() {
79
+ super.flatten();
80
+ // If we've already resolved the animation, re-initialise it
81
+ if (this._resolved) {
82
+ Object.assign(this._resolved, this.initPlayback(this._resolved.keyframes));
83
+ }
84
+ }
78
85
  initPlayback(keyframes$1) {
79
86
  const { type = "keyframes", repeat = 0, repeatDelay = 0, repeatType, velocity = 0, } = this.options;
80
87
  const generatorFactory = isGenerator(type)
@@ -112,6 +112,12 @@ class NativeAnimation {
112
112
  get startTime() {
113
113
  return this.animation ? this.animation.startTime : null;
114
114
  }
115
+ flatten() {
116
+ var _a;
117
+ if (!this.animation)
118
+ return;
119
+ (_a = this.animation.effect) === null || _a === void 0 ? void 0 : _a.updateTiming({ easing: "linear" });
120
+ }
115
121
  play() {
116
122
  if (this.state === "finished") {
117
123
  this.updateFinishedPromise();
@@ -1,6 +1,7 @@
1
1
  import { scrollInfo } from './track.mjs';
2
2
  import { observeTimeline } from './observe.mjs';
3
3
  import { supportsScrollTimeline } from './supports.mjs';
4
+ import { noop } from '../../../utils/noop.mjs';
4
5
 
5
6
  function scrollTimelineFallback({ source, container, axis = "y", }) {
6
7
  // Support legacy source argument. Deprecate later.
@@ -54,6 +55,7 @@ function scrollFunction(onScroll, options) {
54
55
  }
55
56
  }
56
57
  function scrollAnimation(animation, options) {
58
+ animation.flatten();
57
59
  if (needsElementTracking(options)) {
58
60
  animation.pause();
59
61
  return scrollInfo((info) => {
@@ -62,12 +64,17 @@ function scrollAnimation(animation, options) {
62
64
  }
63
65
  else {
64
66
  const timeline = getTimeline(options);
65
- return animation.attachTimeline(timeline, (valueAnimation) => {
66
- valueAnimation.pause();
67
- return observeTimeline((progress) => {
68
- valueAnimation.time = valueAnimation.duration * progress;
69
- }, timeline);
70
- });
67
+ if (animation.attachTimeline) {
68
+ return animation.attachTimeline(timeline, (valueAnimation) => {
69
+ valueAnimation.pause();
70
+ return observeTimeline((progress) => {
71
+ valueAnimation.time = valueAnimation.duration * progress;
72
+ }, timeline);
73
+ });
74
+ }
75
+ else {
76
+ return noop;
77
+ }
71
78
  }
72
79
  }
73
80
  function scroll(onScroll, { axis = "y", ...options } = {}) {