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/types/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
|
|
@@ -56,10 +56,13 @@ const K = 1e3;
|
|
|
56
56
|
const maxFps = 120;
|
|
57
57
|
// Strings
|
|
58
58
|
const emptyString = '';
|
|
59
|
-
const shortTransforms =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
const shortTransforms = /*#__PURE__*/ (() => {
|
|
60
|
+
const map = new Map();
|
|
61
|
+
map.set('x', 'translateX');
|
|
62
|
+
map.set('y', 'translateY');
|
|
63
|
+
map.set('z', 'translateZ');
|
|
64
|
+
return map;
|
|
65
|
+
})();
|
|
63
66
|
const validTransforms = [
|
|
64
67
|
'translateX',
|
|
65
68
|
'translateY',
|
|
@@ -79,7 +82,7 @@ const validTransforms = [
|
|
|
79
82
|
'matrix',
|
|
80
83
|
'matrix3d',
|
|
81
84
|
];
|
|
82
|
-
const transformsFragmentStrings = validTransforms.reduce((a, v) => ({ ...a, [v]: v + '(' }), {});
|
|
85
|
+
const transformsFragmentStrings = /*#__PURE__*/ validTransforms.reduce((a, v) => ({ ...a, [v]: v + '(' }), {});
|
|
83
86
|
// Functions
|
|
84
87
|
/** @return {void} */
|
|
85
88
|
const noop = () => { };
|
|
@@ -123,13 +126,15 @@ const defaults = {
|
|
|
123
126
|
onComplete: noop,
|
|
124
127
|
onRender: noop,
|
|
125
128
|
};
|
|
129
|
+
const scope = {
|
|
130
|
+
/** @type {Scope} */
|
|
131
|
+
current: null,
|
|
132
|
+
/** @type {Document|DOMTarget} */
|
|
133
|
+
root: doc,
|
|
134
|
+
};
|
|
126
135
|
const globals = {
|
|
127
136
|
/** @type {DefaultsParams} */
|
|
128
137
|
defaults,
|
|
129
|
-
/** @type {Document|DOMTarget} */
|
|
130
|
-
root: doc,
|
|
131
|
-
/** @type {Scope} */
|
|
132
|
-
scope: null,
|
|
133
138
|
/** @type {Number} */
|
|
134
139
|
precision: 4,
|
|
135
140
|
/** @type {Number} */
|
|
@@ -137,7 +142,7 @@ const globals = {
|
|
|
137
142
|
/** @type {Number} */
|
|
138
143
|
tickThreshold: 200,
|
|
139
144
|
};
|
|
140
|
-
const globalVersions = { version: '4.0
|
|
145
|
+
const globalVersions = { version: '4.1.0', engine: null };
|
|
141
146
|
if (isBrowser) {
|
|
142
147
|
if (!win.AnimeJS)
|
|
143
148
|
win.AnimeJS = [];
|
|
@@ -244,6 +249,28 @@ const snap = (v, increment) => isArr(increment) ? increment.reduce((closest, cv)
|
|
|
244
249
|
* @return {Number}
|
|
245
250
|
*/
|
|
246
251
|
const interpolate = (start, end, progress) => start + (end - start) * progress;
|
|
252
|
+
/**
|
|
253
|
+
* @param {Number} min
|
|
254
|
+
* @param {Number} max
|
|
255
|
+
* @param {Number} [decimalLength]
|
|
256
|
+
* @return {Number}
|
|
257
|
+
*/
|
|
258
|
+
const random = (min, max, decimalLength) => { const m = 10 ** (decimalLength || 0); return floor((Math.random() * (max - min + (1 / m)) + min) * m) / m; };
|
|
259
|
+
/**
|
|
260
|
+
* Adapted from https://bost.ocks.org/mike/shuffle/
|
|
261
|
+
* @param {Array} items
|
|
262
|
+
* @return {Array}
|
|
263
|
+
*/
|
|
264
|
+
const shuffle = items => {
|
|
265
|
+
let m = items.length, t, i;
|
|
266
|
+
while (m) {
|
|
267
|
+
i = random(0, --m);
|
|
268
|
+
t = items[m];
|
|
269
|
+
items[m] = items[i];
|
|
270
|
+
items[i] = t;
|
|
271
|
+
}
|
|
272
|
+
return items;
|
|
273
|
+
};
|
|
247
274
|
/**
|
|
248
275
|
* @param {Number} v
|
|
249
276
|
* @return {Number}
|
|
@@ -253,7 +280,7 @@ const clampInfinity = v => v === Infinity ? maxValue : v === -Infinity ? -1e12 :
|
|
|
253
280
|
* @param {Number} v
|
|
254
281
|
* @return {Number}
|
|
255
282
|
*/
|
|
256
|
-
const
|
|
283
|
+
const normalizeTime = v => v <= minValue ? minValue : clampInfinity(round(v, 11));
|
|
257
284
|
// Arrays
|
|
258
285
|
/**
|
|
259
286
|
* @template T
|
|
@@ -332,6 +359,32 @@ const addChild = (parent, child, sortMethod, prevProp = '_prev', nextProp = '_ne
|
|
|
332
359
|
child[prevProp] = prev;
|
|
333
360
|
child[nextProp] = next;
|
|
334
361
|
};
|
|
362
|
+
/**
|
|
363
|
+
* @param {(...args: any[]) => Tickable | ((...args: any[]) => void)} constructor
|
|
364
|
+
* @return {(...args: any[]) => Tickable | ((...args: any[]) => void)}
|
|
365
|
+
*/
|
|
366
|
+
const createRefreshable = constructor => {
|
|
367
|
+
/** @type {Tickable} */
|
|
368
|
+
let tracked;
|
|
369
|
+
return (...args) => {
|
|
370
|
+
let currentIteration, currentIterationProgress, reversed, alternate;
|
|
371
|
+
if (tracked) {
|
|
372
|
+
currentIteration = tracked.currentIteration;
|
|
373
|
+
currentIterationProgress = tracked.iterationProgress;
|
|
374
|
+
reversed = tracked.reversed;
|
|
375
|
+
alternate = tracked._alternate;
|
|
376
|
+
tracked.revert();
|
|
377
|
+
}
|
|
378
|
+
const cleanup = constructor(...args);
|
|
379
|
+
if (cleanup && !isFnc(cleanup) && cleanup.revert)
|
|
380
|
+
tracked = cleanup;
|
|
381
|
+
if (!isUnd(currentIterationProgress)) {
|
|
382
|
+
/** @type {Tickable} */ (tracked).currentIteration = currentIteration;
|
|
383
|
+
/** @type {Tickable} */ (tracked).iterationProgress = (alternate ? !(currentIteration % 2) ? reversed : !reversed : reversed) ? 1 - currentIterationProgress : currentIterationProgress;
|
|
384
|
+
}
|
|
385
|
+
return cleanup || noop;
|
|
386
|
+
};
|
|
387
|
+
};
|
|
335
388
|
|
|
336
389
|
/*
|
|
337
390
|
* Base class to control framerate and playback rate.
|
|
@@ -801,8 +854,8 @@ const addAdditiveAnimation = lookups => {
|
|
|
801
854
|
return animation;
|
|
802
855
|
};
|
|
803
856
|
|
|
804
|
-
const engineTickMethod = isBrowser ? requestAnimationFrame : setImmediate;
|
|
805
|
-
const engineCancelMethod = isBrowser ? cancelAnimationFrame : clearImmediate;
|
|
857
|
+
const engineTickMethod = /*#__PURE__*/ (() => isBrowser ? requestAnimationFrame : setImmediate)();
|
|
858
|
+
const engineCancelMethod = /*#__PURE__*/ (() => isBrowser ? cancelAnimationFrame : clearImmediate)();
|
|
806
859
|
class Engine extends Clock {
|
|
807
860
|
/** @param {Number} [initTime] */
|
|
808
861
|
constructor(initTime) {
|
|
@@ -958,7 +1011,7 @@ const parseInlineTransforms = (target, propName, animationInlineStyles) => {
|
|
|
958
1011
|
* @return {NodeList|HTMLCollection}
|
|
959
1012
|
*/
|
|
960
1013
|
function getNodeList(v) {
|
|
961
|
-
const n = isStr(v) ?
|
|
1014
|
+
const n = isStr(v) ? scope.root.querySelectorAll(v) : v;
|
|
962
1015
|
if (n instanceof NodeList || n instanceof HTMLCollection)
|
|
963
1016
|
return n;
|
|
964
1017
|
}
|
|
@@ -1112,14 +1165,34 @@ const morphTo = (path2, precision = .33) => ($path1) => {
|
|
|
1112
1165
|
return [v1, v2];
|
|
1113
1166
|
};
|
|
1114
1167
|
/**
|
|
1115
|
-
* @param {SVGGeometryElement} $el
|
|
1116
|
-
* @
|
|
1117
|
-
* @param {Number} end
|
|
1118
|
-
* @return {Proxy}
|
|
1168
|
+
* @param {SVGGeometryElement} [$el]
|
|
1169
|
+
* @return {Number}
|
|
1119
1170
|
*/
|
|
1120
|
-
|
|
1121
|
-
|
|
1171
|
+
const getScaleFactor = $el => {
|
|
1172
|
+
let scaleFactor = 1;
|
|
1173
|
+
if ($el && $el.getCTM) {
|
|
1174
|
+
const ctm = $el.getCTM();
|
|
1175
|
+
if (ctm) {
|
|
1176
|
+
const scaleX = sqrt(ctm.a * ctm.a + ctm.b * ctm.b);
|
|
1177
|
+
const scaleY = sqrt(ctm.c * ctm.c + ctm.d * ctm.d);
|
|
1178
|
+
scaleFactor = (scaleX + scaleY) / 2;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return scaleFactor;
|
|
1182
|
+
};
|
|
1183
|
+
/**
|
|
1184
|
+
* Creates a proxy that wraps an SVGGeometryElement and adds drawing functionality.
|
|
1185
|
+
* @param {SVGGeometryElement} $el - The SVG element to transform into a drawable
|
|
1186
|
+
* @param {number} start - Starting position (0-1)
|
|
1187
|
+
* @param {number} end - Ending position (0-1)
|
|
1188
|
+
* @return {DrawableSVGGeometry} - Returns a proxy that preserves the original element's type with additional 'draw' attribute functionality
|
|
1189
|
+
*/
|
|
1190
|
+
const createDrawableProxy = ($el, start, end) => {
|
|
1122
1191
|
const pathLength = K;
|
|
1192
|
+
const computedStyles = getComputedStyle($el);
|
|
1193
|
+
const strokeLineCap = computedStyles.strokeLinecap;
|
|
1194
|
+
// @ts-ignore
|
|
1195
|
+
const $scalled = computedStyles.vectorEffect === 'non-scaling-stroke' ? $el : null;
|
|
1123
1196
|
let currentCap = strokeLineCap;
|
|
1124
1197
|
const proxy = new Proxy($el, {
|
|
1125
1198
|
get(target, property) {
|
|
@@ -1127,7 +1200,6 @@ function createDrawableProxy($el, start, end) {
|
|
|
1127
1200
|
if (property === proxyTargetSymbol)
|
|
1128
1201
|
return target;
|
|
1129
1202
|
if (property === 'setAttribute') {
|
|
1130
|
-
/** @param {any[]} args */
|
|
1131
1203
|
return (...args) => {
|
|
1132
1204
|
if (args[0] === 'draw') {
|
|
1133
1205
|
const value = args[1];
|
|
@@ -1138,15 +1210,15 @@ function createDrawableProxy($el, start, end) {
|
|
|
1138
1210
|
// const spaceIndex = value.indexOf(' ');
|
|
1139
1211
|
// const v1 = round(+value.slice(0, spaceIndex), precision);
|
|
1140
1212
|
// const v2 = round(+value.slice(spaceIndex + 1), precision);
|
|
1141
|
-
const
|
|
1142
|
-
const
|
|
1143
|
-
|
|
1144
|
-
const d2 = (pathLength
|
|
1145
|
-
|
|
1213
|
+
const scaleFactor = getScaleFactor($scalled);
|
|
1214
|
+
const os = v1 * -1e3 * scaleFactor;
|
|
1215
|
+
const d1 = (v2 * pathLength * scaleFactor) + os;
|
|
1216
|
+
const d2 = (pathLength * scaleFactor +
|
|
1217
|
+
((v1 === 0 && v2 === 1) || (v1 === 1 && v2 === 0) ? 0 : 10 * scaleFactor) - d1);
|
|
1146
1218
|
if (strokeLineCap !== 'butt') {
|
|
1147
1219
|
const newCap = v1 === v2 ? 'butt' : strokeLineCap;
|
|
1148
1220
|
if (currentCap !== newCap) {
|
|
1149
|
-
target.
|
|
1221
|
+
target.style.strokeLinecap = `${newCap}`;
|
|
1150
1222
|
currentCap = newCap;
|
|
1151
1223
|
}
|
|
1152
1224
|
}
|
|
@@ -1157,7 +1229,6 @@ function createDrawableProxy($el, start, end) {
|
|
|
1157
1229
|
};
|
|
1158
1230
|
}
|
|
1159
1231
|
if (isFnc(value)) {
|
|
1160
|
-
/** @param {any[]} args */
|
|
1161
1232
|
return (...args) => Reflect.apply(value, target, args);
|
|
1162
1233
|
}
|
|
1163
1234
|
else {
|
|
@@ -1169,18 +1240,19 @@ function createDrawableProxy($el, start, end) {
|
|
|
1169
1240
|
$el.setAttribute('pathLength', `${pathLength}`);
|
|
1170
1241
|
proxy.setAttribute('draw', `${start} ${end}`);
|
|
1171
1242
|
}
|
|
1172
|
-
return /** @type {
|
|
1173
|
-
}
|
|
1243
|
+
return /** @type {DrawableSVGGeometry} */ (proxy);
|
|
1244
|
+
};
|
|
1174
1245
|
/**
|
|
1175
|
-
*
|
|
1176
|
-
* @param {
|
|
1177
|
-
* @param {
|
|
1178
|
-
* @
|
|
1246
|
+
* Creates drawable proxies for multiple SVG elements.
|
|
1247
|
+
* @param {TargetsParam} selector - CSS selector, SVG element, or array of elements and selectors
|
|
1248
|
+
* @param {number} [start=0] - Starting position (0-1)
|
|
1249
|
+
* @param {number} [end=0] - Ending position (0-1)
|
|
1250
|
+
* @return {Array<DrawableSVGGeometry>} - Array of proxied elements with drawing functionality
|
|
1179
1251
|
*/
|
|
1180
1252
|
const createDrawable = (selector, start = 0, end = 0) => {
|
|
1181
|
-
const els =
|
|
1182
|
-
els.
|
|
1183
|
-
|
|
1253
|
+
const els = parseTargets(selector);
|
|
1254
|
+
return els.map($el => createDrawableProxy(
|
|
1255
|
+
/** @type {SVGGeometryElement} */ ($el), start, end));
|
|
1184
1256
|
};
|
|
1185
1257
|
// Motion path animation
|
|
1186
1258
|
/**
|
|
@@ -1784,8 +1856,8 @@ class Timer extends Clock {
|
|
|
1784
1856
|
constructor(parameters = {}, parent = null, parentPosition = 0) {
|
|
1785
1857
|
super(0);
|
|
1786
1858
|
const { id, delay, duration, reversed, alternate, loop, loopDelay, autoplay, frameRate, playbackRate, onComplete, onLoop, onPause, onBegin, onBeforeUpdate, onUpdate, } = parameters;
|
|
1787
|
-
if (
|
|
1788
|
-
|
|
1859
|
+
if (scope.current)
|
|
1860
|
+
scope.current.register(this);
|
|
1789
1861
|
const timerInitTime = parent ? 0 : engine._elapsedTime;
|
|
1790
1862
|
const timerDefaults = parent ? parent.defaults : globals.defaults;
|
|
1791
1863
|
const timerDelay = /** @type {Number} */ (isFnc(delay) || isUnd(delay) ? timerDefaults.delay : +delay);
|
|
@@ -1905,14 +1977,14 @@ class Timer extends Clock {
|
|
|
1905
1977
|
this.currentTime = (this.iterationDuration * this._currentIteration) + time;
|
|
1906
1978
|
}
|
|
1907
1979
|
get progress() {
|
|
1908
|
-
return clamp(round(this._currentTime / this.duration,
|
|
1980
|
+
return clamp(round(this._currentTime / this.duration, 10), 0, 1);
|
|
1909
1981
|
}
|
|
1910
1982
|
/** @param {Number} progress */
|
|
1911
1983
|
set progress(progress) {
|
|
1912
1984
|
this.currentTime = this.duration * progress;
|
|
1913
1985
|
}
|
|
1914
1986
|
get iterationProgress() {
|
|
1915
|
-
return clamp(round(this._iterationTime / this.iterationDuration,
|
|
1987
|
+
return clamp(round(this._iterationTime / this.iterationDuration, 10), 0, 1);
|
|
1916
1988
|
}
|
|
1917
1989
|
/** @param {Number} progress */
|
|
1918
1990
|
set iterationProgress(progress) {
|
|
@@ -2093,12 +2165,13 @@ class Timer extends Clock {
|
|
|
2093
2165
|
*/
|
|
2094
2166
|
stretch(newDuration) {
|
|
2095
2167
|
const currentDuration = this.duration;
|
|
2096
|
-
|
|
2168
|
+
const normlizedDuration = normalizeTime(newDuration);
|
|
2169
|
+
if (currentDuration === normlizedDuration)
|
|
2097
2170
|
return this;
|
|
2098
2171
|
const timeScale = newDuration / currentDuration;
|
|
2099
2172
|
const isSetter = newDuration <= minValue;
|
|
2100
|
-
this.duration = isSetter ? minValue :
|
|
2101
|
-
this.iterationDuration = isSetter ? minValue :
|
|
2173
|
+
this.duration = isSetter ? minValue : normlizedDuration;
|
|
2174
|
+
this.iterationDuration = isSetter ? minValue : normalizeTime(this.iterationDuration * timeScale);
|
|
2102
2175
|
this._offset *= timeScale;
|
|
2103
2176
|
this._delay *= timeScale;
|
|
2104
2177
|
this._loopDelay *= timeScale;
|
|
@@ -3051,14 +3124,14 @@ class JSAnimation extends Timer {
|
|
|
3051
3124
|
*/
|
|
3052
3125
|
stretch(newDuration) {
|
|
3053
3126
|
const currentDuration = this.duration;
|
|
3054
|
-
if (currentDuration ===
|
|
3127
|
+
if (currentDuration === normalizeTime(newDuration))
|
|
3055
3128
|
return this;
|
|
3056
3129
|
const timeScale = newDuration / currentDuration;
|
|
3057
3130
|
// NOTE: Find a better way to handle the stretch of an animation after stretch = 0
|
|
3058
3131
|
forEachChildren(this, (/** @type {Tween} */ tween) => {
|
|
3059
3132
|
// Rounding is necessary here to minimize floating point errors
|
|
3060
|
-
tween._updateDuration =
|
|
3061
|
-
tween._changeDuration =
|
|
3133
|
+
tween._updateDuration = normalizeTime(tween._updateDuration * timeScale);
|
|
3134
|
+
tween._changeDuration = normalizeTime(tween._changeDuration * timeScale);
|
|
3062
3135
|
tween._currentTime *= timeScale;
|
|
3063
3136
|
tween._startTime *= timeScale;
|
|
3064
3137
|
tween._absoluteStartTime *= timeScale;
|
|
@@ -3070,12 +3143,13 @@ class JSAnimation extends Timer {
|
|
|
3070
3143
|
*/
|
|
3071
3144
|
refresh() {
|
|
3072
3145
|
forEachChildren(this, (/** @type {Tween} */ tween) => {
|
|
3073
|
-
const
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3146
|
+
const tweenFunc = tween._func;
|
|
3147
|
+
if (tweenFunc) {
|
|
3148
|
+
const ogValue = getOriginalAnimatableValue(tween.target, tween.property, tween._tweenType);
|
|
3149
|
+
decomposeRawValue(ogValue, decomposedOriginalValue);
|
|
3150
|
+
decomposeRawValue(tweenFunc(), toTargetObject);
|
|
3151
|
+
tween._fromNumbers = cloneArray(decomposedOriginalValue.d);
|
|
3152
|
+
tween._fromNumber = decomposedOriginalValue.n;
|
|
3079
3153
|
tween._toNumbers = cloneArray(toTargetObject.d);
|
|
3080
3154
|
tween._strings = cloneArray(toTargetObject.s);
|
|
3081
3155
|
tween._toNumber = toTargetObject.n;
|
|
@@ -3154,6 +3228,7 @@ const parseWAAPIEasing = (ease) => {
|
|
|
3154
3228
|
if (isFnc(parsed))
|
|
3155
3229
|
parsedEase = parsed === none ? 'linear' : easingToLinear(parsed);
|
|
3156
3230
|
}
|
|
3231
|
+
WAAPIEasesLookups[ease] = parsedEase;
|
|
3157
3232
|
}
|
|
3158
3233
|
else if (isFnc(ease)) {
|
|
3159
3234
|
const easing = easingToLinear(ease);
|
|
@@ -3163,7 +3238,7 @@ const parseWAAPIEasing = (ease) => {
|
|
|
3163
3238
|
else if ( /** @type {Spring} */(ease).ease) {
|
|
3164
3239
|
parsedEase = easingToLinear(/** @type {Spring} */ (ease).ease);
|
|
3165
3240
|
}
|
|
3166
|
-
return
|
|
3241
|
+
return parsedEase;
|
|
3167
3242
|
};
|
|
3168
3243
|
/**
|
|
3169
3244
|
* @typedef {String|Number|Array<String>|Array<Number>} WAAPITweenValue
|
|
@@ -3222,26 +3297,8 @@ const commonDefaultPXProperties = [
|
|
|
3222
3297
|
'borderRadius',
|
|
3223
3298
|
...transformsShorthands
|
|
3224
3299
|
];
|
|
3225
|
-
const validIndividualTransforms = [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))];
|
|
3226
|
-
|
|
3227
|
-
let transformsPropertiesRegistered = isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'));
|
|
3228
|
-
const registerTransformsProperties = () => {
|
|
3229
|
-
validTransforms.forEach(t => {
|
|
3230
|
-
const isSkew = stringStartsWith(t, 'skew');
|
|
3231
|
-
const isScale = stringStartsWith(t, 'scale');
|
|
3232
|
-
const isRotate = stringStartsWith(t, 'rotate');
|
|
3233
|
-
const isTranslate = stringStartsWith(t, 'translate');
|
|
3234
|
-
const isAngle = isRotate || isSkew;
|
|
3235
|
-
const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
|
|
3236
|
-
CSS.registerProperty({
|
|
3237
|
-
name: '--' + t,
|
|
3238
|
-
syntax,
|
|
3239
|
-
inherits: false,
|
|
3240
|
-
initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
|
|
3241
|
-
});
|
|
3242
|
-
});
|
|
3243
|
-
transformsPropertiesRegistered = true;
|
|
3244
|
-
};
|
|
3300
|
+
const validIndividualTransforms = /*#__PURE__*/ (() => [...transformsShorthands, ...validTransforms.filter(t => ['X', 'Y', 'Z'].some(axis => t.endsWith(axis)))])();
|
|
3301
|
+
let transformsPropertiesRegistered = null;
|
|
3245
3302
|
const WAAPIAnimationsLookups = {
|
|
3246
3303
|
_head: null,
|
|
3247
3304
|
_tail: null,
|
|
@@ -3354,10 +3411,34 @@ class WAAPIAnimation {
|
|
|
3354
3411
|
* @param {WAAPIAnimationParams} params
|
|
3355
3412
|
*/
|
|
3356
3413
|
constructor(targets, params) {
|
|
3357
|
-
if (
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3414
|
+
if (scope.current)
|
|
3415
|
+
scope.current.register(this);
|
|
3416
|
+
// Skip the registration and fallback to no animation in case CSS.registerProperty is not supported
|
|
3417
|
+
if (isNil(transformsPropertiesRegistered)) {
|
|
3418
|
+
if (isBrowser && (isUnd(CSS) || !Object.hasOwnProperty.call(CSS, 'registerProperty'))) {
|
|
3419
|
+
transformsPropertiesRegistered = false;
|
|
3420
|
+
}
|
|
3421
|
+
else {
|
|
3422
|
+
validTransforms.forEach(t => {
|
|
3423
|
+
const isSkew = stringStartsWith(t, 'skew');
|
|
3424
|
+
const isScale = stringStartsWith(t, 'scale');
|
|
3425
|
+
const isRotate = stringStartsWith(t, 'rotate');
|
|
3426
|
+
const isTranslate = stringStartsWith(t, 'translate');
|
|
3427
|
+
const isAngle = isRotate || isSkew;
|
|
3428
|
+
const syntax = isAngle ? '<angle>' : isScale ? "<number>" : isTranslate ? "<length-percentage>" : "*";
|
|
3429
|
+
try {
|
|
3430
|
+
CSS.registerProperty({
|
|
3431
|
+
name: '--' + t,
|
|
3432
|
+
syntax,
|
|
3433
|
+
inherits: false,
|
|
3434
|
+
initialValue: isTranslate ? '0px' : isAngle ? '0deg' : isScale ? '1' : '0',
|
|
3435
|
+
});
|
|
3436
|
+
}
|
|
3437
|
+
catch { }
|
|
3438
|
+
});
|
|
3439
|
+
transformsPropertiesRegistered = true;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3361
3442
|
const parsedTargets = registerTargets(targets);
|
|
3362
3443
|
const targetsLength = parsedTargets.length;
|
|
3363
3444
|
if (!targetsLength) {
|
|
@@ -3513,7 +3594,14 @@ class WAAPIAnimation {
|
|
|
3513
3594
|
/** @param {Number} time */
|
|
3514
3595
|
set currentTime(time) {
|
|
3515
3596
|
const t = time * (globals.timeScale === 1 ? 1 : K);
|
|
3516
|
-
this.forEach(anim =>
|
|
3597
|
+
this.forEach(anim => {
|
|
3598
|
+
// Make sure the animation playState is not 'paused' in order to properly trigger an onfinish callback.
|
|
3599
|
+
// 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.
|
|
3600
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Animation/finish_event
|
|
3601
|
+
if (t >= this.duration)
|
|
3602
|
+
anim.play();
|
|
3603
|
+
anim.currentTime = t;
|
|
3604
|
+
});
|
|
3517
3605
|
}
|
|
3518
3606
|
get progress() {
|
|
3519
3607
|
return this.currentTime / this.duration;
|
|
@@ -3783,32 +3871,15 @@ const remove = (targets, renderable, propertyName) => {
|
|
|
3783
3871
|
return targetsArray;
|
|
3784
3872
|
};
|
|
3785
3873
|
/**
|
|
3786
|
-
* @param {
|
|
3787
|
-
* @
|
|
3788
|
-
* @param {Number} [decimalLength]
|
|
3789
|
-
* @return {Number}
|
|
3874
|
+
* @param {(...args: any[]) => Tickable} constructor
|
|
3875
|
+
* @return {(...args: any[]) => Tickable}
|
|
3790
3876
|
*/
|
|
3791
|
-
const
|
|
3877
|
+
const keepTime = createRefreshable;
|
|
3792
3878
|
/**
|
|
3793
3879
|
* @param {String|Array} items
|
|
3794
3880
|
* @return {any}
|
|
3795
3881
|
*/
|
|
3796
3882
|
const randomPick = items => items[random(0, items.length - 1)];
|
|
3797
|
-
/**
|
|
3798
|
-
* Adapted from https://bost.ocks.org/mike/shuffle/
|
|
3799
|
-
* @param {Array} items
|
|
3800
|
-
* @return {Array}
|
|
3801
|
-
*/
|
|
3802
|
-
const shuffle = items => {
|
|
3803
|
-
let m = items.length, t, i;
|
|
3804
|
-
while (m) {
|
|
3805
|
-
i = random(0, --m);
|
|
3806
|
-
t = items[m];
|
|
3807
|
-
items[m] = items[i];
|
|
3808
|
-
items[i] = t;
|
|
3809
|
-
}
|
|
3810
|
-
return items;
|
|
3811
|
-
};
|
|
3812
3883
|
/**
|
|
3813
3884
|
* @param {Number|String} v
|
|
3814
3885
|
* @param {Number} decimalLength
|
|
@@ -3989,6 +4060,7 @@ const utils = {
|
|
|
3989
4060
|
shuffle,
|
|
3990
4061
|
lerp,
|
|
3991
4062
|
sync,
|
|
4063
|
+
keepTime,
|
|
3992
4064
|
clamp: /** @type {typeof clamp & ChainedClamp} */ (makeChainable(clamp)),
|
|
3993
4065
|
round: /** @type {typeof round & ChainedRound} */ (makeChainable(round)),
|
|
3994
4066
|
snap: /** @type {typeof snap & ChainedSnap} */ (makeChainable(snap)),
|
|
@@ -4260,16 +4332,13 @@ class Timeline extends Timer {
|
|
|
4260
4332
|
*/
|
|
4261
4333
|
stretch(newDuration) {
|
|
4262
4334
|
const currentDuration = this.duration;
|
|
4263
|
-
if (currentDuration ===
|
|
4335
|
+
if (currentDuration === normalizeTime(newDuration))
|
|
4264
4336
|
return this;
|
|
4265
4337
|
const timeScale = newDuration / currentDuration;
|
|
4266
4338
|
const labels = this.labels;
|
|
4267
|
-
forEachChildren(this, (/** @type {JSAnimation} */ child) =>
|
|
4268
|
-
|
|
4269
|
-
});
|
|
4270
|
-
for (let labelName in labels) {
|
|
4339
|
+
forEachChildren(this, (/** @type {JSAnimation} */ child) => child.stretch(child.duration * timeScale));
|
|
4340
|
+
for (let labelName in labels)
|
|
4271
4341
|
labels[labelName] *= timeScale;
|
|
4272
|
-
}
|
|
4273
4342
|
return super.stretch(newDuration);
|
|
4274
4343
|
}
|
|
4275
4344
|
/**
|
|
@@ -4311,8 +4380,8 @@ class Animatable {
|
|
|
4311
4380
|
* @param {AnimatableParams} parameters
|
|
4312
4381
|
*/
|
|
4313
4382
|
constructor(targets, parameters) {
|
|
4314
|
-
if (
|
|
4315
|
-
|
|
4383
|
+
if (scope.current)
|
|
4384
|
+
scope.current.register(this);
|
|
4316
4385
|
/** @type {AnimationParams} */
|
|
4317
4386
|
const globalParams = {};
|
|
4318
4387
|
const properties = {};
|
|
@@ -4620,11 +4689,6 @@ class Transforms {
|
|
|
4620
4689
|
});
|
|
4621
4690
|
}
|
|
4622
4691
|
}
|
|
4623
|
-
/**
|
|
4624
|
-
* @typedef {Object} DraggableCursorParams
|
|
4625
|
-
* @property {String} [onHover]
|
|
4626
|
-
* @property {String} [onGrab]
|
|
4627
|
-
*/
|
|
4628
4692
|
/**
|
|
4629
4693
|
* @template {Array<Number>|DOMTargetSelector|String|Number|Boolean|Function|DraggableCursorParams} T
|
|
4630
4694
|
* @param {T | ((draggable: Draggable) => T)} value
|
|
@@ -4633,45 +4697,6 @@ class Transforms {
|
|
|
4633
4697
|
*/
|
|
4634
4698
|
const parseDraggableFunctionParameter = (value, draggable) => value && isFnc(value) ? /** @type {Function} */ (value)(draggable) : value;
|
|
4635
4699
|
let zIndex = 0;
|
|
4636
|
-
/**
|
|
4637
|
-
* @typedef {Object} DraggableAxisParam
|
|
4638
|
-
* @property {String} [mapTo]
|
|
4639
|
-
* @property {TweenModifier} [modifier]
|
|
4640
|
-
* @property {TweenComposition} [composition]
|
|
4641
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
4642
|
-
*/
|
|
4643
|
-
/**
|
|
4644
|
-
* @typedef {Object} DraggableParams
|
|
4645
|
-
* @property {DOMTargetSelector} [trigger]
|
|
4646
|
-
* @property {DOMTargetSelector|Array<Number>|((draggable: Draggable) => DOMTargetSelector|Array<Number>)} [container]
|
|
4647
|
-
* @property {Boolean|DraggableAxisParam} [x]
|
|
4648
|
-
* @property {Boolean|DraggableAxisParam} [y]
|
|
4649
|
-
* @property {TweenModifier} [modifier]
|
|
4650
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [snap]
|
|
4651
|
-
* @property {Number|Array<Number>|((draggable: Draggable) => Number|Array<Number>)} [containerPadding]
|
|
4652
|
-
* @property {Number|((draggable: Draggable) => Number)} [containerFriction]
|
|
4653
|
-
* @property {Number|((draggable: Draggable) => Number)} [releaseContainerFriction]
|
|
4654
|
-
* @property {Number|((draggable: Draggable) => Number)} [dragSpeed]
|
|
4655
|
-
* @property {Number|((draggable: Draggable) => Number)} [scrollSpeed]
|
|
4656
|
-
* @property {Number|((draggable: Draggable) => Number)} [scrollThreshold]
|
|
4657
|
-
* @property {Number|((draggable: Draggable) => Number)} [minVelocity]
|
|
4658
|
-
* @property {Number|((draggable: Draggable) => Number)} [maxVelocity]
|
|
4659
|
-
* @property {Number|((draggable: Draggable) => Number)} [velocityMultiplier]
|
|
4660
|
-
* @property {Number} [releaseMass]
|
|
4661
|
-
* @property {Number} [releaseStiffness]
|
|
4662
|
-
* @property {Number} [releaseDamping]
|
|
4663
|
-
* @property {Boolean} [releaseDamping]
|
|
4664
|
-
* @property {EasingParam} [releaseEase]
|
|
4665
|
-
* @property {Boolean|DraggableCursorParams|((draggable: Draggable) => Boolean|DraggableCursorParams)} [cursor]
|
|
4666
|
-
* @property {Callback<Draggable>} [onGrab]
|
|
4667
|
-
* @property {Callback<Draggable>} [onDrag]
|
|
4668
|
-
* @property {Callback<Draggable>} [onRelease]
|
|
4669
|
-
* @property {Callback<Draggable>} [onUpdate]
|
|
4670
|
-
* @property {Callback<Draggable>} [onSettle]
|
|
4671
|
-
* @property {Callback<Draggable>} [onSnap]
|
|
4672
|
-
* @property {Callback<Draggable>} [onResize]
|
|
4673
|
-
* @property {Callback<Draggable>} [onAfterResize]
|
|
4674
|
-
*/
|
|
4675
4700
|
class Draggable {
|
|
4676
4701
|
/**
|
|
4677
4702
|
* @param {TargetsParam} target
|
|
@@ -4680,8 +4705,8 @@ class Draggable {
|
|
|
4680
4705
|
constructor(target, parameters = {}) {
|
|
4681
4706
|
if (!target)
|
|
4682
4707
|
return;
|
|
4683
|
-
if (
|
|
4684
|
-
|
|
4708
|
+
if (scope.current)
|
|
4709
|
+
scope.current.register(this);
|
|
4685
4710
|
const paramX = parameters.x;
|
|
4686
4711
|
const paramY = parameters.y;
|
|
4687
4712
|
const trigger = parameters.trigger;
|
|
@@ -4885,7 +4910,7 @@ class Draggable {
|
|
|
4885
4910
|
this.canScroll = false;
|
|
4886
4911
|
this.enabled = false;
|
|
4887
4912
|
this.initialized = false;
|
|
4888
|
-
this.activeProp = this.disabled[
|
|
4913
|
+
this.activeProp = this.disabled[1] ? xProp : yProp;
|
|
4889
4914
|
this.animate.animations[this.activeProp].onRender = () => {
|
|
4890
4915
|
const hasUpdated = this.updated;
|
|
4891
4916
|
const hasMoved = this.grabbed && hasUpdated;
|
|
@@ -5665,6 +5690,7 @@ class Draggable {
|
|
|
5665
5690
|
this.overshootXTicker.revert();
|
|
5666
5691
|
this.overshootYTicker.revert();
|
|
5667
5692
|
this.resizeTicker.revert();
|
|
5693
|
+
this.animate.revert();
|
|
5668
5694
|
return this;
|
|
5669
5695
|
}
|
|
5670
5696
|
/**
|
|
@@ -5710,39 +5736,11 @@ class Draggable {
|
|
|
5710
5736
|
const createDraggable = (target, parameters) => new Draggable(target, parameters);
|
|
5711
5737
|
|
|
5712
5738
|
|
|
5713
|
-
/**
|
|
5714
|
-
* @typedef {Object} ReactRef
|
|
5715
|
-
* @property {HTMLElement|SVGElement|null} [current]
|
|
5716
|
-
*/
|
|
5717
|
-
/**
|
|
5718
|
-
* @typedef {Object} AngularRef
|
|
5719
|
-
* @property {HTMLElement|SVGElement} [nativeElement]
|
|
5720
|
-
*/
|
|
5721
|
-
/**
|
|
5722
|
-
* @typedef {Object} ScopeParams
|
|
5723
|
-
* @property {DOMTargetSelector|ReactRef|AngularRef} [root]
|
|
5724
|
-
* @property {DefaultsParams} [defaults]
|
|
5725
|
-
* @property {Record<String, String>} [mediaQueries]
|
|
5726
|
-
*/
|
|
5727
|
-
/**
|
|
5728
|
-
* @callback ScopeCleanup
|
|
5729
|
-
* @param {Scope} [scope]
|
|
5730
|
-
*/
|
|
5731
|
-
/**
|
|
5732
|
-
* @callback ScopeConstructor
|
|
5733
|
-
* @param {Scope} [scope]
|
|
5734
|
-
* @return {ScopeCleanup|void}
|
|
5735
|
-
*/
|
|
5736
|
-
/**
|
|
5737
|
-
* @callback ScopeMethod
|
|
5738
|
-
* @param {...*} args
|
|
5739
|
-
* @return {ScopeCleanup|void}
|
|
5740
|
-
*/
|
|
5741
5739
|
class Scope {
|
|
5742
5740
|
/** @param {ScopeParams} [parameters] */
|
|
5743
5741
|
constructor(parameters = {}) {
|
|
5744
|
-
if (
|
|
5745
|
-
|
|
5742
|
+
if (scope.current)
|
|
5743
|
+
scope.current.register(this);
|
|
5746
5744
|
const rootParam = parameters.root;
|
|
5747
5745
|
/** @type {Document|DOMTarget} */
|
|
5748
5746
|
let root = doc;
|
|
@@ -5759,13 +5757,23 @@ class Scope {
|
|
|
5759
5757
|
this.defaults = scopeDefaults ? mergeObjects(scopeDefaults, globalDefault) : globalDefault;
|
|
5760
5758
|
/** @type {Document|DOMTarget} */
|
|
5761
5759
|
this.root = root;
|
|
5762
|
-
/** @type {Array<
|
|
5760
|
+
/** @type {Array<ScopeConstructorCallback>} */
|
|
5763
5761
|
this.constructors = [];
|
|
5764
|
-
/** @type {Array<
|
|
5762
|
+
/** @type {Array<ScopeCleanupCallback>} */
|
|
5765
5763
|
this.revertConstructors = [];
|
|
5766
5764
|
/** @type {Array<Revertible>} */
|
|
5767
5765
|
this.revertibles = [];
|
|
5768
|
-
/** @type {
|
|
5766
|
+
/** @type {Array<ScopeConstructorCallback | ((scope: this) => Tickable)>} */
|
|
5767
|
+
this.constructorsOnce = [];
|
|
5768
|
+
/** @type {Array<ScopeCleanupCallback>} */
|
|
5769
|
+
this.revertConstructorsOnce = [];
|
|
5770
|
+
/** @type {Array<Revertible>} */
|
|
5771
|
+
this.revertiblesOnce = [];
|
|
5772
|
+
/** @type {Boolean} */
|
|
5773
|
+
this.once = false;
|
|
5774
|
+
/** @type {Number} */
|
|
5775
|
+
this.onceIndex = 0;
|
|
5776
|
+
/** @type {Record<String, ScopeMethod>} */
|
|
5769
5777
|
this.methods = {};
|
|
5770
5778
|
/** @type {Record<String, Boolean>} */
|
|
5771
5779
|
this.matches = {};
|
|
@@ -5782,26 +5790,30 @@ class Scope {
|
|
|
5782
5790
|
}
|
|
5783
5791
|
}
|
|
5784
5792
|
/**
|
|
5785
|
-
* @
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5793
|
+
* @param {Revertible} revertible
|
|
5794
|
+
*/
|
|
5795
|
+
register(revertible) {
|
|
5796
|
+
const store = this.once ? this.revertiblesOnce : this.revertibles;
|
|
5797
|
+
store.push(revertible);
|
|
5798
|
+
}
|
|
5799
|
+
/**
|
|
5800
|
+
* @template T
|
|
5801
|
+
* @param {ScopedCallback<T>} cb
|
|
5802
|
+
* @return {T}
|
|
5791
5803
|
*/
|
|
5792
5804
|
execute(cb) {
|
|
5793
|
-
let activeScope =
|
|
5794
|
-
let activeRoot =
|
|
5805
|
+
let activeScope = scope.current;
|
|
5806
|
+
let activeRoot = scope.root;
|
|
5795
5807
|
let activeDefaults = globals.defaults;
|
|
5796
|
-
|
|
5797
|
-
|
|
5808
|
+
scope.current = this;
|
|
5809
|
+
scope.root = this.root;
|
|
5798
5810
|
globals.defaults = this.defaults;
|
|
5799
5811
|
const mqs = this.mediaQueryLists;
|
|
5800
5812
|
for (let mq in mqs)
|
|
5801
5813
|
this.matches[mq] = mqs[mq].matches;
|
|
5802
5814
|
const returned = cb(this);
|
|
5803
|
-
|
|
5804
|
-
|
|
5815
|
+
scope.current = activeScope;
|
|
5816
|
+
scope.root = activeRoot;
|
|
5805
5817
|
globals.defaults = activeDefaults;
|
|
5806
5818
|
return returned;
|
|
5807
5819
|
}
|
|
@@ -5809,6 +5821,7 @@ class Scope {
|
|
|
5809
5821
|
* @return {this}
|
|
5810
5822
|
*/
|
|
5811
5823
|
refresh() {
|
|
5824
|
+
this.onceIndex = 0;
|
|
5812
5825
|
this.execute(() => {
|
|
5813
5826
|
let i = this.revertibles.length;
|
|
5814
5827
|
let y = this.revertConstructors.length;
|
|
@@ -5818,9 +5831,9 @@ class Scope {
|
|
|
5818
5831
|
this.revertConstructors[y](this);
|
|
5819
5832
|
this.revertibles.length = 0;
|
|
5820
5833
|
this.revertConstructors.length = 0;
|
|
5821
|
-
this.constructors.forEach(constructor => {
|
|
5834
|
+
this.constructors.forEach((/** @type {ScopeConstructorCallback} */ constructor) => {
|
|
5822
5835
|
const revertConstructor = constructor(this);
|
|
5823
|
-
if (revertConstructor) {
|
|
5836
|
+
if (isFnc(revertConstructor)) {
|
|
5824
5837
|
this.revertConstructors.push(revertConstructor);
|
|
5825
5838
|
}
|
|
5826
5839
|
});
|
|
@@ -5828,28 +5841,26 @@ class Scope {
|
|
|
5828
5841
|
return this;
|
|
5829
5842
|
}
|
|
5830
5843
|
/**
|
|
5831
|
-
* @callback contructorCallback
|
|
5832
|
-
* @param {this} self
|
|
5833
|
-
*
|
|
5834
5844
|
* @overload
|
|
5835
5845
|
* @param {String} a1
|
|
5836
5846
|
* @param {ScopeMethod} a2
|
|
5837
5847
|
* @return {this}
|
|
5838
5848
|
*
|
|
5839
5849
|
* @overload
|
|
5840
|
-
* @param {
|
|
5850
|
+
* @param {ScopeConstructorCallback} a1
|
|
5841
5851
|
* @return {this}
|
|
5842
5852
|
*
|
|
5843
|
-
* @param {String|
|
|
5853
|
+
* @param {String|ScopeConstructorCallback} a1
|
|
5844
5854
|
* @param {ScopeMethod} [a2]
|
|
5845
5855
|
*/
|
|
5846
5856
|
add(a1, a2) {
|
|
5857
|
+
this.once = false;
|
|
5847
5858
|
if (isFnc(a1)) {
|
|
5848
|
-
const constructor = /** @type {
|
|
5859
|
+
const constructor = /** @type {ScopeConstructorCallback} */ (a1);
|
|
5849
5860
|
this.constructors.push(constructor);
|
|
5850
5861
|
this.execute(() => {
|
|
5851
5862
|
const revertConstructor = constructor(this);
|
|
5852
|
-
if (revertConstructor) {
|
|
5863
|
+
if (isFnc(revertConstructor)) {
|
|
5853
5864
|
this.revertConstructors.push(revertConstructor);
|
|
5854
5865
|
}
|
|
5855
5866
|
});
|
|
@@ -5859,6 +5870,46 @@ class Scope {
|
|
|
5859
5870
|
}
|
|
5860
5871
|
return this;
|
|
5861
5872
|
}
|
|
5873
|
+
/**
|
|
5874
|
+
* @param {ScopeConstructorCallback} scopeConstructorCallback
|
|
5875
|
+
* @return {this}
|
|
5876
|
+
*/
|
|
5877
|
+
addOnce(scopeConstructorCallback) {
|
|
5878
|
+
this.once = true;
|
|
5879
|
+
if (isFnc(scopeConstructorCallback)) {
|
|
5880
|
+
const currentIndex = this.onceIndex++;
|
|
5881
|
+
const tracked = this.constructorsOnce[currentIndex];
|
|
5882
|
+
if (tracked)
|
|
5883
|
+
return this;
|
|
5884
|
+
const constructor = /** @type {ScopeConstructorCallback} */ (scopeConstructorCallback);
|
|
5885
|
+
this.constructorsOnce[currentIndex] = constructor;
|
|
5886
|
+
this.execute(() => {
|
|
5887
|
+
const revertConstructor = constructor(this);
|
|
5888
|
+
if (isFnc(revertConstructor)) {
|
|
5889
|
+
this.revertConstructorsOnce.push(revertConstructor);
|
|
5890
|
+
}
|
|
5891
|
+
});
|
|
5892
|
+
}
|
|
5893
|
+
return this;
|
|
5894
|
+
}
|
|
5895
|
+
/**
|
|
5896
|
+
* @param {(scope: this) => Tickable} cb
|
|
5897
|
+
* @return {Tickable}
|
|
5898
|
+
*/
|
|
5899
|
+
keepTime(cb) {
|
|
5900
|
+
this.once = true;
|
|
5901
|
+
const currentIndex = this.onceIndex++;
|
|
5902
|
+
const tracked = /** @type {(scope: this) => Tickable} */ (this.constructorsOnce[currentIndex]);
|
|
5903
|
+
if (isFnc(tracked))
|
|
5904
|
+
return tracked(this);
|
|
5905
|
+
const constructor = /** @type {(scope: this) => Tickable} */ (createRefreshable(cb));
|
|
5906
|
+
this.constructorsOnce[currentIndex] = constructor;
|
|
5907
|
+
let trackedTickable;
|
|
5908
|
+
this.execute(() => {
|
|
5909
|
+
trackedTickable = constructor(this);
|
|
5910
|
+
});
|
|
5911
|
+
return trackedTickable;
|
|
5912
|
+
}
|
|
5862
5913
|
/**
|
|
5863
5914
|
* @param {Event} e
|
|
5864
5915
|
*/
|
|
@@ -5872,18 +5923,30 @@ class Scope {
|
|
|
5872
5923
|
revert() {
|
|
5873
5924
|
const revertibles = this.revertibles;
|
|
5874
5925
|
const revertConstructors = this.revertConstructors;
|
|
5926
|
+
const revertiblesOnce = this.revertiblesOnce;
|
|
5927
|
+
const revertConstructorsOnce = this.revertConstructorsOnce;
|
|
5875
5928
|
const mqs = this.mediaQueryLists;
|
|
5876
5929
|
let i = revertibles.length;
|
|
5877
|
-
let
|
|
5930
|
+
let j = revertConstructors.length;
|
|
5931
|
+
let k = revertiblesOnce.length;
|
|
5932
|
+
let l = revertConstructorsOnce.length;
|
|
5878
5933
|
while (i--)
|
|
5879
5934
|
revertibles[i].revert();
|
|
5880
|
-
while (
|
|
5881
|
-
revertConstructors[
|
|
5935
|
+
while (j--)
|
|
5936
|
+
revertConstructors[j](this);
|
|
5937
|
+
while (k--)
|
|
5938
|
+
revertiblesOnce[k].revert();
|
|
5939
|
+
while (l--)
|
|
5940
|
+
revertConstructorsOnce[l](this);
|
|
5882
5941
|
for (let mq in mqs)
|
|
5883
5942
|
mqs[mq].removeEventListener('change', this);
|
|
5884
5943
|
revertibles.length = 0;
|
|
5885
5944
|
revertConstructors.length = 0;
|
|
5886
5945
|
this.constructors.length = 0;
|
|
5946
|
+
revertiblesOnce.length = 0;
|
|
5947
|
+
revertConstructorsOnce.length = 0;
|
|
5948
|
+
this.constructorsOnce.length = 0;
|
|
5949
|
+
this.onceIndex = 0;
|
|
5887
5950
|
this.matches = {};
|
|
5888
5951
|
this.methods = {};
|
|
5889
5952
|
this.mediaQueryLists = {};
|
|
@@ -6200,7 +6263,7 @@ const getAnimationDomTarget = linked => {
|
|
|
6200
6263
|
return $linkedTarget;
|
|
6201
6264
|
};
|
|
6202
6265
|
let scrollerIndex = 0;
|
|
6203
|
-
const debugColors = ['#FF4B4B', '#FF971B', '#FFC730', '#F9F640', '#7AFF5A', '#18FF74', '#17E09B', '#3CFFEC', '#05DBE9', '#33B3F1', '#638CF9', '#C563FE', '#FF4FCF', '#F93F8A'];
|
|
6266
|
+
const debugColors$1 = ['#FF4B4B', '#FF971B', '#FFC730', '#F9F640', '#7AFF5A', '#18FF74', '#17E09B', '#3CFFEC', '#05DBE9', '#33B3F1', '#638CF9', '#C563FE', '#FF4FCF', '#F93F8A'];
|
|
6204
6267
|
/**
|
|
6205
6268
|
* @typedef {Object} ScrollThresholdParam
|
|
6206
6269
|
* @property {ScrollThresholdValue} [target]
|
|
@@ -6241,8 +6304,8 @@ class ScrollObserver {
|
|
|
6241
6304
|
* @param {ScrollObserverParams} parameters
|
|
6242
6305
|
*/
|
|
6243
6306
|
constructor(parameters = {}) {
|
|
6244
|
-
if (
|
|
6245
|
-
|
|
6307
|
+
if (scope.current)
|
|
6308
|
+
scope.current.register(this);
|
|
6246
6309
|
const syncMode = setValue(parameters.sync, 'play pause');
|
|
6247
6310
|
const ease = syncMode ? parseEasings(/** @type {EasingParam} */ (syncMode)) : null;
|
|
6248
6311
|
const isLinear = syncMode && (syncMode === 'linear' || syncMode === none);
|
|
@@ -6432,7 +6495,7 @@ class ScrollObserver {
|
|
|
6432
6495
|
const $debug = doc.createElement('div');
|
|
6433
6496
|
const $thresholds = doc.createElement('div');
|
|
6434
6497
|
const $triggers = doc.createElement('div');
|
|
6435
|
-
const color = debugColors[this.index % debugColors.length];
|
|
6498
|
+
const color = debugColors$1[this.index % debugColors$1.length];
|
|
6436
6499
|
const useWin = container.useWin;
|
|
6437
6500
|
const containerWidth = useWin ? container.winWidth : container.width;
|
|
6438
6501
|
const containerHeight = useWin ? container.winHeight : container.height;
|
|
@@ -6785,27 +6848,474 @@ class ScrollObserver {
|
|
|
6785
6848
|
const onScroll = (parameters = {}) => new ScrollObserver(parameters);
|
|
6786
6849
|
|
|
6787
6850
|
|
|
6851
|
+
const segmenter = !isUnd(Intl) && Intl.Segmenter;
|
|
6852
|
+
const valueRgx = /\{value\}/g;
|
|
6853
|
+
const indexRgx = /\{i\}/g;
|
|
6854
|
+
const whiteSpaceGroupRgx = /(\s+)/;
|
|
6855
|
+
const whiteSpaceRgx = /^\s+$/;
|
|
6856
|
+
const lineType = 'line';
|
|
6857
|
+
const wordType = 'word';
|
|
6858
|
+
const charType = 'char';
|
|
6859
|
+
const dataLine = `data-line`;
|
|
6860
|
+
/**
|
|
6861
|
+
* @typedef {Object} Segment
|
|
6862
|
+
* @property {String} segment
|
|
6863
|
+
* @property {Boolean} [isWordLike]
|
|
6864
|
+
*/
|
|
6865
|
+
/**
|
|
6866
|
+
* @typedef {Object} Segmenter
|
|
6867
|
+
* @property {function(String): Iterable<Segment>} segment
|
|
6868
|
+
*/
|
|
6869
|
+
/** @type {Segmenter} */
|
|
6870
|
+
let wordSegmenter = null;
|
|
6871
|
+
/** @type {Segmenter} */
|
|
6872
|
+
let graphemeSegmenter = null;
|
|
6873
|
+
let $splitTemplate = null;
|
|
6874
|
+
/**
|
|
6875
|
+
* @param {Segment} seg
|
|
6876
|
+
* @return {Boolean}
|
|
6877
|
+
*/
|
|
6878
|
+
const isSegmentWordLike = seg => {
|
|
6879
|
+
return seg.isWordLike ||
|
|
6880
|
+
seg.segment === ' ' || // Consider spaces as words first, then handle them diffrently later
|
|
6881
|
+
isNum(+seg.segment); // Safari doesn't considers numbers as words
|
|
6882
|
+
};
|
|
6788
6883
|
/**
|
|
6789
|
-
* @
|
|
6790
|
-
* @property {Number|String} [start]
|
|
6791
|
-
* @property {Number|'first'|'center'|'last'} [from]
|
|
6792
|
-
* @property {Boolean} [reversed]
|
|
6793
|
-
* @property {Array.<Number>} [grid]
|
|
6794
|
-
* @property {('x'|'y')} [axis]
|
|
6795
|
-
* @property {EasingParam} [ease]
|
|
6796
|
-
* @property {TweenModifier} [modifier]
|
|
6884
|
+
* @param {HTMLElement} $el
|
|
6797
6885
|
*/
|
|
6886
|
+
const setAriaHidden = $el => $el.setAttribute('aria-hidden', 'true');
|
|
6798
6887
|
/**
|
|
6799
|
-
* @
|
|
6800
|
-
* @param {
|
|
6801
|
-
* @
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6888
|
+
* @param {DOMTarget} $el
|
|
6889
|
+
* @param {String} type
|
|
6890
|
+
* @return {Array<HTMLElement>}
|
|
6891
|
+
*/
|
|
6892
|
+
const getAllTopLevelElements = ($el, type) => [... /** @type {*} */($el.querySelectorAll(`[data-${type}]:not([data-${type}] [data-${type}])`))];
|
|
6893
|
+
const debugColors = { line: '#00D672', word: '#FF4B4B', char: '#5A87FF' };
|
|
6894
|
+
/**
|
|
6895
|
+
* @param {HTMLElement} $el
|
|
6896
|
+
*/
|
|
6897
|
+
const filterEmptyElements = $el => {
|
|
6898
|
+
if (!$el.childElementCount && !$el.textContent.trim()) {
|
|
6899
|
+
const $parent = $el.parentElement;
|
|
6900
|
+
$el.remove();
|
|
6901
|
+
if ($parent)
|
|
6902
|
+
filterEmptyElements($parent);
|
|
6903
|
+
}
|
|
6904
|
+
};
|
|
6905
|
+
/**
|
|
6906
|
+
* @param {HTMLElement} $el
|
|
6907
|
+
* @param {Number} lineIndex
|
|
6908
|
+
* @param {Set<HTMLElement>} bin
|
|
6909
|
+
* @returns {Set<HTMLElement>}
|
|
6910
|
+
*/
|
|
6911
|
+
const filterLineElements = ($el, lineIndex, bin) => {
|
|
6912
|
+
const dataLineAttr = $el.getAttribute(dataLine);
|
|
6913
|
+
if (dataLineAttr !== null && +dataLineAttr !== lineIndex || $el.tagName === 'BR')
|
|
6914
|
+
bin.add($el);
|
|
6915
|
+
let i = $el.childElementCount;
|
|
6916
|
+
while (i--)
|
|
6917
|
+
filterLineElements(/** @type {HTMLElement} */ ($el.children[i]), lineIndex, bin);
|
|
6918
|
+
return bin;
|
|
6919
|
+
};
|
|
6920
|
+
/**
|
|
6921
|
+
* @param {'line'|'word'|'char'} type
|
|
6922
|
+
* @param {splitTemplateParams} params
|
|
6923
|
+
* @return {String}
|
|
6924
|
+
*/
|
|
6925
|
+
const generateTemplate = (type, params = {}) => {
|
|
6926
|
+
let template = ``;
|
|
6927
|
+
const classString = isStr(params.class) ? ` class="${params.class}"` : '';
|
|
6928
|
+
const cloneType = setValue(params.clone, false);
|
|
6929
|
+
const wrapType = setValue(params.wrap, false);
|
|
6930
|
+
const overflow = wrapType ? wrapType === true ? 'clip' : wrapType : cloneType ? 'clip' : false;
|
|
6931
|
+
if (wrapType)
|
|
6932
|
+
template += `<span${overflow ? ` style="overflow:${overflow};"` : ''}>`;
|
|
6933
|
+
template += `<span${classString}${cloneType ? ` style="position:relative;"` : ''} data-${type}="{i}">`;
|
|
6934
|
+
if (cloneType) {
|
|
6935
|
+
const left = cloneType === 'left' ? '-100%' : cloneType === 'right' ? '100%' : '0';
|
|
6936
|
+
const top = cloneType === 'top' ? '-100%' : cloneType === 'bottom' ? '100%' : '0';
|
|
6937
|
+
template += `<span>{value}</span>`;
|
|
6938
|
+
template += `<span inert style="position:absolute;top:${top};left:${left};white-space:nowrap;">{value}</span>`;
|
|
6939
|
+
}
|
|
6940
|
+
else {
|
|
6941
|
+
template += `{value}`;
|
|
6942
|
+
}
|
|
6943
|
+
template += `</span>`;
|
|
6944
|
+
if (wrapType)
|
|
6945
|
+
template += `</span>`;
|
|
6946
|
+
return template;
|
|
6947
|
+
};
|
|
6948
|
+
/**
|
|
6949
|
+
* @param {String|SplitFunctionValue} htmlTemplate
|
|
6950
|
+
* @param {Array<HTMLElement>} store
|
|
6951
|
+
* @param {Node|HTMLElement} node
|
|
6952
|
+
* @param {DocumentFragment} $parentFragment
|
|
6953
|
+
* @param {'line'|'word'|'char'} type
|
|
6954
|
+
* @param {Boolean} debug
|
|
6955
|
+
* @param {Number} lineIndex
|
|
6956
|
+
* @param {Number} [wordIndex]
|
|
6957
|
+
* @param {Number} [charIndex]
|
|
6958
|
+
* @return {HTMLElement}
|
|
6959
|
+
*/
|
|
6960
|
+
const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, debug, lineIndex, wordIndex, charIndex) => {
|
|
6961
|
+
const isLine = type === lineType;
|
|
6962
|
+
const isChar = type === charType;
|
|
6963
|
+
const className = `_${type}_`;
|
|
6964
|
+
const template = isFnc(htmlTemplate) ? htmlTemplate(node) : htmlTemplate;
|
|
6965
|
+
const displayStyle = isLine ? 'block' : 'inline-block';
|
|
6966
|
+
$splitTemplate.innerHTML = template
|
|
6967
|
+
.replace(valueRgx, `<i class="${className}"></i>`)
|
|
6968
|
+
.replace(indexRgx, `${isChar ? charIndex : isLine ? lineIndex : wordIndex}`);
|
|
6969
|
+
const $content = $splitTemplate.content;
|
|
6970
|
+
const $highestParent = /** @type {HTMLElement} */ ($content.firstElementChild);
|
|
6971
|
+
const $split = /** @type {HTMLElement} */ ($content.querySelector(`[data-${type}]`)) || $highestParent;
|
|
6972
|
+
const $replacables = /** @type {NodeListOf<HTMLElement>} */ ($content.querySelectorAll(`i.${className}`));
|
|
6973
|
+
const replacablesLength = $replacables.length;
|
|
6974
|
+
if (replacablesLength) {
|
|
6975
|
+
$highestParent.style.display = displayStyle;
|
|
6976
|
+
$split.style.display = displayStyle;
|
|
6977
|
+
$split.setAttribute(dataLine, `${lineIndex}`);
|
|
6978
|
+
if (!isLine) {
|
|
6979
|
+
$split.setAttribute('data-word', `${wordIndex}`);
|
|
6980
|
+
if (isChar)
|
|
6981
|
+
$split.setAttribute('data-char', `${charIndex}`);
|
|
6982
|
+
}
|
|
6983
|
+
let i = replacablesLength;
|
|
6984
|
+
while (i--) {
|
|
6985
|
+
const $replace = $replacables[i];
|
|
6986
|
+
const $closestParent = $replace.parentElement;
|
|
6987
|
+
$closestParent.style.display = displayStyle;
|
|
6988
|
+
if (isLine) {
|
|
6989
|
+
$closestParent.innerHTML = /** @type {HTMLElement} */ (node).innerHTML;
|
|
6990
|
+
}
|
|
6991
|
+
else {
|
|
6992
|
+
$closestParent.replaceChild(node.cloneNode(true), $replace);
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
store.push($split);
|
|
6996
|
+
$parentFragment.appendChild($content);
|
|
6997
|
+
}
|
|
6998
|
+
else {
|
|
6999
|
+
console.warn(`The expression "{value}" is missing from the provided template.`);
|
|
7000
|
+
}
|
|
7001
|
+
if (debug)
|
|
7002
|
+
$highestParent.style.outline = `1px dotted ${debugColors[type]}`;
|
|
7003
|
+
return $highestParent;
|
|
7004
|
+
};
|
|
7005
|
+
/**
|
|
7006
|
+
* A class that splits text into words and wraps them in span elements while preserving the original HTML structure.
|
|
7007
|
+
* @class
|
|
7008
|
+
*/
|
|
7009
|
+
class TextSplitter {
|
|
7010
|
+
/**
|
|
7011
|
+
* @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
|
|
7012
|
+
* @param {TextSplitterParams} [parameters]
|
|
7013
|
+
*/
|
|
7014
|
+
constructor(target, parameters = {}) {
|
|
7015
|
+
// Only init segmenters when needed
|
|
7016
|
+
if (!wordSegmenter)
|
|
7017
|
+
wordSegmenter = segmenter ? new segmenter([], { granularity: wordType }) : {
|
|
7018
|
+
segment: (text) => {
|
|
7019
|
+
const segments = [];
|
|
7020
|
+
const words = text.split(whiteSpaceGroupRgx);
|
|
7021
|
+
for (let i = 0, l = words.length; i < l; i++) {
|
|
7022
|
+
const segment = words[i];
|
|
7023
|
+
segments.push({
|
|
7024
|
+
segment,
|
|
7025
|
+
isWordLike: !whiteSpaceRgx.test(segment), // Consider non-whitespace as word-like
|
|
7026
|
+
});
|
|
7027
|
+
}
|
|
7028
|
+
return segments;
|
|
7029
|
+
}
|
|
7030
|
+
};
|
|
7031
|
+
if (!graphemeSegmenter)
|
|
7032
|
+
graphemeSegmenter = segmenter ? new segmenter([], { granularity: 'grapheme' }) : {
|
|
7033
|
+
segment: text => [...text].map(char => ({ segment: char }))
|
|
7034
|
+
};
|
|
7035
|
+
if (!$splitTemplate && isBrowser)
|
|
7036
|
+
$splitTemplate = doc.createElement('template');
|
|
7037
|
+
if (scope.current)
|
|
7038
|
+
scope.current.register(this);
|
|
7039
|
+
const { words, chars, lines, accessible, includeSpaces, debug } = parameters;
|
|
7040
|
+
const $target = /** @type {HTMLElement} */ ((target = isArr(target) ? target[0] : target) && /** @type {Node} */ (target).nodeType ? target : (getNodeList(target) || [])[0]);
|
|
7041
|
+
const lineParams = lines === true ? {} : lines;
|
|
7042
|
+
const wordParams = words === true || isUnd(words) ? {} : words;
|
|
7043
|
+
const charParams = chars === true ? {} : chars;
|
|
7044
|
+
this.debug = setValue(debug, false);
|
|
7045
|
+
this.includeSpaces = setValue(includeSpaces, false);
|
|
7046
|
+
this.accessible = setValue(accessible, true);
|
|
7047
|
+
this.linesOnly = lineParams && (!wordParams && !charParams);
|
|
7048
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7049
|
+
this.lineTemplate = isObj(lineParams) ? generateTemplate(lineType, /** @type {splitTemplateParams} */ (lineParams)) : lineParams;
|
|
7050
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7051
|
+
this.wordTemplate = isObj(wordParams) || this.linesOnly ? generateTemplate(wordType, /** @type {splitTemplateParams} */ (wordParams)) : wordParams;
|
|
7052
|
+
/** @type {String|false|SplitFunctionValue} */
|
|
7053
|
+
this.charTemplate = isObj(charParams) ? generateTemplate(charType, /** @type {splitTemplateParams} */ (charParams)) : charParams;
|
|
7054
|
+
this.$target = $target;
|
|
7055
|
+
this.html = $target && $target.innerHTML;
|
|
7056
|
+
this.lines = [];
|
|
7057
|
+
this.words = [];
|
|
7058
|
+
this.chars = [];
|
|
7059
|
+
this.effects = [];
|
|
7060
|
+
this.effectsCleanups = [];
|
|
7061
|
+
this.cache = null;
|
|
7062
|
+
this.ready = false;
|
|
7063
|
+
this.width = 0;
|
|
7064
|
+
this.resizeTimeout = null;
|
|
7065
|
+
const handleSplit = () => this.html && (lineParams || wordParams || charParams) && this.split();
|
|
7066
|
+
// Make sure this is declared before calling handleSplit() in case revert() is called inside an effect callback
|
|
7067
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
7068
|
+
// Use a setTimeout instead of a Timer for better tree shaking
|
|
7069
|
+
clearTimeout(this.resizeTimeout);
|
|
7070
|
+
this.resizeTimeout = setTimeout(() => {
|
|
7071
|
+
const currentWidth = /** @type {HTMLElement} */ ($target).offsetWidth;
|
|
7072
|
+
if (currentWidth === this.width)
|
|
7073
|
+
return;
|
|
7074
|
+
this.width = currentWidth;
|
|
7075
|
+
handleSplit();
|
|
7076
|
+
}, 150);
|
|
7077
|
+
});
|
|
7078
|
+
// Only declare the font ready promise when splitting by lines and not alreay split
|
|
7079
|
+
if (this.lineTemplate && !this.ready) {
|
|
7080
|
+
doc.fonts.ready.then(handleSplit);
|
|
7081
|
+
}
|
|
7082
|
+
else {
|
|
7083
|
+
handleSplit();
|
|
7084
|
+
}
|
|
7085
|
+
$target ? this.resizeObserver.observe($target) : console.warn('No Text Splitter target found.');
|
|
7086
|
+
}
|
|
7087
|
+
/**
|
|
7088
|
+
* @param {(...args: any[]) => Tickable | (() => void)} effect
|
|
7089
|
+
* @return this
|
|
7090
|
+
*/
|
|
7091
|
+
addEffect(effect) {
|
|
7092
|
+
if (!isFnc(effect))
|
|
7093
|
+
return console.warn('Effect must return a function.');
|
|
7094
|
+
const refreshableEffect = createRefreshable(effect);
|
|
7095
|
+
this.effects.push(refreshableEffect);
|
|
7096
|
+
if (this.ready)
|
|
7097
|
+
this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
|
|
7098
|
+
return this;
|
|
7099
|
+
}
|
|
7100
|
+
revert() {
|
|
7101
|
+
clearTimeout(this.resizeTimeout);
|
|
7102
|
+
this.lines.length = this.words.length = this.chars.length = 0;
|
|
7103
|
+
this.resizeObserver.disconnect();
|
|
7104
|
+
// Make sure to revert the effects after disconnecting the resizeObserver to avoid triggering it in the process
|
|
7105
|
+
this.effectsCleanups.forEach(cleanup => isFnc(cleanup) ? cleanup(this) : cleanup.revert && cleanup.revert());
|
|
7106
|
+
this.$target.innerHTML = this.html;
|
|
7107
|
+
return this;
|
|
7108
|
+
}
|
|
7109
|
+
/**
|
|
7110
|
+
* Recursively processes a node and its children
|
|
7111
|
+
* @param {Node} node
|
|
7112
|
+
*/
|
|
7113
|
+
splitNode(node) {
|
|
7114
|
+
const wordTemplate = this.wordTemplate;
|
|
7115
|
+
const charTemplate = this.charTemplate;
|
|
7116
|
+
const includeSpaces = this.includeSpaces;
|
|
7117
|
+
const debug = this.debug;
|
|
7118
|
+
const nodeType = node.nodeType;
|
|
7119
|
+
if (nodeType === 3) {
|
|
7120
|
+
const nodeText = node.nodeValue;
|
|
7121
|
+
// If the nodeText is only whitespace, leave it as is
|
|
7122
|
+
if (nodeText.trim()) {
|
|
7123
|
+
const tempWords = [];
|
|
7124
|
+
const words = this.words;
|
|
7125
|
+
const chars = this.chars;
|
|
7126
|
+
const wordSegments = wordSegmenter.segment(nodeText);
|
|
7127
|
+
const $wordsFragment = doc.createDocumentFragment();
|
|
7128
|
+
let prevSeg = null;
|
|
7129
|
+
for (const wordSegment of wordSegments) {
|
|
7130
|
+
const segment = wordSegment.segment;
|
|
7131
|
+
const isWordLike = isSegmentWordLike(wordSegment);
|
|
7132
|
+
// Determine if this segment should be a new word, first segment always becomes a new word
|
|
7133
|
+
if (!prevSeg || (isWordLike && (prevSeg && (isSegmentWordLike(prevSeg))))) {
|
|
7134
|
+
tempWords.push(segment);
|
|
7135
|
+
}
|
|
7136
|
+
else {
|
|
7137
|
+
// Only concatenate if both current and previous are non-word-like and don't contain spaces
|
|
7138
|
+
const lastWordIndex = tempWords.length - 1;
|
|
7139
|
+
const lastWord = tempWords[lastWordIndex];
|
|
7140
|
+
if (!lastWord.includes(' ') && !segment.includes(' ')) {
|
|
7141
|
+
tempWords[lastWordIndex] += segment;
|
|
7142
|
+
}
|
|
7143
|
+
else {
|
|
7144
|
+
tempWords.push(segment);
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
prevSeg = wordSegment;
|
|
7148
|
+
}
|
|
7149
|
+
for (let i = 0, l = tempWords.length; i < l; i++) {
|
|
7150
|
+
const word = tempWords[i];
|
|
7151
|
+
if (!word.trim()) {
|
|
7152
|
+
// Preserve whitespace only if includeSpaces is false and if the current space is not the first node
|
|
7153
|
+
if (i && includeSpaces)
|
|
7154
|
+
continue;
|
|
7155
|
+
$wordsFragment.appendChild(doc.createTextNode(word));
|
|
7156
|
+
}
|
|
7157
|
+
else {
|
|
7158
|
+
const nextWord = tempWords[i + 1];
|
|
7159
|
+
const hasWordFollowingSpace = includeSpaces && nextWord && !nextWord.trim();
|
|
7160
|
+
const wordToProcess = word;
|
|
7161
|
+
const charSegments = charTemplate ? graphemeSegmenter.segment(wordToProcess) : null;
|
|
7162
|
+
const $charsFragment = charTemplate ? doc.createDocumentFragment() : doc.createTextNode(hasWordFollowingSpace ? word + '\xa0' : word);
|
|
7163
|
+
if (charTemplate) {
|
|
7164
|
+
const charSegmentsArray = [...charSegments];
|
|
7165
|
+
for (let j = 0, jl = charSegmentsArray.length; j < jl; j++) {
|
|
7166
|
+
const charSegment = charSegmentsArray[j];
|
|
7167
|
+
const isLastChar = j === jl - 1;
|
|
7168
|
+
// If this is the last character and includeSpaces is true with a following space, append the space
|
|
7169
|
+
const charText = isLastChar && hasWordFollowingSpace ? charSegment.segment + '\xa0' : charSegment.segment;
|
|
7170
|
+
const $charNode = doc.createTextNode(charText);
|
|
7171
|
+
processHTMLTemplate(charTemplate, chars, $charNode, /** @type {DocumentFragment} */ ($charsFragment), charType, debug, -1, words.length, chars.length);
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
if (wordTemplate) {
|
|
7175
|
+
processHTMLTemplate(wordTemplate, words, $charsFragment, $wordsFragment, wordType, debug, -1, words.length, chars.length);
|
|
7176
|
+
// Chars elements must be re-parsed in the split() method if both words and chars are parsed
|
|
7177
|
+
}
|
|
7178
|
+
else if (charTemplate) {
|
|
7179
|
+
$wordsFragment.appendChild($charsFragment);
|
|
7180
|
+
}
|
|
7181
|
+
else {
|
|
7182
|
+
$wordsFragment.appendChild(doc.createTextNode(word));
|
|
7183
|
+
}
|
|
7184
|
+
// Skip the next iteration if we included a space
|
|
7185
|
+
if (hasWordFollowingSpace)
|
|
7186
|
+
i++;
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
node.parentNode.replaceChild($wordsFragment, node);
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
else if (nodeType === 1) {
|
|
7193
|
+
// Converting to an array is necessary to work around childNodes pottential mutation
|
|
7194
|
+
const childNodes = /** @type {Array<Node>} */ ([... /** @type {*} */(node.childNodes)]);
|
|
7195
|
+
for (let i = 0, l = childNodes.length; i < l; i++)
|
|
7196
|
+
this.splitNode(childNodes[i]);
|
|
7197
|
+
}
|
|
7198
|
+
}
|
|
7199
|
+
/**
|
|
7200
|
+
* @param {Boolean} clearCache
|
|
7201
|
+
* @return {this}
|
|
7202
|
+
*/
|
|
7203
|
+
split(clearCache = false) {
|
|
7204
|
+
const $el = this.$target;
|
|
7205
|
+
const isCached = !!this.cache && !clearCache;
|
|
7206
|
+
const lineTemplate = this.lineTemplate;
|
|
7207
|
+
const wordTemplate = this.wordTemplate;
|
|
7208
|
+
const charTemplate = this.charTemplate;
|
|
7209
|
+
const fontsReady = doc.fonts.status !== 'loading';
|
|
7210
|
+
const canSplitLines = lineTemplate && fontsReady;
|
|
7211
|
+
this.ready = !lineTemplate || fontsReady;
|
|
7212
|
+
if (!isCached) {
|
|
7213
|
+
if (clearCache) {
|
|
7214
|
+
$el.innerHTML = this.html;
|
|
7215
|
+
this.words.length = this.chars.length = 0;
|
|
7216
|
+
}
|
|
7217
|
+
this.splitNode($el);
|
|
7218
|
+
this.cache = $el.innerHTML;
|
|
7219
|
+
}
|
|
7220
|
+
// Always reset the html when splitting by lines
|
|
7221
|
+
if (canSplitLines) {
|
|
7222
|
+
// No need to revert effects animations here since it's already taken care by the refreshable
|
|
7223
|
+
this.effectsCleanups.forEach(cleanup => isFnc(cleanup) && cleanup(this));
|
|
7224
|
+
if (isCached)
|
|
7225
|
+
$el.innerHTML = this.cache;
|
|
7226
|
+
this.lines.length = 0;
|
|
7227
|
+
if (wordTemplate)
|
|
7228
|
+
this.words = getAllTopLevelElements($el, wordType);
|
|
7229
|
+
}
|
|
7230
|
+
// Always reparse characters after a line reset or if both words and chars are activated
|
|
7231
|
+
if (charTemplate && (canSplitLines || wordTemplate)) {
|
|
7232
|
+
this.chars = getAllTopLevelElements($el, charType);
|
|
7233
|
+
}
|
|
7234
|
+
// Words are used when lines only and prioritized over chars
|
|
7235
|
+
const elementsArray = this.words.length ? this.words : this.chars;
|
|
7236
|
+
let y, linesCount = 0;
|
|
7237
|
+
for (let i = 0, l = elementsArray.length; i < l; i++) {
|
|
7238
|
+
const $el = elementsArray[i];
|
|
7239
|
+
const { top, height } = $el.getBoundingClientRect();
|
|
7240
|
+
if (y && top - y > height * .5)
|
|
7241
|
+
linesCount++;
|
|
7242
|
+
$el.setAttribute(dataLine, `${linesCount}`);
|
|
7243
|
+
const nested = $el.querySelectorAll(`[${dataLine}]`);
|
|
7244
|
+
let c = nested.length;
|
|
7245
|
+
while (c--)
|
|
7246
|
+
nested[c].setAttribute(dataLine, `${linesCount}`);
|
|
7247
|
+
y = top;
|
|
7248
|
+
}
|
|
7249
|
+
if (canSplitLines) {
|
|
7250
|
+
const linesFragment = doc.createDocumentFragment();
|
|
7251
|
+
const parents = new Set();
|
|
7252
|
+
const clones = [];
|
|
7253
|
+
for (let lineIndex = 0; lineIndex < linesCount + 1; lineIndex++) {
|
|
7254
|
+
const $clone = /** @type {HTMLElement} */ ($el.cloneNode(true));
|
|
7255
|
+
filterLineElements($clone, lineIndex, new Set()).forEach($el => {
|
|
7256
|
+
const $parent = $el.parentElement;
|
|
7257
|
+
if ($parent)
|
|
7258
|
+
parents.add($parent);
|
|
7259
|
+
$el.remove();
|
|
7260
|
+
});
|
|
7261
|
+
clones.push($clone);
|
|
7262
|
+
}
|
|
7263
|
+
parents.forEach(filterEmptyElements);
|
|
7264
|
+
for (let cloneIndex = 0, clonesLength = clones.length; cloneIndex < clonesLength; cloneIndex++) {
|
|
7265
|
+
processHTMLTemplate(lineTemplate, this.lines, clones[cloneIndex], linesFragment, lineType, this.debug, cloneIndex);
|
|
7266
|
+
}
|
|
7267
|
+
$el.innerHTML = '';
|
|
7268
|
+
$el.appendChild(linesFragment);
|
|
7269
|
+
if (wordTemplate)
|
|
7270
|
+
this.words = getAllTopLevelElements($el, wordType);
|
|
7271
|
+
if (charTemplate)
|
|
7272
|
+
this.chars = getAllTopLevelElements($el, charType);
|
|
7273
|
+
}
|
|
7274
|
+
// Remove the word wrappers and clear the words array if lines split only
|
|
7275
|
+
if (this.linesOnly) {
|
|
7276
|
+
const words = this.words;
|
|
7277
|
+
let w = words.length;
|
|
7278
|
+
while (w--) {
|
|
7279
|
+
const $word = words[w];
|
|
7280
|
+
$word.replaceWith($word.textContent);
|
|
7281
|
+
}
|
|
7282
|
+
words.length = 0;
|
|
7283
|
+
}
|
|
7284
|
+
if (canSplitLines || clearCache) {
|
|
7285
|
+
this.effects.forEach((effect, i) => this.effectsCleanups[i] = effect(this));
|
|
7286
|
+
}
|
|
7287
|
+
if (this.accessible && (canSplitLines || !isCached)) {
|
|
7288
|
+
const $accessible = doc.createElement('span');
|
|
7289
|
+
// Make the accessible element visually-hidden (https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html)
|
|
7290
|
+
$accessible.style.cssText = `position:absolute;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);width:1px;height:1px;white-space:nowrap;`;
|
|
7291
|
+
// $accessible.setAttribute('tabindex', '-1');
|
|
7292
|
+
$accessible.innerHTML = this.html;
|
|
7293
|
+
$el.insertBefore($accessible, $el.firstChild);
|
|
7294
|
+
this.lines.forEach(setAriaHidden);
|
|
7295
|
+
this.words.forEach(setAriaHidden);
|
|
7296
|
+
this.chars.forEach(setAriaHidden);
|
|
7297
|
+
}
|
|
7298
|
+
this.width = /** @type {HTMLElement} */ ($el).offsetWidth;
|
|
7299
|
+
return this;
|
|
7300
|
+
}
|
|
7301
|
+
refresh() {
|
|
7302
|
+
this.split(true);
|
|
7303
|
+
}
|
|
7304
|
+
}
|
|
7305
|
+
/**
|
|
7306
|
+
* @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
|
|
7307
|
+
* @param {TextSplitterParams} [parameters]
|
|
7308
|
+
* @return {TextSplitter}
|
|
6805
7309
|
*/
|
|
7310
|
+
const split = (target, parameters) => new TextSplitter(target, parameters);
|
|
7311
|
+
const text = {
|
|
7312
|
+
split,
|
|
7313
|
+
};
|
|
7314
|
+
|
|
7315
|
+
|
|
6806
7316
|
/**
|
|
6807
7317
|
* @param {Number|String|[Number|String,Number|String]} val
|
|
6808
|
-
* @param {
|
|
7318
|
+
* @param {StaggerParams} params
|
|
6809
7319
|
* @return {StaggerFunction}
|
|
6810
7320
|
*/
|
|
6811
7321
|
const stagger = (val, params = {}) => {
|
|
@@ -6819,22 +7329,29 @@ const stagger = (val, params = {}) => {
|
|
|
6819
7329
|
const staggerEase = hasSpring ? /** @type {Spring} */ (ease).ease : hasEasing ? parseEasings(ease) : null;
|
|
6820
7330
|
const grid = params.grid;
|
|
6821
7331
|
const axis = params.axis;
|
|
7332
|
+
const customTotal = params.total;
|
|
6822
7333
|
const fromFirst = isUnd(from) || from === 0 || from === 'first';
|
|
6823
7334
|
const fromCenter = from === 'center';
|
|
6824
7335
|
const fromLast = from === 'last';
|
|
7336
|
+
const fromRandom = from === 'random';
|
|
6825
7337
|
const isRange = isArr(val);
|
|
7338
|
+
const useProp = params.use;
|
|
6826
7339
|
const val1 = isRange ? parseNumber(val[0]) : parseNumber(val);
|
|
6827
7340
|
const val2 = isRange ? parseNumber(val[1]) : 0;
|
|
6828
7341
|
const unitMatch = unitsExecRgx.exec((isRange ? val[1] : val) + emptyString);
|
|
6829
7342
|
const start = params.start || 0 + (isRange ? val1 : 0);
|
|
6830
7343
|
let fromIndex = fromFirst ? 0 : isNum(from) ? from : 0;
|
|
6831
|
-
return (
|
|
7344
|
+
return (target, i, t, tl) => {
|
|
7345
|
+
const [registeredTarget] = registerTargets(target);
|
|
7346
|
+
const total = isUnd(customTotal) ? t : customTotal;
|
|
7347
|
+
const customIndex = !isUnd(useProp) ? isFnc(useProp) ? useProp(registeredTarget, i, total) : getOriginalAnimatableValue(registeredTarget, useProp) : false;
|
|
7348
|
+
const staggerIndex = isNum(customIndex) || isStr(customIndex) && isNum(+customIndex) ? +customIndex : i;
|
|
6832
7349
|
if (fromCenter)
|
|
6833
|
-
fromIndex = (
|
|
7350
|
+
fromIndex = (total - 1) / 2;
|
|
6834
7351
|
if (fromLast)
|
|
6835
|
-
fromIndex =
|
|
7352
|
+
fromIndex = total - 1;
|
|
6836
7353
|
if (!values.length) {
|
|
6837
|
-
for (let index = 0; index <
|
|
7354
|
+
for (let index = 0; index < total; index++) {
|
|
6838
7355
|
if (!grid) {
|
|
6839
7356
|
values.push(abs(fromIndex - index));
|
|
6840
7357
|
}
|
|
@@ -6858,11 +7375,13 @@ const stagger = (val, params = {}) => {
|
|
|
6858
7375
|
values = values.map(val => staggerEase(val / maxValue) * maxValue);
|
|
6859
7376
|
if (reversed)
|
|
6860
7377
|
values = values.map(val => axis ? (val < 0) ? val * -1 : -val : abs(maxValue - val));
|
|
7378
|
+
if (fromRandom)
|
|
7379
|
+
values = shuffle(values);
|
|
6861
7380
|
}
|
|
6862
7381
|
const spacing = isRange ? (val2 - val1) / maxValue : val1;
|
|
6863
7382
|
const offset = tl ? parseTimelinePosition(tl, isUnd(params.start) ? tl.iterationDuration : start) : /** @type {Number} */ (start);
|
|
6864
7383
|
/** @type {String|Number} */
|
|
6865
|
-
let output = offset + ((spacing * round(values[
|
|
7384
|
+
let output = offset + ((spacing * round(values[staggerIndex], 2)) || 0);
|
|
6866
7385
|
if (params.modifier)
|
|
6867
7386
|
output = params.modifier(output);
|
|
6868
7387
|
if (unitMatch)
|
|
@@ -6871,4 +7390,4 @@ const stagger = (val, params = {}) => {
|
|
|
6871
7390
|
};
|
|
6872
7391
|
};
|
|
6873
7392
|
|
|
6874
|
-
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 };
|
|
7393
|
+
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 };
|