animejs 4.0.1 → 4.1.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.
- package/README.md +30 -215
- package/lib/anime.cjs +2 -2
- package/lib/anime.esm.js +951 -287
- package/lib/anime.esm.min.js +2 -2
- package/lib/anime.iife.js +2 -2
- package/lib/anime.iife.min.js +2 -2
- package/lib/anime.min.cjs +2 -2
- package/lib/anime.umd.js +2 -2
- package/lib/anime.umd.min.js +2 -2
- package/lib/gui/index.js +6341 -0
- package/package.json +1 -1
- package/types/index.d.ts +179 -116
- package/types/index.js +756 -237
package/lib/anime.esm.js
CHANGED
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* anime.js - ESM
|
|
3
|
-
* @version v4.0
|
|
3
|
+
* @version v4.1.0
|
|
4
4
|
* @author Julian Garnier
|
|
5
5
|
* @license MIT
|
|
6
6
|
* @copyright (c) 2025 Julian Garnier
|
|
7
7
|
* @see https://animejs.com
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
// Global types ///////////////////////////////////////////////////////////////
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* @typedef {Object} DefaultsParams
|
|
12
|
-
* @property {
|
|
14
|
+
* @property {Number|String} [id]
|
|
13
15
|
* @property {PercentageKeyframes|DurationKeyframes} [keyframes]
|
|
14
16
|
* @property {EasingParam} [playbackEase]
|
|
15
|
-
* @property {
|
|
16
|
-
* @property {
|
|
17
|
-
* @property {
|
|
18
|
-
* @property {
|
|
19
|
-
* @property {
|
|
20
|
-
* @property {
|
|
21
|
-
* @property {
|
|
22
|
-
* @property {
|
|
23
|
-
* @property {
|
|
17
|
+
* @property {Number} [playbackRate]
|
|
18
|
+
* @property {Number} [frameRate]
|
|
19
|
+
* @property {Number|Boolean} [loop]
|
|
20
|
+
* @property {Boolean} [reversed]
|
|
21
|
+
* @property {Boolean} [alternate]
|
|
22
|
+
* @property {Boolean|ScrollObserver} [autoplay]
|
|
23
|
+
* @property {Number|FunctionValue} [duration]
|
|
24
|
+
* @property {Number|FunctionValue} [delay]
|
|
25
|
+
* @property {Number} [loopDelay]
|
|
24
26
|
* @property {EasingParam} [ease]
|
|
25
27
|
* @property {'none'|'replace'|'blend'|compositionTypes} [composition]
|
|
26
28
|
* @property {(v: any) => any} [modifier]
|
|
@@ -36,7 +38,33 @@
|
|
|
36
38
|
/** @typedef {JSAnimation|Timeline} Renderable */
|
|
37
39
|
/** @typedef {Timer|Renderable} Tickable */
|
|
38
40
|
/** @typedef {Timer&JSAnimation&Timeline} CallbackArgument */
|
|
39
|
-
/** @typedef {Animatable|Tickable|Draggable|ScrollObserver|Scope} Revertible */
|
|
41
|
+
/** @typedef {Animatable|Tickable|Draggable|ScrollObserver|TextSplitter|Scope} Revertible */
|
|
42
|
+
|
|
43
|
+
// Stagger types //////////////////////////////////////////////////////////////
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @callback StaggerFunction
|
|
47
|
+
* @param {Target} [target]
|
|
48
|
+
* @param {Number} [index]
|
|
49
|
+
* @param {Number} [length]
|
|
50
|
+
* @param {Timeline} [tl]
|
|
51
|
+
* @return {Number|String}
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @typedef {Object} StaggerParams
|
|
56
|
+
* @property {Number|String} [start]
|
|
57
|
+
* @property {Number|'first'|'center'|'last'|'random'} [from]
|
|
58
|
+
* @property {Boolean} [reversed]
|
|
59
|
+
* @property {Array.<Number>} [grid]
|
|
60
|
+
* @property {('x'|'y')} [axis]
|
|
61
|
+
* @property {String|StaggerFunction} [use]
|
|
62
|
+
* @property {Number} [total]
|
|
63
|
+
* @property {EasingParam} [ease]
|
|
64
|
+
* @property {TweenModifier} [modifier]
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
// Eases types ////////////////////////////////////////////////////////////////
|
|
40
68
|
|
|
41
69
|
/**
|
|
42
70
|
* @callback EasingFunction
|
|
@@ -64,21 +92,7 @@
|
|
|
64
92
|
/** @typedef {Array.<TargetSelector>|TargetSelector} TargetsParam */
|
|
65
93
|
/** @typedef {Array.<Target>} TargetsArray */
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
* @callback FunctionValue
|
|
69
|
-
* @param {Target} target - The animated target
|
|
70
|
-
* @param {Number} index - The target index
|
|
71
|
-
* @param {Number} length - The total number of animated targets
|
|
72
|
-
* @return {Number|String|TweenObjectValue|Array.<Number|String|TweenObjectValue>}
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @callback TweenModifier
|
|
77
|
-
* @param {Number} value - The animated value
|
|
78
|
-
* @return {Number|String}
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
/** @typedef {[Number, Number, Number, Number]} ColorArray */
|
|
95
|
+
// Callback types ////////////////////////////////////////////////////////////
|
|
82
96
|
|
|
83
97
|
/**
|
|
84
98
|
* @template T
|
|
@@ -105,6 +119,46 @@
|
|
|
105
119
|
* @property {Callback<T>} [onRender]
|
|
106
120
|
*/
|
|
107
121
|
|
|
122
|
+
// Timer types ////////////////////////////////////////////////////////////////
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @typedef {Object} TimerOptions
|
|
126
|
+
* @property {Number|String} [id]
|
|
127
|
+
* @property {TweenParamValue} [duration]
|
|
128
|
+
* @property {TweenParamValue} [delay]
|
|
129
|
+
* @property {Number} [loopDelay]
|
|
130
|
+
* @property {Boolean} [reversed]
|
|
131
|
+
* @property {Boolean} [alternate]
|
|
132
|
+
* @property {Boolean|Number} [loop]
|
|
133
|
+
* @property {Boolean|ScrollObserver} [autoplay]
|
|
134
|
+
* @property {Number} [frameRate]
|
|
135
|
+
* @property {Number} [playbackRate]
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @typedef {TimerOptions & TickableCallbacks<Timer>} TimerParams
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
// Tween types ////////////////////////////////////////////////////////////////
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @callback FunctionValue
|
|
148
|
+
* @param {Target} target - The animated target
|
|
149
|
+
* @param {Number} index - The target index
|
|
150
|
+
* @param {Number} length - The total number of animated targets
|
|
151
|
+
* @return {Number|String|TweenObjectValue|Array.<Number|String|TweenObjectValue>}
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @callback TweenModifier
|
|
156
|
+
* @param {Number} value - The animated value
|
|
157
|
+
* @return {Number|String}
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/** @typedef {[Number, Number, Number, Number]} ColorArray */
|
|
161
|
+
|
|
108
162
|
/**
|
|
109
163
|
* @typedef {Object} Tween
|
|
110
164
|
* @property {Number} id
|
|
@@ -158,25 +212,7 @@
|
|
|
158
212
|
/** @typedef {WeakMap.<Target, TweenLookups>} TweenReplaceLookups */
|
|
159
213
|
/** @typedef {Map.<Target, TweenLookups>} TweenAdditiveLookups */
|
|
160
214
|
|
|
161
|
-
|
|
162
|
-
* @typedef {Object} TimerOptions
|
|
163
|
-
* @property {Number|String} [id]
|
|
164
|
-
* @property {TweenParamValue} [duration]
|
|
165
|
-
* @property {TweenParamValue} [delay]
|
|
166
|
-
* @property {Number} [loopDelay]
|
|
167
|
-
* @property {Boolean} [reversed]
|
|
168
|
-
* @property {Boolean} [alternate]
|
|
169
|
-
* @property {Boolean|Number} [loop]
|
|
170
|
-
* @property {Boolean|ScrollObserver} [autoplay]
|
|
171
|
-
* @property {Number} [frameRate]
|
|
172
|
-
* @property {Number} [playbackRate]
|
|
173
|
-
*/
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* @typedef {TimerOptions & TickableCallbacks<Timer>} TimerParams
|
|
179
|
-
*/
|
|
215
|
+
// Animation types ////////////////////////////////////////////////////////////
|
|
180
216
|
|
|
181
217
|
/**
|
|
182
218
|
* @typedef {Number|String|FunctionValue} TweenParamValue
|
|
@@ -250,6 +286,8 @@
|
|
|
250
286
|
* @typedef {Record<String, TweenOptions | Callback<JSAnimation> | TweenModifier | boolean | PercentageKeyframes | DurationKeyframes | ScrollObserver> & TimerOptions & AnimationOptions & TweenParamsOptions & TickableCallbacks<JSAnimation> & RenderableCallbacks<JSAnimation>} AnimationParams
|
|
251
287
|
*/
|
|
252
288
|
|
|
289
|
+
// Timeline types /////////////////////////////////////////////////////////////
|
|
290
|
+
|
|
253
291
|
/**
|
|
254
292
|
* @typedef {Object} TimelineOptions
|
|
255
293
|
* @property {DefaultsParams} [defaults]
|
|
@@ -260,6 +298,8 @@
|
|
|
260
298
|
* @typedef {TimerOptions & TimelineOptions & TickableCallbacks<Timeline> & RenderableCallbacks<Timeline>} TimelineParams
|
|
261
299
|
*/
|
|
262
300
|
|
|
301
|
+
// Animatable types ///////////////////////////////////////////////////////////
|
|
302
|
+
|
|
263
303
|
/**
|
|
264
304
|
* @callback AnimatablePropertySetter
|
|
265
305
|
* @param {Number|Array.<Number>} to
|
|
@@ -294,6 +334,136 @@
|
|
|
294
334
|
* @typedef {Record<String, TweenParamValue | EasingParam | TweenModifier | TweenComposition | AnimatablePropertyParamsOptions> & AnimatablePropertyParamsOptions} AnimatableParams
|
|
295
335
|
*/
|
|
296
336
|
|
|
337
|
+
// Scope types ////////////////////////////////////////////////////////////////
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* @typedef {Object} ReactRef
|
|
341
|
+
* @property {HTMLElement|SVGElement|null} [current]
|
|
342
|
+
*/
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @typedef {Object} AngularRef
|
|
346
|
+
* @property {HTMLElement|SVGElement} [nativeElement]
|
|
347
|
+
*/
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* @typedef {Object} ScopeParams
|
|
351
|
+
* @property {DOMTargetSelector|ReactRef|AngularRef} [root]
|
|
352
|
+
* @property {DefaultsParams} [defaults]
|
|
353
|
+
* @property {Record<String, String>} [mediaQueries]
|
|
354
|
+
*/
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @template T
|
|
358
|
+
* @callback ScopedCallback
|
|
359
|
+
* @param {Scope} scope
|
|
360
|
+
* @return {T}
|
|
361
|
+
*/
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* @callback ScopeCleanupCallback
|
|
365
|
+
* @param {Scope} [scope]
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* @callback ScopeConstructorCallback
|
|
370
|
+
* @param {Scope} [scope]
|
|
371
|
+
* @return {ScopeCleanupCallback|void}
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* @callback ScopeMethod
|
|
376
|
+
* @param {...*} args
|
|
377
|
+
* @return {ScopeCleanupCallback|void}
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
// Draggable types ////////////////////////////////////////////////////////////
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* @typedef {Object} DraggableAxisParam
|
|
384
|
+
* @property {String} [mapTo]
|
|
385
|
+
* @property {TweenModifier} [modifier]
|
|
386
|
+
* @property {TweenComposition} [composition]
|
|
387
|
+
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
388
|
+
*/
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @typedef {Object} DraggableCursorParams
|
|
392
|
+
* @property {String} [onHover]
|
|
393
|
+
* @property {String} [onGrab]
|
|
394
|
+
*/
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @typedef {Object} DraggableParams
|
|
398
|
+
* @property {DOMTargetSelector} [trigger]
|
|
399
|
+
* @property {DOMTargetSelector|Array<Number>|((draggable: Draggable) => DOMTargetSelector|Array<Number>)} [container]
|
|
400
|
+
* @property {Boolean|DraggableAxisParam} [x]
|
|
401
|
+
* @property {Boolean|DraggableAxisParam} [y]
|
|
402
|
+
* @property {TweenModifier} [modifier]
|
|
403
|
+
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
404
|
+
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [containerPadding]
|
|
405
|
+
* @property {Number|((draggable: Draggable) => Number)} [containerFriction]
|
|
406
|
+
* @property {Number|((draggable: Draggable) => Number)} [releaseContainerFriction]
|
|
407
|
+
* @property {Number|((draggable: Draggable) => Number)} [dragSpeed]
|
|
408
|
+
* @property {Number|((draggable: Draggable) => Number)} [scrollSpeed]
|
|
409
|
+
* @property {Number|((draggable: Draggable) => Number)} [scrollThreshold]
|
|
410
|
+
* @property {Number|((draggable: Draggable) => Number)} [minVelocity]
|
|
411
|
+
* @property {Number|((draggable: Draggable) => Number)} [maxVelocity]
|
|
412
|
+
* @property {Number|((draggable: Draggable) => Number)} [velocityMultiplier]
|
|
413
|
+
* @property {Number} [releaseMass]
|
|
414
|
+
* @property {Number} [releaseStiffness]
|
|
415
|
+
* @property {Number} [releaseDamping]
|
|
416
|
+
* @property {Boolean} [releaseDamping]
|
|
417
|
+
* @property {EasingParam} [releaseEase]
|
|
418
|
+
* @property {Boolean|DraggableCursorParams|((draggable: Draggable) => Boolean|DraggableCursorParams)} [cursor]
|
|
419
|
+
* @property {Callback<Draggable>} [onGrab]
|
|
420
|
+
* @property {Callback<Draggable>} [onDrag]
|
|
421
|
+
* @property {Callback<Draggable>} [onRelease]
|
|
422
|
+
* @property {Callback<Draggable>} [onUpdate]
|
|
423
|
+
* @property {Callback<Draggable>} [onSettle]
|
|
424
|
+
* @property {Callback<Draggable>} [onSnap]
|
|
425
|
+
* @property {Callback<Draggable>} [onResize]
|
|
426
|
+
* @property {Callback<Draggable>} [onAfterResize]
|
|
427
|
+
*/
|
|
428
|
+
|
|
429
|
+
// Text types /////////////////////////////////////////////////////////////////
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* @typedef {Object} splitTemplateParams
|
|
433
|
+
* @property {false|String} [class]
|
|
434
|
+
* @property {Boolean|'hidden'|'clip'|'visible'|'scroll'|'auto'} [wrap]
|
|
435
|
+
* @property {Boolean|'top'|'right'|'bottom'|'left'|'center'} [clone]
|
|
436
|
+
*/
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* @typedef {Boolean|String} SplitValue
|
|
440
|
+
*/
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* @callback SplitFunctionValue
|
|
444
|
+
* @param {Node|HTMLElement} [value]
|
|
445
|
+
* @return String
|
|
446
|
+
*/
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @typedef {Object} TextSplitterParams
|
|
450
|
+
* @property {SplitValue|splitTemplateParams|SplitFunctionValue} [lines]
|
|
451
|
+
* @property {SplitValue|splitTemplateParams|SplitFunctionValue} [words]
|
|
452
|
+
* @property {SplitValue|splitTemplateParams|SplitFunctionValue} [chars]
|
|
453
|
+
* @property {Boolean} [accessible]
|
|
454
|
+
* @property {Boolean} [includeSpaces]
|
|
455
|
+
* @property {Boolean} [debug]
|
|
456
|
+
*/
|
|
457
|
+
|
|
458
|
+
// SVG types //////////////////////////////////////////////////////////////////
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* @typedef {SVGGeometryElement & {
|
|
462
|
+
* setAttribute(name: 'draw', value: `${number} ${number}`): void;
|
|
463
|
+
* draw: `${number} ${number}`;
|
|
464
|
+
* }} DrawableSVGGeometry
|
|
465
|
+
*/
|
|
466
|
+
|
|
297
467
|
|
|
298
468
|
// Environments
|
|
299
469
|
|
|
@@ -358,11 +528,13 @@ const maxFps = 120;
|
|
|
358
528
|
// Strings
|
|
359
529
|
|
|
360
530
|
const emptyString = '';
|
|
361
|
-
const shortTransforms =
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
531
|
+
const shortTransforms = /*#__PURE__*/ (() => {
|
|
532
|
+
const map = new Map();
|
|
533
|
+
map.set('x', 'translateX');
|
|
534
|
+
map.set('y', 'translateY');
|
|
535
|
+
map.set('z', 'translateZ');
|
|
536
|
+
return map;
|
|
537
|
+
})();
|
|
366
538
|
|
|
367
539
|
const validTransforms = [
|
|
368
540
|
'translateX',
|
|
@@ -384,7 +556,7 @@ const validTransforms = [
|
|
|
384
556
|
'matrix3d',
|
|
385
557
|
];
|
|
386
558
|
|
|
387
|
-
const transformsFragmentStrings = validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
|
|
559
|
+
const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({...a, [v]: v + '('}), {});
|
|
388
560
|
|
|
389
561
|
// Functions
|
|
390
562
|
|
|
@@ -435,13 +607,16 @@ const defaults = {
|
|
|
435
607
|
onRender: noop,
|
|
436
608
|
};
|
|
437
609
|
|
|
610
|
+
const scope = {
|
|
611
|
+
/** @type {Scope} */
|
|
612
|
+
current: null,
|
|
613
|
+
/** @type {Document|DOMTarget} */
|
|
614
|
+
root: doc,
|
|
615
|
+
};
|
|
616
|
+
|
|
438
617
|
const globals = {
|
|
439
618
|
/** @type {DefaultsParams} */
|
|
440
619
|
defaults,
|
|
441
|
-
/** @type {Document|DOMTarget} */
|
|
442
|
-
root: doc,
|
|
443
|
-
/** @type {Scope} */
|
|
444
|
-
scope: null,
|
|
445
620
|
/** @type {Number} */
|
|
446
621
|
precision: 4,
|
|
447
622
|
/** @type {Number} */
|
|
@@ -450,7 +625,7 @@ const globals = {
|
|
|
450
625
|
tickThreshold: 200,
|
|
451
626
|
};
|
|
452
627
|
|
|
453
|
-
const globalVersions = { version: '4.0
|
|
628
|
+
const globalVersions = { version: '4.1.0', engine: null };
|
|
454
629
|
|
|
455
630
|
if (isBrowser) {
|
|
456
631
|
if (!win.AnimeJS) win.AnimeJS = [];
|
|
@@ -531,7 +706,6 @@ const atan2 = Math.atan2;
|
|
|
531
706
|
const PI = Math.PI;
|
|
532
707
|
const _round = Math.round;
|
|
533
708
|
|
|
534
|
-
|
|
535
709
|
/**
|
|
536
710
|
* @param {Number} v
|
|
537
711
|
* @param {Number} min
|
|
@@ -570,6 +744,25 @@ const snap = (v, increment) => isArr(increment) ? increment.reduce((closest, cv)
|
|
|
570
744
|
*/
|
|
571
745
|
const interpolate = (start, end, progress) => start + (end - start) * progress;
|
|
572
746
|
|
|
747
|
+
/**
|
|
748
|
+
* @param {Number} min
|
|
749
|
+
* @param {Number} max
|
|
750
|
+
* @param {Number} [decimalLength]
|
|
751
|
+
* @return {Number}
|
|
752
|
+
*/
|
|
753
|
+
const random = (min, max, decimalLength) => { const m = 10 ** (decimalLength || 0); return floor((Math.random() * (max - min + (1 / m)) + min) * m) / m };
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Adapted from https://bost.ocks.org/mike/shuffle/
|
|
757
|
+
* @param {Array} items
|
|
758
|
+
* @return {Array}
|
|
759
|
+
*/
|
|
760
|
+
const shuffle = items => {
|
|
761
|
+
let m = items.length, t, i;
|
|
762
|
+
while (m) { i = random(0, --m); t = items[m]; items[m] = items[i]; items[i] = t; }
|
|
763
|
+
return items;
|
|
764
|
+
};
|
|
765
|
+
|
|
573
766
|
/**
|
|
574
767
|
* @param {Number} v
|
|
575
768
|
* @return {Number}
|
|
@@ -580,7 +773,7 @@ const clampInfinity = v => v === Infinity ? maxValue : v === -Infinity ? -1e12 :
|
|
|
580
773
|
* @param {Number} v
|
|
581
774
|
* @return {Number}
|
|
582
775
|
*/
|
|
583
|
-
const
|
|
776
|
+
const normalizeTime = v => v <= minValue ? minValue : clampInfinity(round(v, 11));
|
|
584
777
|
|
|
585
778
|
// Arrays
|
|
586
779
|
|
|
@@ -666,6 +859,32 @@ const addChild = (parent, child, sortMethod, prevProp = '_prev', nextProp = '_ne
|
|
|
666
859
|
child[nextProp] = next;
|
|
667
860
|
};
|
|
668
861
|
|
|
862
|
+
/**
|
|
863
|
+
* @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
|
|
864
|
+
* @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
|
|
865
|
+
*/
|
|
866
|
+
const createRefreshable = constructor => {
|
|
867
|
+
/** @type {Tickable} */
|
|
868
|
+
let tracked;
|
|
869
|
+
return (...args) => {
|
|
870
|
+
let currentIteration, currentIterationProgress, reversed, alternate;
|
|
871
|
+
if (tracked) {
|
|
872
|
+
currentIteration = tracked.currentIteration;
|
|
873
|
+
currentIterationProgress = tracked.iterationProgress;
|
|
874
|
+
reversed = tracked.reversed;
|
|
875
|
+
alternate = tracked._alternate;
|
|
876
|
+
tracked.revert();
|
|
877
|
+
}
|
|
878
|
+
const cleanup = constructor(...args);
|
|
879
|
+
if (cleanup && !isFnc(cleanup) && cleanup.revert) tracked = cleanup;
|
|
880
|
+
if (!isUnd(currentIterationProgress)) {
|
|
881
|
+
/** @type {Tickable} */(tracked).currentIteration = currentIteration;
|
|
882
|
+
/** @type {Tickable} */(tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
|
|
883
|
+
}
|
|
884
|
+
return cleanup || noop;
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
|
|
669
888
|
/*
|
|
670
889
|
* Base class to control framerate and playback rate.
|
|
671
890
|
* Inherited by Engine, Timer, Animation and Timeline.
|
|
@@ -1174,8 +1393,8 @@ const addAdditiveAnimation = lookups => {
|
|
|
1174
1393
|
return animation;
|
|
1175
1394
|
};
|
|
1176
1395
|
|
|
1177
|
-
const engineTickMethod = isBrowser ? requestAnimationFrame : setImmediate;
|
|
1178
|
-
const engineCancelMethod = isBrowser ? cancelAnimationFrame : clearImmediate;
|
|
1396
|
+
const engineTickMethod = /*#__PURE__*/ (() => isBrowser ? requestAnimationFrame : setImmediate)();
|
|
1397
|
+
const engineCancelMethod = /*#__PURE__*/ (() => isBrowser ? cancelAnimationFrame : clearImmediate)();
|
|
1179
1398
|
|
|
1180
1399
|
class Engine extends Clock {
|
|
1181
1400
|
|
|
@@ -1349,7 +1568,7 @@ const parseInlineTransforms = (target, propName, animationInlineStyles) => {
|
|
|
1349
1568
|
* @return {NodeList|HTMLCollection}
|
|
1350
1569
|
*/
|
|
1351
1570
|
function getNodeList(v) {
|
|
1352
|
-
const n = isStr(v) ?
|
|
1571
|
+
const n = isStr(v) ? scope.root.querySelectorAll(v) : v;
|
|
1353
1572
|
if (n instanceof NodeList || n instanceof HTMLCollection) return n;
|
|
1354
1573
|
}
|
|
1355
1574
|
|
|
@@ -1504,43 +1723,61 @@ const morphTo = (path2, precision = .33) => ($path1) => {
|
|
|
1504
1723
|
};
|
|
1505
1724
|
|
|
1506
1725
|
/**
|
|
1507
|
-
* @param {SVGGeometryElement} $el
|
|
1508
|
-
* @
|
|
1509
|
-
|
|
1510
|
-
|
|
1726
|
+
* @param {SVGGeometryElement} [$el]
|
|
1727
|
+
* @return {Number}
|
|
1728
|
+
*/
|
|
1729
|
+
const getScaleFactor = $el => {
|
|
1730
|
+
let scaleFactor = 1;
|
|
1731
|
+
if ($el && $el.getCTM) {
|
|
1732
|
+
const ctm = $el.getCTM();
|
|
1733
|
+
if (ctm) {
|
|
1734
|
+
const scaleX = sqrt(ctm.a * ctm.a + ctm.b * ctm.b);
|
|
1735
|
+
const scaleY = sqrt(ctm.c * ctm.c + ctm.d * ctm.d);
|
|
1736
|
+
scaleFactor = (scaleX + scaleY) / 2;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return scaleFactor;
|
|
1740
|
+
};
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* Creates a proxy that wraps an SVGGeometryElement and adds drawing functionality.
|
|
1744
|
+
* @param {SVGGeometryElement} $el - The SVG element to transform into a drawable
|
|
1745
|
+
* @param {number} start - Starting position (0-1)
|
|
1746
|
+
* @param {number} end - Ending position (0-1)
|
|
1747
|
+
* @return {DrawableSVGGeometry} - Returns a proxy that preserves the original element's type with additional 'draw' attribute functionality
|
|
1511
1748
|
*/
|
|
1512
|
-
|
|
1513
|
-
const strokeLineCap = getComputedStyle($el).strokeLinecap;
|
|
1749
|
+
const createDrawableProxy = ($el, start, end) => {
|
|
1514
1750
|
const pathLength = K;
|
|
1751
|
+
const computedStyles = getComputedStyle($el);
|
|
1752
|
+
const strokeLineCap = computedStyles.strokeLinecap;
|
|
1753
|
+
// @ts-ignore
|
|
1754
|
+
const $scalled = computedStyles.vectorEffect === 'non-scaling-stroke' ? $el : null;
|
|
1515
1755
|
let currentCap = strokeLineCap;
|
|
1756
|
+
|
|
1516
1757
|
const proxy = new Proxy($el, {
|
|
1517
1758
|
get(target, property) {
|
|
1518
1759
|
const value = target[property];
|
|
1519
1760
|
if (property === proxyTargetSymbol) return target;
|
|
1520
1761
|
if (property === 'setAttribute') {
|
|
1521
|
-
/** @param {any[]} args */
|
|
1522
1762
|
return (...args) => {
|
|
1523
1763
|
if (args[0] === 'draw') {
|
|
1524
1764
|
const value = args[1];
|
|
1525
1765
|
const values = value.split(' ');
|
|
1526
1766
|
const v1 = +values[0];
|
|
1527
1767
|
const v2 = +values[1];
|
|
1528
|
-
|
|
1529
1768
|
// TOTO: Benchmark if performing two slices is more performant than one split
|
|
1530
|
-
|
|
1531
1769
|
// const spaceIndex = value.indexOf(' ');
|
|
1532
1770
|
// const v1 = round(+value.slice(0, spaceIndex), precision);
|
|
1533
1771
|
// const v2 = round(+value.slice(spaceIndex + 1), precision);
|
|
1534
|
-
|
|
1535
|
-
const os = v1 * -1e3;
|
|
1536
|
-
const d1 = (v2 * pathLength) + os;
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
// Handle cases where the cap is still visible when the line is completly hidden
|
|
1772
|
+
const scaleFactor = getScaleFactor($scalled);
|
|
1773
|
+
const os = v1 * -1e3 * scaleFactor;
|
|
1774
|
+
const d1 = (v2 * pathLength * scaleFactor) + os;
|
|
1775
|
+
const d2 = (pathLength * scaleFactor +
|
|
1776
|
+
((v1 === 0 && v2 === 1) || (v1 === 1 && v2 === 0) ? 0 : 10 * scaleFactor) - d1);
|
|
1540
1777
|
if (strokeLineCap !== 'butt') {
|
|
1541
1778
|
const newCap = v1 === v2 ? 'butt' : strokeLineCap;
|
|
1542
1779
|
if (currentCap !== newCap) {
|
|
1543
|
-
target.
|
|
1780
|
+
target.style.strokeLinecap = `${newCap}`;
|
|
1544
1781
|
currentCap = newCap;
|
|
1545
1782
|
}
|
|
1546
1783
|
}
|
|
@@ -1550,31 +1787,37 @@ function createDrawableProxy($el, start, end) {
|
|
|
1550
1787
|
return Reflect.apply(value, target, args);
|
|
1551
1788
|
};
|
|
1552
1789
|
}
|
|
1790
|
+
|
|
1553
1791
|
if (isFnc(value)) {
|
|
1554
|
-
/** @param {any[]} args */
|
|
1555
1792
|
return (...args) => Reflect.apply(value, target, args);
|
|
1556
1793
|
} else {
|
|
1557
1794
|
return value;
|
|
1558
1795
|
}
|
|
1559
1796
|
}
|
|
1560
1797
|
});
|
|
1798
|
+
|
|
1561
1799
|
if ($el.getAttribute('pathLength') !== `${pathLength}`) {
|
|
1562
1800
|
$el.setAttribute('pathLength', `${pathLength}`);
|
|
1563
1801
|
proxy.setAttribute('draw', `${start} ${end}`);
|
|
1564
1802
|
}
|
|
1565
|
-
|
|
1566
|
-
}
|
|
1803
|
+
|
|
1804
|
+
return /** @type {DrawableSVGGeometry} */(proxy);
|
|
1805
|
+
};
|
|
1567
1806
|
|
|
1568
1807
|
/**
|
|
1569
|
-
*
|
|
1570
|
-
* @param {
|
|
1571
|
-
* @param {
|
|
1572
|
-
* @
|
|
1808
|
+
* Creates drawable proxies for multiple SVG elements.
|
|
1809
|
+
* @param {TargetsParam} selector - CSS selector, SVG element, or array of elements and selectors
|
|
1810
|
+
* @param {number} [start=0] - Starting position (0-1)
|
|
1811
|
+
* @param {number} [end=0] - Ending position (0-1)
|
|
1812
|
+
* @return {Array<DrawableSVGGeometry>} - Array of proxied elements with drawing functionality
|
|
1573
1813
|
*/
|
|
1574
1814
|
const createDrawable = (selector, start = 0, end = 0) => {
|
|
1575
|
-
const els =
|
|
1576
|
-
els.
|
|
1577
|
-
|
|
1815
|
+
const els = parseTargets(selector);
|
|
1816
|
+
return els.map($el => createDrawableProxy(
|
|
1817
|
+
/** @type {SVGGeometryElement} */($el),
|
|
1818
|
+
start,
|
|
1819
|
+
end
|
|
1820
|
+
));
|
|
1578
1821
|
};
|
|
1579
1822
|
|
|
1580
1823
|
// Motion path animation
|
|
@@ -2258,7 +2501,7 @@ class Timer extends Clock {
|
|
|
2258
2501
|
onUpdate,
|
|
2259
2502
|
} = parameters;
|
|
2260
2503
|
|
|
2261
|
-
if (
|
|
2504
|
+
if (scope.current) scope.current.register(this);
|
|
2262
2505
|
|
|
2263
2506
|
const timerInitTime = parent ? 0 : engine._elapsedTime;
|
|
2264
2507
|
const timerDefaults = parent ? parent.defaults : globals.defaults;
|
|
@@ -2388,7 +2631,7 @@ class Timer extends Clock {
|
|
|
2388
2631
|
}
|
|
2389
2632
|
|
|
2390
2633
|
get progress() {
|
|
2391
|
-
return clamp(round(this._currentTime / this.duration,
|
|
2634
|
+
return clamp(round(this._currentTime / this.duration, 10), 0, 1);
|
|
2392
2635
|
}
|
|
2393
2636
|
|
|
2394
2637
|
/** @param {Number} progress */
|
|
@@ -2397,7 +2640,7 @@ class Timer extends Clock {
|
|
|
2397
2640
|
}
|
|
2398
2641
|
|
|
2399
2642
|
get iterationProgress() {
|
|
2400
|
-
return clamp(round(this._iterationTime / this.iterationDuration,
|
|
2643
|
+
return clamp(round(this._iterationTime / this.iterationDuration, 10), 0, 1);
|
|
2401
2644
|
}
|
|
2402
2645
|
|
|
2403
2646
|
/** @param {Number} progress */
|
|
@@ -2589,11 +2832,12 @@ class Timer extends Clock {
|
|
|
2589
2832
|
*/
|
|
2590
2833
|
stretch(newDuration) {
|
|
2591
2834
|
const currentDuration = this.duration;
|
|
2592
|
-
|
|
2835
|
+
const normlizedDuration = normalizeTime(newDuration);
|
|
2836
|
+
if (currentDuration === normlizedDuration) return this;
|
|
2593
2837
|
const timeScale = newDuration / currentDuration;
|
|
2594
2838
|
const isSetter = newDuration <= minValue;
|
|
2595
|
-
this.duration = isSetter ? minValue :
|
|
2596
|
-
this.iterationDuration = isSetter ? minValue :
|
|
2839
|
+
this.duration = isSetter ? minValue : normlizedDuration;
|
|
2840
|
+
this.iterationDuration = isSetter ? minValue : normalizeTime(this.iterationDuration * timeScale);
|
|
2597
2841
|
this._offset *= timeScale;
|
|
2598
2842
|
this._delay *= timeScale;
|
|
2599
2843
|
this._loopDelay *= timeScale;
|
|
@@ -3637,13 +3881,13 @@ class JSAnimation extends Timer {
|
|
|
3637
3881
|
*/
|
|
3638
3882
|
stretch(newDuration) {
|
|
3639
3883
|
const currentDuration = this.duration;
|
|
3640
|
-
if (currentDuration ===
|
|
3884
|
+
if (currentDuration === normalizeTime(newDuration)) return this;
|
|
3641
3885
|
const timeScale = newDuration / currentDuration;
|
|
3642
3886
|
// NOTE: Find a better way to handle the stretch of an animation after stretch = 0
|
|
3643
3887
|
forEachChildren(this, (/** @type {Tween} */tween) => {
|
|
3644
3888
|
// Rounding is necessary here to minimize floating point errors
|
|
3645
|
-
tween._updateDuration =
|
|
3646
|
-
tween._changeDuration =
|
|
3889
|
+
tween._updateDuration = normalizeTime(tween._updateDuration * timeScale);
|
|
3890
|
+
tween._changeDuration = normalizeTime(tween._changeDuration * timeScale);
|
|
3647
3891
|
tween._currentTime *= timeScale;
|
|
3648
3892
|
tween._startTime *= timeScale;
|
|
3649
3893
|
tween._absoluteStartTime *= timeScale;
|
|
@@ -3656,12 +3900,13 @@ class JSAnimation extends Timer {
|
|
|
3656
3900
|
*/
|
|
3657
3901
|
refresh() {
|
|
3658
3902
|
forEachChildren(this, (/** @type {Tween} */tween) => {
|
|
3659
|
-
const
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3903
|
+
const tweenFunc = tween._func;
|
|
3904
|
+
if (tweenFunc) {
|
|
3905
|
+
const ogValue = getOriginalAnimatableValue(tween.target, tween.property, tween._tweenType);
|
|
3906
|
+
decomposeRawValue(ogValue, decomposedOriginalValue);
|
|
3907
|
+
decomposeRawValue(tweenFunc(), toTargetObject);
|
|
3908
|
+
tween._fromNumbers = cloneArray(decomposedOriginalValue.d);
|
|
3909
|
+
tween._fromNumber = decomposedOriginalValue.n;
|
|
3665
3910
|
tween._toNumbers = cloneArray(toTargetObject.d);
|
|
3666
3911
|
tween._strings = cloneArray(toTargetObject.s);
|
|
3667
3912
|
tween._toNumber = toTargetObject.n;
|
|
@@ -3745,13 +3990,14 @@ const parseWAAPIEasing = (ease) => {
|
|
|
3745
3990
|
const parsed = parseEaseString(ease, WAAPIeases, WAAPIEasesLookups);
|
|
3746
3991
|
if (isFnc(parsed)) parsedEase = parsed === none ? 'linear' : easingToLinear(parsed);
|
|
3747
3992
|
}
|
|
3993
|
+
WAAPIEasesLookups[ease] = parsedEase;
|
|
3748
3994
|
} else if (isFnc(ease)) {
|
|
3749
3995
|
const easing = easingToLinear(ease);
|
|
3750
3996
|
if (easing) parsedEase = easing;
|
|
3751
3997
|
} else if (/** @type {Spring} */(ease).ease) {
|
|
3752
3998
|
parsedEase = easingToLinear(/** @type {Spring} */(ease).ease);
|
|
3753
3999
|
}
|
|
3754
|
-
return
|
|
4000
|
+
return parsedEase;
|
|
3755
4001
|
};
|
|
3756
4002
|
|
|
3757
4003
|
/**
|
|
@@ -3819,28 +4065,9 @@ const commonDefaultPXProperties = [
|
|
|
3819
4065
|
...transformsShorthands
|
|
3820
4066
|
];
|
|
3821
4067
|
|
|
3822
|
-
const validIndividualTransforms = [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))];
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
let transformsPropertiesRegistered = isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'));
|
|
3826
|
-
|
|
3827
|
-
const registerTransformsProperties = () => {
|
|
3828
|
-
validTransforms.forEach(t => {
|
|
3829
|
-
const isSkew = stringStartsWith(t, 'skew');
|
|
3830
|
-
const isScale = stringStartsWith(t, 'scale');
|
|
3831
|
-
const isRotate = stringStartsWith(t, 'rotate');
|
|
3832
|
-
const isTranslate = stringStartsWith(t, 'translate');
|
|
3833
|
-
const isAngle = isRotate || isSkew;
|
|
3834
|
-
const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
|
|
3835
|
-
CSS.registerProperty({
|
|
3836
|
-
name: '--' + t,
|
|
3837
|
-
syntax,
|
|
3838
|
-
inherits: false,
|
|
3839
|
-
initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
|
|
3840
|
-
});
|
|
3841
|
-
});
|
|
3842
|
-
transformsPropertiesRegistered = true;
|
|
3843
|
-
};
|
|
4068
|
+
const validIndividualTransforms = /*#__PURE__*/ (() => [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))])();
|
|
4069
|
+
|
|
4070
|
+
let transformsPropertiesRegistered = null;
|
|
3844
4071
|
|
|
3845
4072
|
const WAAPIAnimationsLookups = {
|
|
3846
4073
|
_head: null,
|
|
@@ -3951,9 +4178,31 @@ class WAAPIAnimation {
|
|
|
3951
4178
|
*/
|
|
3952
4179
|
constructor(targets, params) {
|
|
3953
4180
|
|
|
3954
|
-
if (
|
|
4181
|
+
if (scope.current) scope.current.register(this);
|
|
3955
4182
|
|
|
3956
|
-
|
|
4183
|
+
// Skip the registration and fallback to no animation in case CSS.registerProperty is not supported
|
|
4184
|
+
if (isNil(transformsPropertiesRegistered)) {
|
|
4185
|
+
if (isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'))) {
|
|
4186
|
+
transformsPropertiesRegistered = false;
|
|
4187
|
+
} else {
|
|
4188
|
+
validTransforms.forEach(t => {
|
|
4189
|
+
const isSkew = stringStartsWith(t, 'skew');
|
|
4190
|
+
const isScale = stringStartsWith(t, 'scale');
|
|
4191
|
+
const isRotate = stringStartsWith(t, 'rotate');
|
|
4192
|
+
const isTranslate = stringStartsWith(t, 'translate');
|
|
4193
|
+
const isAngle = isRotate || isSkew;
|
|
4194
|
+
const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
|
|
4195
|
+
try {
|
|
4196
|
+
CSS.registerProperty({
|
|
4197
|
+
name: '--' + t,
|
|
4198
|
+
syntax,
|
|
4199
|
+
inherits: false,
|
|
4200
|
+
initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
|
|
4201
|
+
});
|
|
4202
|
+
} catch {} });
|
|
4203
|
+
transformsPropertiesRegistered = true;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
3957
4206
|
|
|
3958
4207
|
const parsedTargets = registerTargets(targets);
|
|
3959
4208
|
const targetsLength = parsedTargets.length;
|
|
@@ -4120,7 +4369,13 @@ class WAAPIAnimation {
|
|
|
4120
4369
|
/** @param {Number} time */
|
|
4121
4370
|
set currentTime(time) {
|
|
4122
4371
|
const t = time * (globals.timeScale === 1 ? 1 : K);
|
|
4123
|
-
this.forEach(anim =>
|
|
4372
|
+
this.forEach(anim => {
|
|
4373
|
+
// Make sure the animation playState is not 'paused' in order to properly trigger an onfinish callback.
|
|
4374
|
+
// The "paused" play state supersedes the "finished" play state; if the animation is both paused and finished, the "paused" state is the one that will be reported.
|
|
4375
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Animation/finish_event
|
|
4376
|
+
if (t >= this.duration) anim.play();
|
|
4377
|
+
anim.currentTime = t;
|
|
4378
|
+
});
|
|
4124
4379
|
}
|
|
4125
4380
|
|
|
4126
4381
|
get progress() {
|
|
@@ -4401,12 +4656,10 @@ const remove = (targets, renderable, propertyName) => {
|
|
|
4401
4656
|
};
|
|
4402
4657
|
|
|
4403
4658
|
/**
|
|
4404
|
-
* @param {
|
|
4405
|
-
* @
|
|
4406
|
-
* @param {Number} [decimalLength]
|
|
4407
|
-
* @return {Number}
|
|
4659
|
+
* @param {(...args: any[]) => Tickable} constructor
|
|
4660
|
+
* @return {(...args: any[]) => Tickable}
|
|
4408
4661
|
*/
|
|
4409
|
-
const
|
|
4662
|
+
const keepTime = createRefreshable;
|
|
4410
4663
|
|
|
4411
4664
|
/**
|
|
4412
4665
|
* @param {String|Array} items
|
|
@@ -4414,17 +4667,6 @@ const random = (min, max, decimalLength) => { const m = 10 ** (decimalLength ||
|
|
|
4414
4667
|
*/
|
|
4415
4668
|
const randomPick = items => items[random(0, items.length - 1)];
|
|
4416
4669
|
|
|
4417
|
-
/**
|
|
4418
|
-
* Adapted from https://bost.ocks.org/mike/shuffle/
|
|
4419
|
-
* @param {Array} items
|
|
4420
|
-
* @return {Array}
|
|
4421
|
-
*/
|
|
4422
|
-
const shuffle = items => {
|
|
4423
|
-
let m = items.length, t, i;
|
|
4424
|
-
while (m) { i = random(0, --m); t = items[m]; items[m] = items[i]; items[i] = t; }
|
|
4425
|
-
return items;
|
|
4426
|
-
};
|
|
4427
|
-
|
|
4428
4670
|
/**
|
|
4429
4671
|
* @param {Number|String} v
|
|
4430
4672
|
* @param {Number} decimalLength
|
|
@@ -4619,6 +4861,7 @@ const utils = {
|
|
|
4619
4861
|
shuffle,
|
|
4620
4862
|
lerp,
|
|
4621
4863
|
sync,
|
|
4864
|
+
keepTime,
|
|
4622
4865
|
clamp: /** @type {typeof clamp & ChainedClamp} */(makeChainable(clamp)),
|
|
4623
4866
|
round: /** @type {typeof round & ChainedRound} */(makeChainable(round)),
|
|
4624
4867
|
snap: /** @type {typeof snap & ChainedSnap} */(makeChainable(snap)),
|
|
@@ -4907,15 +5150,11 @@ class Timeline extends Timer {
|
|
|
4907
5150
|
*/
|
|
4908
5151
|
stretch(newDuration) {
|
|
4909
5152
|
const currentDuration = this.duration;
|
|
4910
|
-
if (currentDuration ===
|
|
5153
|
+
if (currentDuration === normalizeTime(newDuration)) return this;
|
|
4911
5154
|
const timeScale = newDuration / currentDuration;
|
|
4912
5155
|
const labels = this.labels;
|
|
4913
|
-
forEachChildren(this, (/** @type {JSAnimation} */child) =>
|
|
4914
|
-
|
|
4915
|
-
});
|
|
4916
|
-
for (let labelName in labels) {
|
|
4917
|
-
labels[labelName] *= timeScale;
|
|
4918
|
-
}
|
|
5156
|
+
forEachChildren(this, (/** @type {JSAnimation} */child) => child.stretch(child.duration * timeScale));
|
|
5157
|
+
for (let labelName in labels) labels[labelName] *= timeScale;
|
|
4919
5158
|
return super.stretch(newDuration);
|
|
4920
5159
|
}
|
|
4921
5160
|
|
|
@@ -4962,7 +5201,7 @@ class Animatable {
|
|
|
4962
5201
|
* @param {AnimatableParams} parameters
|
|
4963
5202
|
*/
|
|
4964
5203
|
constructor(targets, parameters) {
|
|
4965
|
-
if (
|
|
5204
|
+
if (scope.current) scope.current.register(this);
|
|
4966
5205
|
/** @type {AnimationParams} */
|
|
4967
5206
|
const globalParams = {};
|
|
4968
5207
|
const properties = {};
|
|
@@ -5281,12 +5520,6 @@ class Transforms {
|
|
|
5281
5520
|
}
|
|
5282
5521
|
}
|
|
5283
5522
|
|
|
5284
|
-
/**
|
|
5285
|
-
* @typedef {Object} DraggableCursorParams
|
|
5286
|
-
* @property {String} [onHover]
|
|
5287
|
-
* @property {String} [onGrab]
|
|
5288
|
-
*/
|
|
5289
|
-
|
|
5290
5523
|
/**
|
|
5291
5524
|
* @template {Array<Number>|DOMTargetSelector|String|Number|Boolean|Function|DraggableCursorParams} T
|
|
5292
5525
|
* @param {T | ((draggable: Draggable) => T)} value
|
|
@@ -5297,47 +5530,6 @@ const parseDraggableFunctionParameter = (value, draggable) => value && isFnc(val
|
|
|
5297
5530
|
|
|
5298
5531
|
let zIndex = 0;
|
|
5299
5532
|
|
|
5300
|
-
/**
|
|
5301
|
-
* @typedef {Object} DraggableAxisParam
|
|
5302
|
-
* @property {String} [mapTo]
|
|
5303
|
-
* @property {TweenModifier} [modifier]
|
|
5304
|
-
* @property {TweenComposition} [composition]
|
|
5305
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
5306
|
-
*/
|
|
5307
|
-
|
|
5308
|
-
/**
|
|
5309
|
-
* @typedef {Object} DraggableParams
|
|
5310
|
-
* @property {DOMTargetSelector} [trigger]
|
|
5311
|
-
* @property {DOMTargetSelector|Array<Number>|((draggable: Draggable) => DOMTargetSelector|Array<Number>)} [container]
|
|
5312
|
-
* @property {Boolean|DraggableAxisParam} [x]
|
|
5313
|
-
* @property {Boolean|DraggableAxisParam} [y]
|
|
5314
|
-
* @property {TweenModifier} [modifier]
|
|
5315
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
5316
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [containerPadding]
|
|
5317
|
-
* @property {Number|((draggable: Draggable) => Number)} [containerFriction]
|
|
5318
|
-
* @property {Number|((draggable: Draggable) => Number)} [releaseContainerFriction]
|
|
5319
|
-
* @property {Number|((draggable: Draggable) => Number)} [dragSpeed]
|
|
5320
|
-
* @property {Number|((draggable: Draggable) => Number)} [scrollSpeed]
|
|
5321
|
-
* @property {Number|((draggable: Draggable) => Number)} [scrollThreshold]
|
|
5322
|
-
* @property {Number|((draggable: Draggable) => Number)} [minVelocity]
|
|
5323
|
-
* @property {Number|((draggable: Draggable) => Number)} [maxVelocity]
|
|
5324
|
-
* @property {Number|((draggable: Draggable) => Number)} [velocityMultiplier]
|
|
5325
|
-
* @property {Number} [releaseMass]
|
|
5326
|
-
* @property {Number} [releaseStiffness]
|
|
5327
|
-
* @property {Number} [releaseDamping]
|
|
5328
|
-
* @property {Boolean} [releaseDamping]
|
|
5329
|
-
* @property {EasingParam} [releaseEase]
|
|
5330
|
-
* @property {Boolean|DraggableCursorParams|((draggable: Draggable) => Boolean|DraggableCursorParams)} [cursor]
|
|
5331
|
-
* @property {Callback<Draggable>} [onGrab]
|
|
5332
|
-
* @property {Callback<Draggable>} [onDrag]
|
|
5333
|
-
* @property {Callback<Draggable>} [onRelease]
|
|
5334
|
-
* @property {Callback<Draggable>} [onUpdate]
|
|
5335
|
-
* @property {Callback<Draggable>} [onSettle]
|
|
5336
|
-
* @property {Callback<Draggable>} [onSnap]
|
|
5337
|
-
* @property {Callback<Draggable>} [onResize]
|
|
5338
|
-
* @property {Callback<Draggable>} [onAfterResize]
|
|
5339
|
-
*/
|
|
5340
|
-
|
|
5341
5533
|
class Draggable {
|
|
5342
5534
|
/**
|
|
5343
5535
|
* @param {TargetsParam} target
|
|
@@ -5345,7 +5537,7 @@ class Draggable {
|
|
|
5345
5537
|
*/
|
|
5346
5538
|
constructor(target, parameters = {}) {
|
|
5347
5539
|
if (!target) return;
|
|
5348
|
-
if (
|
|
5540
|
+
if (scope.current) scope.current.register(this);
|
|
5349
5541
|
const paramX = parameters.x;
|
|
5350
5542
|
const paramY = parameters.y;
|
|
5351
5543
|
const trigger = parameters.trigger;
|
|
@@ -5536,7 +5728,7 @@ class Draggable {
|
|
|
5536
5728
|
this.canScroll = false;
|
|
5537
5729
|
this.enabled = false;
|
|
5538
5730
|
this.initialized = false;
|
|
5539
|
-
this.activeProp = this.disabled[
|
|
5731
|
+
this.activeProp = this.disabled[1] ? xProp : yProp;
|
|
5540
5732
|
this.animate.animations[this.activeProp].onRender = () => {
|
|
5541
5733
|
const hasUpdated = this.updated;
|
|
5542
5734
|
const hasMoved = this.grabbed && hasUpdated;
|
|
@@ -6356,6 +6548,7 @@ class Draggable {
|
|
|
6356
6548
|
this.overshootXTicker.revert();
|
|
6357
6549
|
this.overshootYTicker.revert();
|
|
6358
6550
|
this.resizeTicker.revert();
|
|
6551
|
+
this.animate.revert();
|
|
6359
6552
|
return this;
|
|
6360
6553
|
}
|
|
6361
6554
|
|
|
@@ -6405,44 +6598,10 @@ const createDraggable = (target, parameters) => new Draggable(target, parameters
|
|
|
6405
6598
|
|
|
6406
6599
|
|
|
6407
6600
|
|
|
6408
|
-
/**
|
|
6409
|
-
* @typedef {Object} ReactRef
|
|
6410
|
-
* @property {HTMLElement|SVGElement|null} [current]
|
|
6411
|
-
*/
|
|
6412
|
-
|
|
6413
|
-
/**
|
|
6414
|
-
* @typedef {Object} AngularRef
|
|
6415
|
-
* @property {HTMLElement|SVGElement} [nativeElement]
|
|
6416
|
-
*/
|
|
6417
|
-
|
|
6418
|
-
/**
|
|
6419
|
-
* @typedef {Object} ScopeParams
|
|
6420
|
-
* @property {DOMTargetSelector|ReactRef|AngularRef} [root]
|
|
6421
|
-
* @property {DefaultsParams} [defaults]
|
|
6422
|
-
* @property {Record<String, String>} [mediaQueries]
|
|
6423
|
-
*/
|
|
6424
|
-
|
|
6425
|
-
/**
|
|
6426
|
-
* @callback ScopeCleanup
|
|
6427
|
-
* @param {Scope} [scope]
|
|
6428
|
-
*/
|
|
6429
|
-
|
|
6430
|
-
/**
|
|
6431
|
-
* @callback ScopeConstructor
|
|
6432
|
-
* @param {Scope} [scope]
|
|
6433
|
-
* @return {ScopeCleanup|void}
|
|
6434
|
-
*/
|
|
6435
|
-
|
|
6436
|
-
/**
|
|
6437
|
-
* @callback ScopeMethod
|
|
6438
|
-
* @param {...*} args
|
|
6439
|
-
* @return {ScopeCleanup|void}
|
|
6440
|
-
*/
|
|
6441
|
-
|
|
6442
6601
|
class Scope {
|
|
6443
6602
|
/** @param {ScopeParams} [parameters] */
|
|
6444
6603
|
constructor(parameters = {}) {
|
|
6445
|
-
if (
|
|
6604
|
+
if (scope.current) scope.current.register(this);
|
|
6446
6605
|
const rootParam = parameters.root;
|
|
6447
6606
|
/** @type {Document|DOMTarget} */
|
|
6448
6607
|
let root = doc;
|
|
@@ -6459,13 +6618,23 @@ class Scope {
|
|
|
6459
6618
|
this.defaults = scopeDefaults ? mergeObjects(scopeDefaults, globalDefault) : globalDefault;
|
|
6460
6619
|
/** @type {Document|DOMTarget} */
|
|
6461
6620
|
this.root = root;
|
|
6462
|
-
/** @type {Array<
|
|
6621
|
+
/** @type {Array<ScopeConstructorCallback>} */
|
|
6463
6622
|
this.constructors = [];
|
|
6464
|
-
/** @type {Array<
|
|
6623
|
+
/** @type {Array<ScopeCleanupCallback>} */
|
|
6465
6624
|
this.revertConstructors = [];
|
|
6466
6625
|
/** @type {Array<Revertible>} */
|
|
6467
6626
|
this.revertibles = [];
|
|
6468
|
-
/** @type {
|
|
6627
|
+
/** @type {Array<ScopeConstructorCallback | ((scope: this) => Tickable)>} */
|
|
6628
|
+
this.constructorsOnce = [];
|
|
6629
|
+
/** @type {Array<ScopeCleanupCallback>} */
|
|
6630
|
+
this.revertConstructorsOnce = [];
|
|
6631
|
+
/** @type {Array<Revertible>} */
|
|
6632
|
+
this.revertiblesOnce = [];
|
|
6633
|
+
/** @type {Boolean} */
|
|
6634
|
+
this.once = false;
|
|
6635
|
+
/** @type {Number} */
|
|
6636
|
+
this.onceIndex = 0;
|
|
6637
|
+
/** @type {Record<String, ScopeMethod>} */
|
|
6469
6638
|
this.methods = {};
|
|
6470
6639
|
/** @type {Record<String, Boolean>} */
|
|
6471
6640
|
this.matches = {};
|
|
@@ -6483,25 +6652,30 @@ class Scope {
|
|
|
6483
6652
|
}
|
|
6484
6653
|
|
|
6485
6654
|
/**
|
|
6486
|
-
* @
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6655
|
+
* @param {Revertible} revertible
|
|
6656
|
+
*/
|
|
6657
|
+
register(revertible) {
|
|
6658
|
+
const store = this.once ? this.revertiblesOnce : this.revertibles;
|
|
6659
|
+
store.push(revertible);
|
|
6660
|
+
}
|
|
6661
|
+
|
|
6662
|
+
/**
|
|
6663
|
+
* @template T
|
|
6664
|
+
* @param {ScopedCallback<T>} cb
|
|
6665
|
+
* @return {T}
|
|
6492
6666
|
*/
|
|
6493
6667
|
execute(cb) {
|
|
6494
|
-
let activeScope =
|
|
6495
|
-
let activeRoot =
|
|
6668
|
+
let activeScope = scope.current;
|
|
6669
|
+
let activeRoot = scope.root;
|
|
6496
6670
|
let activeDefaults = globals.defaults;
|
|
6497
|
-
|
|
6498
|
-
|
|
6671
|
+
scope.current = this;
|
|
6672
|
+
scope.root = this.root;
|
|
6499
6673
|
globals.defaults = this.defaults;
|
|
6500
6674
|
const mqs = this.mediaQueryLists;
|
|
6501
6675
|
for (let mq in mqs) this.matches[mq] = mqs[mq].matches;
|
|
6502
6676
|
const returned = cb(this);
|
|
6503
|
-
|
|
6504
|
-
|
|
6677
|
+
scope.current = activeScope;
|
|
6678
|
+
scope.root = activeRoot;
|
|
6505
6679
|
globals.defaults = activeDefaults;
|
|
6506
6680
|
return returned;
|
|
6507
6681
|
}
|
|
@@ -6510,6 +6684,7 @@ class Scope {
|
|
|
6510
6684
|
* @return {this}
|
|
6511
6685
|
*/
|
|
6512
6686
|
refresh() {
|
|
6687
|
+
this.onceIndex = 0;
|
|
6513
6688
|
this.execute(() => {
|
|
6514
6689
|
let i = this.revertibles.length;
|
|
6515
6690
|
let y = this.revertConstructors.length;
|
|
@@ -6517,9 +6692,9 @@ class Scope {
|
|
|
6517
6692
|
while (y--) this.revertConstructors[y](this);
|
|
6518
6693
|
this.revertibles.length = 0;
|
|
6519
6694
|
this.revertConstructors.length = 0;
|
|
6520
|
-
this.constructors.forEach( constructor => {
|
|
6695
|
+
this.constructors.forEach((/** @type {ScopeConstructorCallback} */constructor) => {
|
|
6521
6696
|
const revertConstructor = constructor(this);
|
|
6522
|
-
if (revertConstructor) {
|
|
6697
|
+
if (isFnc(revertConstructor)) {
|
|
6523
6698
|
this.revertConstructors.push(revertConstructor);
|
|
6524
6699
|
}
|
|
6525
6700
|
});
|
|
@@ -6528,28 +6703,26 @@ class Scope {
|
|
|
6528
6703
|
}
|
|
6529
6704
|
|
|
6530
6705
|
/**
|
|
6531
|
-
* @callback contructorCallback
|
|
6532
|
-
* @param {this} self
|
|
6533
|
-
*
|
|
6534
6706
|
* @overload
|
|
6535
6707
|
* @param {String} a1
|
|
6536
6708
|
* @param {ScopeMethod} a2
|
|
6537
6709
|
* @return {this}
|
|
6538
6710
|
*
|
|
6539
6711
|
* @overload
|
|
6540
|
-
* @param {
|
|
6712
|
+
* @param {ScopeConstructorCallback} a1
|
|
6541
6713
|
* @return {this}
|
|
6542
6714
|
*
|
|
6543
|
-
* @param {String|
|
|
6715
|
+
* @param {String|ScopeConstructorCallback} a1
|
|
6544
6716
|
* @param {ScopeMethod} [a2]
|
|
6545
6717
|
*/
|
|
6546
6718
|
add(a1, a2) {
|
|
6719
|
+
this.once = false;
|
|
6547
6720
|
if (isFnc(a1)) {
|
|
6548
|
-
const constructor = /** @type {
|
|
6721
|
+
const constructor = /** @type {ScopeConstructorCallback} */(a1);
|
|
6549
6722
|
this.constructors.push(constructor);
|
|
6550
6723
|
this.execute(() => {
|
|
6551
6724
|
const revertConstructor = constructor(this);
|
|
6552
|
-
if (revertConstructor) {
|
|
6725
|
+
if (isFnc(revertConstructor)) {
|
|
6553
6726
|
this.revertConstructors.push(revertConstructor);
|
|
6554
6727
|
}
|
|
6555
6728
|
});
|
|
@@ -6559,6 +6732,46 @@ class Scope {
|
|
|
6559
6732
|
return this;
|
|
6560
6733
|
}
|
|
6561
6734
|
|
|
6735
|
+
/**
|
|
6736
|
+
* @param {ScopeConstructorCallback} scopeConstructorCallback
|
|
6737
|
+
* @return {this}
|
|
6738
|
+
*/
|
|
6739
|
+
addOnce(scopeConstructorCallback) {
|
|
6740
|
+
this.once = true;
|
|
6741
|
+
if (isFnc(scopeConstructorCallback)) {
|
|
6742
|
+
const currentIndex = this.onceIndex++;
|
|
6743
|
+
const tracked = this.constructorsOnce[currentIndex];
|
|
6744
|
+
if (tracked) return this;
|
|
6745
|
+
const constructor = /** @type {ScopeConstructorCallback} */(scopeConstructorCallback);
|
|
6746
|
+
this.constructorsOnce[currentIndex] = constructor;
|
|
6747
|
+
this.execute(() => {
|
|
6748
|
+
const revertConstructor = constructor(this);
|
|
6749
|
+
if (isFnc(revertConstructor)) {
|
|
6750
|
+
this.revertConstructorsOnce.push(revertConstructor);
|
|
6751
|
+
}
|
|
6752
|
+
});
|
|
6753
|
+
}
|
|
6754
|
+
return this;
|
|
6755
|
+
}
|
|
6756
|
+
|
|
6757
|
+
/**
|
|
6758
|
+
* @param {(scope: this) => Tickable} cb
|
|
6759
|
+
* @return {Tickable}
|
|
6760
|
+
*/
|
|
6761
|
+
keepTime(cb) {
|
|
6762
|
+
this.once = true;
|
|
6763
|
+
const currentIndex = this.onceIndex++;
|
|
6764
|
+
const tracked = /** @type {(scope: this) => Tickable} */(this.constructorsOnce[currentIndex]);
|
|
6765
|
+
if (isFnc(tracked)) return tracked(this);
|
|
6766
|
+
const constructor = /** @type {(scope: this) => Tickable} */(createRefreshable(cb));
|
|
6767
|
+
this.constructorsOnce[currentIndex] = constructor;
|
|
6768
|
+
let trackedTickable;
|
|
6769
|
+
this.execute(() => {
|
|
6770
|
+
trackedTickable = constructor(this);
|
|
6771
|
+
});
|
|
6772
|
+
return trackedTickable;
|
|
6773
|
+
}
|
|
6774
|
+
|
|
6562
6775
|
/**
|
|
6563
6776
|
* @param {Event} e
|
|
6564
6777
|
*/
|
|
@@ -6573,15 +6786,25 @@ class Scope {
|
|
|
6573
6786
|
revert() {
|
|
6574
6787
|
const revertibles = this.revertibles;
|
|
6575
6788
|
const revertConstructors = this.revertConstructors;
|
|
6789
|
+
const revertiblesOnce = this.revertiblesOnce;
|
|
6790
|
+
const revertConstructorsOnce = this.revertConstructorsOnce;
|
|
6576
6791
|
const mqs = this.mediaQueryLists;
|
|
6577
6792
|
let i = revertibles.length;
|
|
6578
|
-
let
|
|
6793
|
+
let j = revertConstructors.length;
|
|
6794
|
+
let k = revertiblesOnce.length;
|
|
6795
|
+
let l = revertConstructorsOnce.length;
|
|
6579
6796
|
while (i--) revertibles[i].revert();
|
|
6580
|
-
while (
|
|
6797
|
+
while (j--) revertConstructors[j](this);
|
|
6798
|
+
while (k--) revertiblesOnce[k].revert();
|
|
6799
|
+
while (l--) revertConstructorsOnce[l](this);
|
|
6581
6800
|
for (let mq in mqs) mqs[mq].removeEventListener('change', this);
|
|
6582
6801
|
revertibles.length = 0;
|
|
6583
6802
|
revertConstructors.length = 0;
|
|
6584
6803
|
this.constructors.length = 0;
|
|
6804
|
+
revertiblesOnce.length = 0;
|
|
6805
|
+
revertConstructorsOnce.length = 0;
|
|
6806
|
+
this.constructorsOnce.length = 0;
|
|
6807
|
+
this.onceIndex = 0;
|
|
6585
6808
|
this.matches = {};
|
|
6586
6809
|
this.methods = {};
|
|
6587
6810
|
this.mediaQueryLists = {};
|
|
@@ -6907,7 +7130,7 @@ const getAnimationDomTarget = linked => {
|
|
|
6907
7130
|
|
|
6908
7131
|
let scrollerIndex = 0;
|
|
6909
7132
|
|
|
6910
|
-
const debugColors = ['#FF4B4B','#FF971B','#FFC730','#F9F640','#7AFF5A','#18FF74','#17E09B','#3CFFEC','#05DBE9','#33B3F1','#638CF9','#C563FE','#FF4FCF','#F93F8A'];
|
|
7133
|
+
const debugColors$1 = ['#FF4B4B','#FF971B','#FFC730','#F9F640','#7AFF5A','#18FF74','#17E09B','#3CFFEC','#05DBE9','#33B3F1','#638CF9','#C563FE','#FF4FCF','#F93F8A'];
|
|
6911
7134
|
|
|
6912
7135
|
/**
|
|
6913
7136
|
* @typedef {Object} ScrollThresholdParam
|
|
@@ -6953,7 +7176,7 @@ class ScrollObserver {
|
|
|
6953
7176
|
* @param {ScrollObserverParams} parameters
|
|
6954
7177
|
*/
|
|
6955
7178
|
constructor(parameters = {}) {
|
|
6956
|
-
if (
|
|
7179
|
+
if (scope.current) scope.current.register(this);
|
|
6957
7180
|
const syncMode = setValue(parameters.sync, 'play pause');
|
|
6958
7181
|
const ease = syncMode ? parseEasings(/** @type {EasingParam} */(syncMode)) : null;
|
|
6959
7182
|
const isLinear = syncMode && (syncMode === 'linear' || syncMode === none);
|
|
@@ -7150,7 +7373,7 @@ class ScrollObserver {
|
|
|
7150
7373
|
const $debug = doc.createElement('div');
|
|
7151
7374
|
const $thresholds = doc.createElement('div');
|
|
7152
7375
|
const $triggers = doc.createElement('div');
|
|
7153
|
-
const color = debugColors[this.index % debugColors.length];
|
|
7376
|
+
const color = debugColors$1[this.index % debugColors$1.length];
|
|
7154
7377
|
const useWin = container.useWin;
|
|
7155
7378
|
const containerWidth = useWin ? container.winWidth : container.width;
|
|
7156
7379
|
const containerHeight = useWin ? container.winHeight : container.height;
|
|
@@ -7508,29 +7731,462 @@ const onScroll = (parameters = {}) => new ScrollObserver(parameters);
|
|
|
7508
7731
|
|
|
7509
7732
|
|
|
7510
7733
|
|
|
7734
|
+
const segmenter = !isUnd(Intl) && Intl.Segmenter;
|
|
7735
|
+
const valueRgx = /\{value\}/g;
|
|
7736
|
+
const indexRgx = /\{i\}/g;
|
|
7737
|
+
const whiteSpaceGroupRgx = /(\s+)/;
|
|
7738
|
+
const whiteSpaceRgx = /^\s+$/;
|
|
7739
|
+
const lineType = 'line';
|
|
7740
|
+
const wordType = 'word';
|
|
7741
|
+
const charType = 'char';
|
|
7742
|
+
const dataLine = `data-line`;
|
|
7743
|
+
|
|
7511
7744
|
/**
|
|
7512
|
-
* @typedef
|
|
7513
|
-
* @property {
|
|
7514
|
-
* @property {
|
|
7515
|
-
* @property {Boolean} [reversed]
|
|
7516
|
-
* @property {Array.<Number>} [grid]
|
|
7517
|
-
* @property {('x'|'y')} [axis]
|
|
7518
|
-
* @property {EasingParam} [ease]
|
|
7519
|
-
* @property {TweenModifier} [modifier]
|
|
7745
|
+
* @typedef {Object} Segment
|
|
7746
|
+
* @property {String} segment
|
|
7747
|
+
* @property {Boolean} [isWordLike]
|
|
7520
7748
|
*/
|
|
7521
7749
|
|
|
7522
7750
|
/**
|
|
7523
|
-
* @
|
|
7524
|
-
* @
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7751
|
+
* @typedef {Object} Segmenter
|
|
7752
|
+
* @property {function(String): Iterable<Segment>} segment
|
|
7753
|
+
*/
|
|
7754
|
+
|
|
7755
|
+
/** @type {Segmenter} */
|
|
7756
|
+
let wordSegmenter = null;
|
|
7757
|
+
/** @type {Segmenter} */
|
|
7758
|
+
let graphemeSegmenter = null;
|
|
7759
|
+
let $splitTemplate = null;
|
|
7760
|
+
|
|
7761
|
+
/**
|
|
7762
|
+
* @param {Segment} seg
|
|
7763
|
+
* @return {Boolean}
|
|
7764
|
+
*/
|
|
7765
|
+
const isSegmentWordLike = seg => {
|
|
7766
|
+
return seg.isWordLike ||
|
|
7767
|
+
seg.segment === ' ' || // Consider spaces as words first, then handle them diffrently later
|
|
7768
|
+
isNum(+seg.segment); // Safari doesn't considers numbers as words
|
|
7769
|
+
};
|
|
7770
|
+
|
|
7771
|
+
/**
|
|
7772
|
+
* @param {HTMLElement} $el
|
|
7773
|
+
*/
|
|
7774
|
+
const setAriaHidden = $el => $el.setAttribute('aria-hidden', 'true');
|
|
7775
|
+
|
|
7776
|
+
/**
|
|
7777
|
+
* @param {DOMTarget} $el
|
|
7778
|
+
* @param {String} type
|
|
7779
|
+
* @return {Array<HTMLElement>}
|
|
7780
|
+
*/
|
|
7781
|
+
const getAllTopLevelElements = ($el, type) => [.../** @type {*} */($el.querySelectorAll(`[data-${type}]:not([data-${type}] [data-${type}])`))];
|
|
7782
|
+
|
|
7783
|
+
const debugColors = { line: '#00D672', word: '#FF4B4B', char: '#5A87FF' };
|
|
7784
|
+
|
|
7785
|
+
/**
|
|
7786
|
+
* @param {HTMLElement} $el
|
|
7787
|
+
*/
|
|
7788
|
+
const filterEmptyElements = $el => {
|
|
7789
|
+
if (!$el.childElementCount && !$el.textContent.trim()) {
|
|
7790
|
+
const $parent = $el.parentElement;
|
|
7791
|
+
$el.remove();
|
|
7792
|
+
if ($parent) filterEmptyElements($parent);
|
|
7793
|
+
}
|
|
7794
|
+
};
|
|
7795
|
+
|
|
7796
|
+
/**
|
|
7797
|
+
* @param {HTMLElement} $el
|
|
7798
|
+
* @param {Number} lineIndex
|
|
7799
|
+
* @param {Set<HTMLElement>} bin
|
|
7800
|
+
* @returns {Set<HTMLElement>}
|
|
7801
|
+
*/
|
|
7802
|
+
const filterLineElements = ($el, lineIndex, bin) => {
|
|
7803
|
+
const dataLineAttr = $el.getAttribute(dataLine);
|
|
7804
|
+
if (dataLineAttr !== null && +dataLineAttr !== lineIndex || $el.tagName === 'BR') bin.add($el);
|
|
7805
|
+
let i = $el.childElementCount;
|
|
7806
|
+
while (i--) filterLineElements(/** @type {HTMLElement} */($el.children[i]), lineIndex, bin);
|
|
7807
|
+
return bin;
|
|
7808
|
+
};
|
|
7809
|
+
|
|
7810
|
+
/**
|
|
7811
|
+
* @param {'line'|'word'|'char'} type
|
|
7812
|
+
* @param {splitTemplateParams} params
|
|
7813
|
+
* @return {String}
|
|
7814
|
+
*/
|
|
7815
|
+
const generateTemplate = (type, params = {}) => {
|
|
7816
|
+
let template = ``;
|
|
7817
|
+
const classString = isStr(params.class) ? ` class="${params.class}"` : '';
|
|
7818
|
+
const cloneType = setValue(params.clone, false);
|
|
7819
|
+
const wrapType = setValue(params.wrap, false);
|
|
7820
|
+
const overflow = wrapType ? wrapType === true ? 'clip' : wrapType : cloneType ? 'clip' : false;
|
|
7821
|
+
if (wrapType) template += `<span${overflow ? ` style="overflow:${overflow};"` : ''}>`;
|
|
7822
|
+
template += `<span${classString}${cloneType ? ` style="position:relative;"` : ''} data-${type}="{i}">`;
|
|
7823
|
+
if (cloneType) {
|
|
7824
|
+
const left = cloneType === 'left' ? '-100%' : cloneType === 'right' ? '100%' : '0';
|
|
7825
|
+
const top = cloneType === 'top' ? '-100%' : cloneType === 'bottom' ? '100%' : '0';
|
|
7826
|
+
template += `<span>{value}</span>`;
|
|
7827
|
+
template += `<span inert style="position:absolute;top:${top};left:${left};white-space:nowrap;">{value}</span>`;
|
|
7828
|
+
} else {
|
|
7829
|
+
template += `{value}`;
|
|
7830
|
+
}
|
|
7831
|
+
template += `</span>`;
|
|
7832
|
+
if (wrapType) template += `</span>`;
|
|
7833
|
+
return template;
|
|
7834
|
+
};
|
|
7835
|
+
|
|
7836
|
+
/**
|
|
7837
|
+
* @param {String|SplitFunctionValue} htmlTemplate
|
|
7838
|
+
* @param {Array<HTMLElement>} store
|
|
7839
|
+
* @param {Node|HTMLElement} node
|
|
7840
|
+
* @param {DocumentFragment} $parentFragment
|
|
7841
|
+
* @param {'line'|'word'|'char'} type
|
|
7842
|
+
* @param {Boolean} debug
|
|
7843
|
+
* @param {Number} lineIndex
|
|
7844
|
+
* @param {Number} [wordIndex]
|
|
7845
|
+
* @param {Number} [charIndex]
|
|
7846
|
+
* @return {HTMLElement}
|
|
7847
|
+
*/
|
|
7848
|
+
const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, debug, lineIndex, wordIndex, charIndex) => {
|
|
7849
|
+
const isLine = type === lineType;
|
|
7850
|
+
const isChar = type === charType;
|
|
7851
|
+
const className = `_${type}_`;
|
|
7852
|
+
const template = isFnc(htmlTemplate) ? htmlTemplate(node) : htmlTemplate;
|
|
7853
|
+
const displayStyle = isLine ? 'block' : 'inline-block';
|
|
7854
|
+
$splitTemplate.innerHTML = template
|
|
7855
|
+
.replace(valueRgx, `<i class="${className}"></i>`)
|
|
7856
|
+
.replace(indexRgx, `${isChar ? charIndex : isLine ? lineIndex : wordIndex}`);
|
|
7857
|
+
const $content = $splitTemplate.content;
|
|
7858
|
+
const $highestParent = /** @type {HTMLElement} */($content.firstElementChild);
|
|
7859
|
+
const $split = /** @type {HTMLElement} */($content.querySelector(`[data-${type}]`)) || $highestParent;
|
|
7860
|
+
const $replacables = /** @type {NodeListOf<HTMLElement>} */($content.querySelectorAll(`i.${className}`));
|
|
7861
|
+
const replacablesLength = $replacables.length;
|
|
7862
|
+
if (replacablesLength) {
|
|
7863
|
+
$highestParent.style.display = displayStyle;
|
|
7864
|
+
$split.style.display = displayStyle;
|
|
7865
|
+
$split.setAttribute(dataLine, `${lineIndex}`);
|
|
7866
|
+
if (!isLine) {
|
|
7867
|
+
$split.setAttribute('data-word', `${wordIndex}`);
|
|
7868
|
+
if (isChar) $split.setAttribute('data-char', `${charIndex}`);
|
|
7869
|
+
}
|
|
7870
|
+
let i = replacablesLength;
|
|
7871
|
+
while (i--) {
|
|
7872
|
+
const $replace = $replacables[i];
|
|
7873
|
+
const $closestParent = $replace.parentElement;
|
|
7874
|
+
$closestParent.style.display = displayStyle;
|
|
7875
|
+
if (isLine) {
|
|
7876
|
+
$closestParent.innerHTML = /** @type {HTMLElement} */(node).innerHTML;
|
|
7877
|
+
} else {
|
|
7878
|
+
$closestParent.replaceChild(node.cloneNode(true), $replace);
|
|
7879
|
+
}
|
|
7880
|
+
}
|
|
7881
|
+
store.push($split);
|
|
7882
|
+
$parentFragment.appendChild($content);
|
|
7883
|
+
} else {
|
|
7884
|
+
console.warn(`The expression "{value}" is missing from the provided template.`);
|
|
7885
|
+
}
|
|
7886
|
+
if (debug) $highestParent.style.outline = `1px dotted ${debugColors[type]}`;
|
|
7887
|
+
return $highestParent;
|
|
7888
|
+
};
|
|
7889
|
+
|
|
7890
|
+
/**
|
|
7891
|
+
* A class that splits text into words and wraps them in span elements while preserving the original HTML structure.
|
|
7892
|
+
* @class
|
|
7893
|
+
*/
|
|
7894
|
+
class TextSplitter {
|
|
7895
|
+
/**
|
|
7896
|
+
* @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
|
|
7897
|
+
* @param {TextSplitterParams} [parameters]
|
|
7898
|
+
*/
|
|
7899
|
+
constructor(target, parameters = {}) {
|
|
7900
|
+
// Only init segmenters when needed
|
|
7901
|
+
if (!wordSegmenter) wordSegmenter = segmenter ? new segmenter([], { granularity: wordType }) : {
|
|
7902
|
+
segment: (text) => {
|
|
7903
|
+
const segments = [];
|
|
7904
|
+
const words = text.split(whiteSpaceGroupRgx);
|
|
7905
|
+
for (let i = 0, l = words.length; i < l; i++) {
|
|
7906
|
+
const segment = words[i];
|
|
7907
|
+
segments.push({
|
|
7908
|
+
segment,
|
|
7909
|
+
isWordLike: !whiteSpaceRgx.test(segment), // Consider non-whitespace as word-like
|
|
7910
|
+
});
|
|
7911
|
+
}
|
|
7912
|
+
return segments;
|
|
7913
|
+
}
|
|
7914
|
+
};
|
|
7915
|
+
if (!graphemeSegmenter) graphemeSegmenter = segmenter ? new segmenter([], { granularity: 'grapheme' }) : {
|
|
7916
|
+
segment: text => [...text].map(char => ({ segment: char }))
|
|
7917
|
+
};
|
|
7918
|
+
if (!$splitTemplate && isBrowser) $splitTemplate = doc.createElement('template');
|
|
7919
|
+
if (scope.current) scope.current.register(this);
|
|
7920
|
+
const { words, chars, lines, accessible, includeSpaces, debug } = parameters;
|
|
7921
|
+
const $target = /** @type {HTMLElement} */((target = isArr(target) ? target[0] : target) && /** @type {Node} */(target).nodeType ? target : (getNodeList(target) || [])[0]);
|
|
7922
|
+
const lineParams = lines === true ? {} : lines;
|
|
7923
|
+
const wordParams = words === true || isUnd(words) ? {} : words;
|
|
7924
|
+
const charParams = chars === true ? {} : chars;
|
|
7925
|
+
this.debug = setValue(debug, false);
|
|
7926
|
+
this.includeSpaces = setValue(includeSpaces, false);
|
|
7927
|
+
this.accessible = setValue(accessible, true);
|
|
7928
|
+
this.linesOnly = lineParams && (!wordParams && !charParams);
|
|
7929
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7930
|
+
this.lineTemplate = isObj(lineParams) ? generateTemplate(lineType, /** @type {splitTemplateParams} */(lineParams)) : lineParams;
|
|
7931
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7932
|
+
this.wordTemplate = isObj(wordParams) || this.linesOnly ? generateTemplate(wordType, /** @type {splitTemplateParams} */(wordParams)) : wordParams;
|
|
7933
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7934
|
+
this.charTemplate = isObj(charParams) ? generateTemplate(charType, /** @type {splitTemplateParams} */(charParams)) : charParams;
|
|
7935
|
+
this.$target = $target;
|
|
7936
|
+
this.html = $target && $target.innerHTML;
|
|
7937
|
+
this.lines = [];
|
|
7938
|
+
this.words = [];
|
|
7939
|
+
this.chars = [];
|
|
7940
|
+
this.effects = [];
|
|
7941
|
+
this.effectsCleanups = [];
|
|
7942
|
+
this.cache = null;
|
|
7943
|
+
this.ready = false;
|
|
7944
|
+
this.width = 0;
|
|
7945
|
+
this.resizeTimeout = null;
|
|
7946
|
+
const handleSplit = () => this.html && (lineParams || wordParams || charParams) && this.split();
|
|
7947
|
+
// Make sure this is declared before calling handleSplit() in case revert() is called inside an effect callback
|
|
7948
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
7949
|
+
// Use a setTimeout instead of a Timer for better tree shaking
|
|
7950
|
+
clearTimeout(this.resizeTimeout);
|
|
7951
|
+
this.resizeTimeout = setTimeout(() => {
|
|
7952
|
+
const currentWidth = /** @type {HTMLElement} */($target).offsetWidth;
|
|
7953
|
+
if (currentWidth === this.width) return;
|
|
7954
|
+
this.width = currentWidth;
|
|
7955
|
+
handleSplit();
|
|
7956
|
+
}, 150);
|
|
7957
|
+
});
|
|
7958
|
+
// Only declare the font ready promise when splitting by lines and not alreay split
|
|
7959
|
+
if (this.lineTemplate && !this.ready) {
|
|
7960
|
+
doc.fonts.ready.then(handleSplit);
|
|
7961
|
+
} else {
|
|
7962
|
+
handleSplit();
|
|
7963
|
+
}
|
|
7964
|
+
$target ? this.resizeObserver.observe($target) : console.warn('No Text Splitter target found.');
|
|
7965
|
+
}
|
|
7966
|
+
|
|
7967
|
+
/**
|
|
7968
|
+
* @param {(...args: any[]) => Tickable | (() => void)} effect
|
|
7969
|
+
* @return this
|
|
7970
|
+
*/
|
|
7971
|
+
addEffect(effect) {
|
|
7972
|
+
if (!isFnc(effect)) return console.warn('Effect must return a function.');
|
|
7973
|
+
const refreshableEffect = createRefreshable(effect);
|
|
7974
|
+
this.effects.push(refreshableEffect);
|
|
7975
|
+
if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
|
|
7976
|
+
return this;
|
|
7977
|
+
}
|
|
7978
|
+
|
|
7979
|
+
revert() {
|
|
7980
|
+
clearTimeout(this.resizeTimeout);
|
|
7981
|
+
this.lines.length = this.words.length = this.chars.length = 0;
|
|
7982
|
+
this.resizeObserver.disconnect();
|
|
7983
|
+
// Make sure to revert the effects after disconnecting the resizeObserver to avoid triggering it in the process
|
|
7984
|
+
this.effectsCleanups.forEach(cleanup => isFnc(cleanup) ? cleanup(this) : cleanup.revert && cleanup.revert());
|
|
7985
|
+
this.$target.innerHTML = this.html;
|
|
7986
|
+
return this;
|
|
7987
|
+
}
|
|
7988
|
+
|
|
7989
|
+
/**
|
|
7990
|
+
* Recursively processes a node and its children
|
|
7991
|
+
* @param {Node} node
|
|
7992
|
+
*/
|
|
7993
|
+
splitNode(node) {
|
|
7994
|
+
const wordTemplate = this.wordTemplate;
|
|
7995
|
+
const charTemplate = this.charTemplate;
|
|
7996
|
+
const includeSpaces = this.includeSpaces;
|
|
7997
|
+
const debug = this.debug;
|
|
7998
|
+
const nodeType = node.nodeType;
|
|
7999
|
+
if (nodeType === 3) {
|
|
8000
|
+
const nodeText = node.nodeValue;
|
|
8001
|
+
// If the nodeText is only whitespace, leave it as is
|
|
8002
|
+
if (nodeText.trim()) {
|
|
8003
|
+
const tempWords = [];
|
|
8004
|
+
const words = this.words;
|
|
8005
|
+
const chars = this.chars;
|
|
8006
|
+
const wordSegments = wordSegmenter.segment(nodeText);
|
|
8007
|
+
const $wordsFragment = doc.createDocumentFragment();
|
|
8008
|
+
let prevSeg = null;
|
|
8009
|
+
for (const wordSegment of wordSegments) {
|
|
8010
|
+
const segment = wordSegment.segment;
|
|
8011
|
+
const isWordLike = isSegmentWordLike(wordSegment);
|
|
8012
|
+
// Determine if this segment should be a new word, first segment always becomes a new word
|
|
8013
|
+
if (!prevSeg || (isWordLike && (prevSeg && (isSegmentWordLike(prevSeg))))) {
|
|
8014
|
+
tempWords.push(segment);
|
|
8015
|
+
} else {
|
|
8016
|
+
// Only concatenate if both current and previous are non-word-like and don't contain spaces
|
|
8017
|
+
const lastWordIndex = tempWords.length - 1;
|
|
8018
|
+
const lastWord = tempWords[lastWordIndex];
|
|
8019
|
+
if (!lastWord.includes(' ') && !segment.includes(' ')) {
|
|
8020
|
+
tempWords[lastWordIndex] += segment;
|
|
8021
|
+
} else {
|
|
8022
|
+
tempWords.push(segment);
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
prevSeg = wordSegment;
|
|
8026
|
+
}
|
|
8027
|
+
|
|
8028
|
+
for (let i = 0, l = tempWords.length; i < l; i++) {
|
|
8029
|
+
const word = tempWords[i];
|
|
8030
|
+
if (!word.trim()) {
|
|
8031
|
+
// Preserve whitespace only if includeSpaces is false and if the current space is not the first node
|
|
8032
|
+
if (i && includeSpaces) continue;
|
|
8033
|
+
$wordsFragment.appendChild(doc.createTextNode(word));
|
|
8034
|
+
} else {
|
|
8035
|
+
const nextWord = tempWords[i + 1];
|
|
8036
|
+
const hasWordFollowingSpace = includeSpaces && nextWord && !nextWord.trim();
|
|
8037
|
+
const wordToProcess = word;
|
|
8038
|
+
const charSegments = charTemplate ? graphemeSegmenter.segment(wordToProcess) : null;
|
|
8039
|
+
const $charsFragment = charTemplate ? doc.createDocumentFragment() : doc.createTextNode(hasWordFollowingSpace ? word + '\xa0' : word);
|
|
8040
|
+
if (charTemplate) {
|
|
8041
|
+
const charSegmentsArray = [...charSegments];
|
|
8042
|
+
for (let j = 0, jl = charSegmentsArray.length; j < jl; j++) {
|
|
8043
|
+
const charSegment = charSegmentsArray[j];
|
|
8044
|
+
const isLastChar = j === jl - 1;
|
|
8045
|
+
// If this is the last character and includeSpaces is true with a following space, append the space
|
|
8046
|
+
const charText = isLastChar && hasWordFollowingSpace ? charSegment.segment + '\xa0' : charSegment.segment;
|
|
8047
|
+
const $charNode = doc.createTextNode(charText);
|
|
8048
|
+
processHTMLTemplate(charTemplate, chars, $charNode, /** @type {DocumentFragment} */($charsFragment), charType, debug, -1, words.length, chars.length);
|
|
8049
|
+
}
|
|
8050
|
+
}
|
|
8051
|
+
if (wordTemplate) {
|
|
8052
|
+
processHTMLTemplate(wordTemplate, words, $charsFragment, $wordsFragment, wordType, debug, -1, words.length, chars.length);
|
|
8053
|
+
// Chars elements must be re-parsed in the split() method if both words and chars are parsed
|
|
8054
|
+
} else if (charTemplate) {
|
|
8055
|
+
$wordsFragment.appendChild($charsFragment);
|
|
8056
|
+
} else {
|
|
8057
|
+
$wordsFragment.appendChild(doc.createTextNode(word));
|
|
8058
|
+
}
|
|
8059
|
+
// Skip the next iteration if we included a space
|
|
8060
|
+
if (hasWordFollowingSpace) i++;
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
8063
|
+
node.parentNode.replaceChild($wordsFragment, node);
|
|
8064
|
+
}
|
|
8065
|
+
} else if (nodeType === 1) {
|
|
8066
|
+
// Converting to an array is necessary to work around childNodes pottential mutation
|
|
8067
|
+
const childNodes = /** @type {Array<Node>} */([.../** @type {*} */(node.childNodes)]);
|
|
8068
|
+
for (let i = 0, l = childNodes.length; i < l; i++) this.splitNode(childNodes[i]);
|
|
8069
|
+
}
|
|
8070
|
+
}
|
|
8071
|
+
|
|
8072
|
+
/**
|
|
8073
|
+
* @param {Boolean} clearCache
|
|
8074
|
+
* @return {this}
|
|
8075
|
+
*/
|
|
8076
|
+
split(clearCache = false) {
|
|
8077
|
+
const $el = this.$target;
|
|
8078
|
+
const isCached = !!this.cache && !clearCache;
|
|
8079
|
+
const lineTemplate = this.lineTemplate;
|
|
8080
|
+
const wordTemplate = this.wordTemplate;
|
|
8081
|
+
const charTemplate = this.charTemplate;
|
|
8082
|
+
const fontsReady = doc.fonts.status !== 'loading';
|
|
8083
|
+
const canSplitLines = lineTemplate && fontsReady;
|
|
8084
|
+
this.ready = !lineTemplate || fontsReady;
|
|
8085
|
+
if (!isCached) {
|
|
8086
|
+
if (clearCache) {
|
|
8087
|
+
$el.innerHTML = this.html;
|
|
8088
|
+
this.words.length = this.chars.length = 0;
|
|
8089
|
+
}
|
|
8090
|
+
this.splitNode($el);
|
|
8091
|
+
this.cache = $el.innerHTML;
|
|
8092
|
+
}
|
|
8093
|
+
// Always reset the html when splitting by lines
|
|
8094
|
+
if (canSplitLines) {
|
|
8095
|
+
// No need to revert effects animations here since it's already taken care by the refreshable
|
|
8096
|
+
this.effectsCleanups.forEach(cleanup => isFnc(cleanup) && cleanup(this));
|
|
8097
|
+
if (isCached) $el.innerHTML = this.cache;
|
|
8098
|
+
this.lines.length = 0;
|
|
8099
|
+
if (wordTemplate) this.words = getAllTopLevelElements($el, wordType);
|
|
8100
|
+
}
|
|
8101
|
+
// Always reparse characters after a line reset or if both words and chars are activated
|
|
8102
|
+
if (charTemplate && (canSplitLines || wordTemplate)) {
|
|
8103
|
+
this.chars = getAllTopLevelElements($el, charType);
|
|
8104
|
+
}
|
|
8105
|
+
// Words are used when lines only and prioritized over chars
|
|
8106
|
+
const elementsArray = this.words.length ? this.words : this.chars;
|
|
8107
|
+
let y, linesCount = 0;
|
|
8108
|
+
for (let i = 0, l = elementsArray.length; i < l; i++) {
|
|
8109
|
+
const $el = elementsArray[i];
|
|
8110
|
+
const { top, height } = $el.getBoundingClientRect();
|
|
8111
|
+
if (y && top - y > height * .5) linesCount++;
|
|
8112
|
+
$el.setAttribute(dataLine, `${linesCount}`);
|
|
8113
|
+
const nested = $el.querySelectorAll(`[${dataLine}]`);
|
|
8114
|
+
let c = nested.length;
|
|
8115
|
+
while (c--) nested[c].setAttribute(dataLine, `${linesCount}`);
|
|
8116
|
+
y = top;
|
|
8117
|
+
}
|
|
8118
|
+
if (canSplitLines) {
|
|
8119
|
+
const linesFragment = doc.createDocumentFragment();
|
|
8120
|
+
const parents = new Set();
|
|
8121
|
+
const clones = [];
|
|
8122
|
+
for (let lineIndex = 0; lineIndex < linesCount + 1; lineIndex++) {
|
|
8123
|
+
const $clone = /** @type {HTMLElement} */($el.cloneNode(true));
|
|
8124
|
+
filterLineElements($clone, lineIndex, new Set()).forEach($el => {
|
|
8125
|
+
const $parent = $el.parentElement;
|
|
8126
|
+
if ($parent) parents.add($parent);
|
|
8127
|
+
$el.remove();
|
|
8128
|
+
});
|
|
8129
|
+
clones.push($clone);
|
|
8130
|
+
}
|
|
8131
|
+
parents.forEach(filterEmptyElements);
|
|
8132
|
+
for (let cloneIndex = 0, clonesLength = clones.length; cloneIndex < clonesLength; cloneIndex++) {
|
|
8133
|
+
processHTMLTemplate(lineTemplate, this.lines, clones[cloneIndex], linesFragment, lineType, this.debug, cloneIndex);
|
|
8134
|
+
}
|
|
8135
|
+
$el.innerHTML = '';
|
|
8136
|
+
$el.appendChild(linesFragment);
|
|
8137
|
+
if (wordTemplate) this.words = getAllTopLevelElements($el, wordType);
|
|
8138
|
+
if (charTemplate) this.chars = getAllTopLevelElements($el, charType);
|
|
8139
|
+
}
|
|
8140
|
+
// Remove the word wrappers and clear the words array if lines split only
|
|
8141
|
+
if (this.linesOnly) {
|
|
8142
|
+
const words = this.words;
|
|
8143
|
+
let w = words.length;
|
|
8144
|
+
while (w--) {
|
|
8145
|
+
const $word = words[w];
|
|
8146
|
+
$word.replaceWith($word.textContent);
|
|
8147
|
+
}
|
|
8148
|
+
words.length = 0;
|
|
8149
|
+
}
|
|
8150
|
+
if (canSplitLines || clearCache) {
|
|
8151
|
+
this.effects.forEach((effect, i) => this.effectsCleanups[i] = effect(this));
|
|
8152
|
+
}
|
|
8153
|
+
if (this.accessible && (canSplitLines || !isCached)) {
|
|
8154
|
+
const $accessible = doc.createElement('span');
|
|
8155
|
+
// Make the accessible element visually-hidden (https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html)
|
|
8156
|
+
$accessible.style.cssText = `position:absolute;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);width:1px;height:1px;white-space:nowrap;`;
|
|
8157
|
+
// $accessible.setAttribute('tabindex', '-1');
|
|
8158
|
+
$accessible.innerHTML = this.html;
|
|
8159
|
+
$el.insertBefore($accessible, $el.firstChild);
|
|
8160
|
+
this.lines.forEach(setAriaHidden);
|
|
8161
|
+
this.words.forEach(setAriaHidden);
|
|
8162
|
+
this.chars.forEach(setAriaHidden);
|
|
8163
|
+
}
|
|
8164
|
+
this.width = /** @type {HTMLElement} */($el).offsetWidth;
|
|
8165
|
+
return this;
|
|
8166
|
+
}
|
|
8167
|
+
|
|
8168
|
+
refresh() {
|
|
8169
|
+
this.split(true);
|
|
8170
|
+
}
|
|
8171
|
+
}
|
|
8172
|
+
|
|
8173
|
+
/**
|
|
8174
|
+
* @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
|
|
8175
|
+
* @param {TextSplitterParams} [parameters]
|
|
8176
|
+
* @return {TextSplitter}
|
|
7529
8177
|
*/
|
|
8178
|
+
const split = (target, parameters) => new TextSplitter(target, parameters);
|
|
8179
|
+
|
|
8180
|
+
const text = {
|
|
8181
|
+
split,
|
|
8182
|
+
};
|
|
8183
|
+
|
|
8184
|
+
|
|
8185
|
+
|
|
7530
8186
|
|
|
7531
8187
|
/**
|
|
7532
8188
|
* @param {Number|String|[Number|String,Number|String]} val
|
|
7533
|
-
* @param {
|
|
8189
|
+
* @param {StaggerParams} params
|
|
7534
8190
|
* @return {StaggerFunction}
|
|
7535
8191
|
*/
|
|
7536
8192
|
const stagger = (val, params = {}) => {
|
|
@@ -7544,20 +8200,27 @@ const stagger = (val, params = {}) => {
|
|
|
7544
8200
|
const staggerEase = hasSpring ? /** @type {Spring} */(ease).ease : hasEasing ? parseEasings(ease) : null;
|
|
7545
8201
|
const grid = params.grid;
|
|
7546
8202
|
const axis = params.axis;
|
|
8203
|
+
const customTotal = params.total;
|
|
7547
8204
|
const fromFirst = isUnd(from) || from === 0 || from === 'first';
|
|
7548
8205
|
const fromCenter = from === 'center';
|
|
7549
8206
|
const fromLast = from === 'last';
|
|
8207
|
+
const fromRandom = from === 'random';
|
|
7550
8208
|
const isRange = isArr(val);
|
|
8209
|
+
const useProp = params.use;
|
|
7551
8210
|
const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
|
|
7552
8211
|
const val2 = isRange ? parseNumber(val[1]) : 0;
|
|
7553
8212
|
const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
|
|
7554
8213
|
const start = params.start || 0 + (isRange ? val1 : 0);
|
|
7555
8214
|
let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
|
|
7556
|
-
return (
|
|
7557
|
-
|
|
7558
|
-
|
|
8215
|
+
return (target, i, t, tl) => {
|
|
8216
|
+
const [ registeredTarget ] = registerTargets(target);
|
|
8217
|
+
const total = isUnd(customTotal) ? t : customTotal;
|
|
8218
|
+
const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
|
|
8219
|
+
const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
|
|
8220
|
+
if (fromCenter) fromIndex = (total - 1) / 2;
|
|
8221
|
+
if (fromLast) fromIndex = total - 1;
|
|
7559
8222
|
if (!values.length) {
|
|
7560
|
-
for (let index = 0; index <
|
|
8223
|
+
for (let index = 0; index < total; index++) {
|
|
7561
8224
|
if (!grid) {
|
|
7562
8225
|
values.push(abs(fromIndex - index));
|
|
7563
8226
|
} else {
|
|
@@ -7576,15 +8239,16 @@ const stagger = (val, params = {}) => {
|
|
|
7576
8239
|
}
|
|
7577
8240
|
if (staggerEase) values = values.map(val => staggerEase(val / maxValue) * maxValue);
|
|
7578
8241
|
if (reversed) values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
|
|
8242
|
+
if (fromRandom) values = shuffle(values);
|
|
7579
8243
|
}
|
|
7580
8244
|
const spacing = isRange ? (val2 - val1) / maxValue : val1;
|
|
7581
8245
|
const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */(start);
|
|
7582
8246
|
/** @type {String|Number} */
|
|
7583
|
-
let output = offset + ((spacing * round(values[
|
|
8247
|
+
let output = offset + ((spacing * round(values[staggerIndex], 2)) || 0);
|
|
7584
8248
|
if (params.modifier) output = params.modifier(output);
|
|
7585
8249
|
if (unitMatch) output = `${output}${unitMatch[2]}`;
|
|
7586
8250
|
return output;
|
|
7587
8251
|
}
|
|
7588
8252
|
};
|
|
7589
8253
|
|
|
7590
|
-
export { Animatable, Draggable, JSAnimation, Scope, ScrollObserver, Spring, Timeline, Timer, WAAPIAnimation, animate, createAnimatable, createDraggable, createScope, createSpring, createTimeline, createTimer, eases, engine, onScroll, scrollContainers, stagger, svg, utils, waapi };
|
|
8254
|
+
export { Animatable, Draggable, JSAnimation, Scope, ScrollObserver, Spring, TextSplitter, Timeline, Timer, WAAPIAnimation, animate, createAnimatable, createDraggable, createScope, createSpring, createTimeline, createTimer, eases, engine, onScroll, scrollContainers, stagger, svg, text, utils, waapi };
|