animejs 4.3.5 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/README.md +9 -12
  2. package/dist/bundles/anime.esm.js +1048 -421
  3. package/dist/bundles/anime.esm.min.js +2 -2
  4. package/dist/bundles/anime.umd.js +1053 -421
  5. package/dist/bundles/anime.umd.min.js +2 -2
  6. package/dist/modules/animatable/animatable.cjs +1 -1
  7. package/dist/modules/animatable/animatable.js +2 -2
  8. package/dist/modules/animatable/index.cjs +1 -1
  9. package/dist/modules/animatable/index.js +1 -1
  10. package/dist/modules/animation/additive.cjs +1 -1
  11. package/dist/modules/animation/additive.js +1 -1
  12. package/dist/modules/animation/animation.cjs +38 -16
  13. package/dist/modules/animation/animation.d.ts +2 -2
  14. package/dist/modules/animation/animation.js +42 -20
  15. package/dist/modules/animation/composition.cjs +1 -1
  16. package/dist/modules/animation/composition.js +3 -3
  17. package/dist/modules/animation/index.cjs +1 -1
  18. package/dist/modules/animation/index.js +1 -1
  19. package/dist/modules/core/clock.cjs +1 -1
  20. package/dist/modules/core/clock.js +1 -1
  21. package/dist/modules/core/colors.cjs +1 -1
  22. package/dist/modules/core/colors.js +1 -1
  23. package/dist/modules/core/consts.cjs +3 -9
  24. package/dist/modules/core/consts.d.ts +1 -5
  25. package/dist/modules/core/consts.js +4 -8
  26. package/dist/modules/core/globals.cjs +16 -5
  27. package/dist/modules/core/globals.d.ts +22 -1
  28. package/dist/modules/core/globals.js +18 -6
  29. package/dist/modules/core/helpers.cjs +7 -10
  30. package/dist/modules/core/helpers.js +8 -11
  31. package/dist/modules/core/render.cjs +66 -63
  32. package/dist/modules/core/render.js +68 -65
  33. package/dist/modules/core/styles.cjs +53 -32
  34. package/dist/modules/core/styles.d.ts +1 -0
  35. package/dist/modules/core/styles.js +55 -35
  36. package/dist/modules/core/targets.cjs +1 -1
  37. package/dist/modules/core/targets.js +1 -1
  38. package/dist/modules/core/transforms.cjs +129 -13
  39. package/dist/modules/core/transforms.d.ts +1 -0
  40. package/dist/modules/core/transforms.js +130 -15
  41. package/dist/modules/core/units.cjs +1 -1
  42. package/dist/modules/core/units.js +1 -1
  43. package/dist/modules/core/values.cjs +68 -8
  44. package/dist/modules/core/values.d.ts +5 -2
  45. package/dist/modules/core/values.js +69 -11
  46. package/dist/modules/draggable/draggable.cjs +1 -1
  47. package/dist/modules/draggable/draggable.js +1 -1
  48. package/dist/modules/draggable/index.cjs +1 -1
  49. package/dist/modules/draggable/index.js +1 -1
  50. package/dist/modules/easings/cubic-bezier/index.cjs +1 -1
  51. package/dist/modules/easings/cubic-bezier/index.js +1 -1
  52. package/dist/modules/easings/eases/index.cjs +1 -1
  53. package/dist/modules/easings/eases/index.js +1 -1
  54. package/dist/modules/easings/eases/parser.cjs +1 -1
  55. package/dist/modules/easings/eases/parser.js +1 -1
  56. package/dist/modules/easings/index.cjs +1 -1
  57. package/dist/modules/easings/index.js +1 -1
  58. package/dist/modules/easings/irregular/index.cjs +1 -1
  59. package/dist/modules/easings/irregular/index.js +1 -1
  60. package/dist/modules/easings/linear/index.cjs +1 -1
  61. package/dist/modules/easings/linear/index.js +1 -1
  62. package/dist/modules/easings/none.cjs +1 -1
  63. package/dist/modules/easings/none.js +1 -1
  64. package/dist/modules/easings/spring/index.cjs +1 -1
  65. package/dist/modules/easings/spring/index.js +1 -1
  66. package/dist/modules/easings/steps/index.cjs +1 -1
  67. package/dist/modules/easings/steps/index.js +1 -1
  68. package/dist/modules/engine/engine.cjs +1 -1
  69. package/dist/modules/engine/engine.js +1 -1
  70. package/dist/modules/engine/index.cjs +1 -1
  71. package/dist/modules/engine/index.js +1 -1
  72. package/dist/modules/events/index.cjs +1 -1
  73. package/dist/modules/events/index.js +1 -1
  74. package/dist/modules/events/scroll.cjs +1 -1
  75. package/dist/modules/events/scroll.js +1 -1
  76. package/dist/modules/index.cjs +9 -1
  77. package/dist/modules/index.d.ts +1 -0
  78. package/dist/modules/index.js +4 -1
  79. package/dist/modules/layout/index.cjs +1 -1
  80. package/dist/modules/layout/index.js +1 -1
  81. package/dist/modules/layout/layout.cjs +47 -24
  82. package/dist/modules/layout/layout.d.ts +4 -3
  83. package/dist/modules/layout/layout.js +48 -25
  84. package/dist/modules/scope/index.cjs +1 -1
  85. package/dist/modules/scope/index.js +1 -1
  86. package/dist/modules/scope/scope.cjs +1 -1
  87. package/dist/modules/scope/scope.js +1 -1
  88. package/dist/modules/svg/drawable.cjs +1 -1
  89. package/dist/modules/svg/drawable.js +1 -1
  90. package/dist/modules/svg/helpers.cjs +1 -1
  91. package/dist/modules/svg/helpers.js +1 -1
  92. package/dist/modules/svg/index.cjs +1 -1
  93. package/dist/modules/svg/index.js +1 -1
  94. package/dist/modules/svg/morphto.cjs +3 -6
  95. package/dist/modules/svg/morphto.js +3 -6
  96. package/dist/modules/svg/motionpath.cjs +1 -1
  97. package/dist/modules/svg/motionpath.js +1 -1
  98. package/dist/modules/text/index.cjs +3 -1
  99. package/dist/modules/text/index.d.ts +1 -0
  100. package/dist/modules/text/index.js +2 -1
  101. package/dist/modules/text/scramble.cjs +272 -0
  102. package/dist/modules/text/scramble.d.ts +3 -0
  103. package/dist/modules/text/scramble.js +270 -0
  104. package/dist/modules/text/split.cjs +5 -5
  105. package/dist/modules/text/split.d.ts +5 -5
  106. package/dist/modules/text/split.js +5 -5
  107. package/dist/modules/timeline/index.cjs +1 -1
  108. package/dist/modules/timeline/index.js +1 -1
  109. package/dist/modules/timeline/position.cjs +1 -1
  110. package/dist/modules/timeline/position.js +1 -1
  111. package/dist/modules/timeline/timeline.cjs +36 -18
  112. package/dist/modules/timeline/timeline.d.ts +6 -5
  113. package/dist/modules/timeline/timeline.js +37 -19
  114. package/dist/modules/timer/index.cjs +1 -1
  115. package/dist/modules/timer/index.js +1 -1
  116. package/dist/modules/timer/timer.cjs +8 -12
  117. package/dist/modules/timer/timer.d.ts +2 -0
  118. package/dist/modules/timer/timer.js +9 -13
  119. package/dist/modules/types/index.d.ts +76 -8
  120. package/dist/modules/utils/chainable.cjs +8 -5
  121. package/dist/modules/utils/chainable.js +8 -5
  122. package/dist/modules/utils/index.cjs +5 -1
  123. package/dist/modules/utils/index.d.ts +1 -0
  124. package/dist/modules/utils/index.js +2 -1
  125. package/dist/modules/utils/number.cjs +1 -1
  126. package/dist/modules/utils/number.js +1 -1
  127. package/dist/modules/utils/random.cjs +1 -1
  128. package/dist/modules/utils/random.js +1 -1
  129. package/dist/modules/utils/stagger.cjs +117 -20
  130. package/dist/modules/utils/stagger.js +118 -21
  131. package/dist/modules/utils/target.cjs +1 -1
  132. package/dist/modules/utils/target.js +1 -1
  133. package/dist/modules/utils/time.cjs +5 -3
  134. package/dist/modules/utils/time.d.ts +1 -1
  135. package/dist/modules/utils/time.js +5 -3
  136. package/dist/modules/waapi/composition.cjs +1 -1
  137. package/dist/modules/waapi/composition.js +1 -1
  138. package/dist/modules/waapi/index.cjs +1 -1
  139. package/dist/modules/waapi/index.js +1 -1
  140. package/dist/modules/waapi/waapi.cjs +19 -20
  141. package/dist/modules/waapi/waapi.js +20 -21
  142. package/package.json +2 -1
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Anime.js - text - CJS
3
+ * @version v4.4.0
4
+ * @license MIT
5
+ * @copyright 2026 - Julian Garnier
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ var random = require('../utils/random.cjs');
11
+ var globals = require('../core/globals.cjs');
12
+ var helpers = require('../core/helpers.cjs');
13
+ var parser = require('../easings/eases/parser.cjs');
14
+ var consts = require('../core/consts.cjs');
15
+
16
+ /**
17
+ * @import {
18
+ * ScrambleTextParams,
19
+ * FunctionValue,
20
+ * } from '../types/index.js'
21
+ */
22
+
23
+ /**
24
+ * '-' is the range operator; place it at the start or end of the string to use it as a literal (e.g. '-abc' or 'abc-')
25
+ * @param {String} str
26
+ * @return {String}
27
+ */
28
+ const expandCharRanges = (str) => {
29
+ let result = '';
30
+ for (let i = 0, l = str.length; i < l; i++) {
31
+ if (i + 2 < l && str[i + 1] === '-' && str.charCodeAt(i) < str.charCodeAt(i + 2)) {
32
+ const start = str.charCodeAt(i);
33
+ const end = str.charCodeAt(i + 2);
34
+ for (let c = start; c <= end; c++) result += String.fromCharCode(c);
35
+ i += 2;
36
+ } else {
37
+ result += str[i];
38
+ }
39
+ }
40
+ return result;
41
+ };
42
+
43
+ const charSets = {
44
+ lowercase: 'a-z',
45
+ uppercase: 'A-Z',
46
+ numbers: '0-9',
47
+ symbols: '!%#_|*+=',
48
+ braille: '⠀-⣿',
49
+ blocks: '▀-▟',
50
+ shades: '░-▓',
51
+ };
52
+
53
+ const originalTexts = new WeakMap();
54
+
55
+ /**
56
+ * Returns a function-based tween value that scrambles the target's text content,
57
+ * progressively revealing the original text.
58
+ *
59
+ * @param {ScrambleTextParams} [params]
60
+ * @return {FunctionValue}
61
+ */
62
+ const scrambleText = (params = {}) => {
63
+ if (!params) params = {};
64
+ const charsParam = params.chars;
65
+ const easeFn = parser.parseEase(params.ease || 'linear');
66
+ const text = params.text;
67
+ const fromParam = params.from;
68
+ const reversed = params.reversed || false;
69
+ const perturbation = params.perturbation || 0;
70
+ const cursorParam = params.cursor;
71
+ const cursorChars = cursorParam === true ? '_'
72
+ : typeof cursorParam === 'number' ? String.fromCharCode(cursorParam)
73
+ : typeof cursorParam === 'string' ? cursorParam
74
+ : '';
75
+ const cursorLen = cursorChars.length;
76
+ const seed = params.seed || 0;
77
+ const override = params.override !== undefined ? params.override : true;
78
+ const revealRate = params.revealRate || 60;
79
+ const interval = 1000 * globals.globals.timeScale / revealRate;
80
+ const settleDuration = params.settleDuration || 300 * globals.globals.timeScale;
81
+ const settleRate = params.settleRate || 30;
82
+ const durationParam = params.duration;
83
+ const revealDelayParam = params.revealDelay;
84
+ const delayParam = params.delay;
85
+ const onChange = params.onChange || consts.noop;
86
+
87
+ return (target, index, targets, prevTween) => {
88
+ const rawChars = typeof charsParam === 'function' ? charsParam(target, index, targets) : (charsParam || 'a-zA-Z0-9!%#_');
89
+ const characters = expandCharRanges(charSets[rawChars] || rawChars);
90
+ const totalChars = characters.length - 1;
91
+ const duration = typeof durationParam === 'function' ? durationParam(target, index, targets) : durationParam;
92
+ const revealDelay = typeof revealDelayParam === 'function' ? revealDelayParam(target, index, targets) : (revealDelayParam || 0);
93
+ const delay = typeof delayParam === 'function' ? delayParam(target, index, targets) : (delayParam || 0);
94
+ const rng = seed ? random.createSeededRandom(seed) : random.createSeededRandom();
95
+ if (!originalTexts.has(target)) originalTexts.set(target, target.textContent);
96
+ const startingText = prevTween ? prevTween._value : target.textContent;
97
+ const targetText = text !== undefined
98
+ ? (typeof text === 'function' ? text(target, index, targets) : text)
99
+ : prevTween ? prevTween._value
100
+ : originalTexts.get(target);
101
+ const settledText = targetText === ' ' || targetText === '&nbsp;' ? ' ' : targetText;
102
+ const startLength = startingText === ' ' ? 0 : startingText.length;
103
+ const endLength = settledText.length;
104
+ const overrideChars = override === true ? characters
105
+ : typeof override === 'string' && override.length > 0 ? expandCharRanges(charSets[/** @type {String} */(override)] || /** @type {String} */(override))
106
+ : null;
107
+ const totalOverrideChars = overrideChars ? overrideChars.length - 1 : 0;
108
+ // Space override uses &nbsp; so the browser doesn't collapse consecutive spaces in innerHTML
109
+ const overrideChar = override === ' ' ? ' ' : null;
110
+ // When starting from blank, only animate the target text length to avoid padding beyond it
111
+ const animLength = override === '' ? endLength : Math.max(startLength, endLength);
112
+ // Compute total duration from interval spacing and settle time, or use the explicit duration
113
+ const animDuration = duration > 0 ? duration : (animLength - 1) * interval + settleDuration;
114
+ const computedDuration = helpers.round((animDuration + revealDelay) / globals.globals.timeScale, 0) * globals.globals.timeScale;
115
+ const revealDelayRatio = revealDelay > 0 ? helpers.round(revealDelay / computedDuration, 12) : 0;
116
+ // Auto-resolve reveal direction: shrinking text reveals from right, growing from left
117
+ const resolvedFrom = fromParam === undefined || fromParam === 'auto' ? (endLength < startLength ? 'right' : 'left') : fromParam;
118
+ const charOrder = new Int32Array(animLength);
119
+ if (resolvedFrom === 'random') {
120
+ for (let i = 0; i < animLength; i++) charOrder[i] = i;
121
+ for (let i = animLength - 1; i > 0; i--) {
122
+ const j = rng(0, i);
123
+ const t = charOrder[i]; charOrder[i] = charOrder[j]; charOrder[j] = t;
124
+ }
125
+ } else {
126
+ const ref = resolvedFrom === 'right' ? (override === '' || !startLength ? animLength : startLength) - 1
127
+ : resolvedFrom === 'center' ? ((override === '' || !startLength ? animLength : startLength) - 1) / 2
128
+ : typeof resolvedFrom === 'number' ? resolvedFrom
129
+ : 0;
130
+ const abs = Math.abs;
131
+ const indices = new Array(animLength);
132
+ for (let i = 0; i < animLength; i++) indices[i] = i;
133
+ indices.sort((a, b) => abs(a - ref) - abs(b - ref));
134
+ for (let i = 0; i < animLength; i++) charOrder[indices[i]] = i;
135
+ }
136
+ if (reversed) {
137
+ const last = animLength - 1;
138
+ for (let i = 0; i < animLength; i++) charOrder[i] = last - charOrder[i];
139
+ }
140
+ // settleRatio is the fraction of the animation each character spends in the active scrambling zone
141
+ const settleRatio = helpers.round(settleDuration / animDuration, 12);
142
+ // settleSpacing is the time gap between consecutive characters entering the active zone
143
+ const settleSpacing = helpers.round((1 - settleRatio) / animLength, 12);
144
+ const cursorZone = cursorLen * settleSpacing;
145
+ // stepRatio controls how often scramble characters refresh (based on settleRate)
146
+ const stepRatio = helpers.round(1000 * globals.globals.timeScale / (settleRate * computedDuration), 12);
147
+ // Pre-compute per-character start and settle times
148
+ const charStarts = new Float32Array(animLength);
149
+ const charEnds = new Float32Array(animLength);
150
+ const scale = perturbation > 0 ? perturbation * settleRatio : 0;
151
+ for (let c = 0; c < animLength; c++) {
152
+ const so = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
153
+ const eo = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
154
+ charStarts[c] = charOrder[c] * settleSpacing + so;
155
+ charEnds[c] = Math.ceil((charStarts[c] + settleRatio + eo) / stepRatio) * stepRatio;
156
+ }
157
+ // When text shrinks with non-sequential from modes, delay target settle times past all extras
158
+ if (endLength < animLength && resolvedFrom !== 'left' && resolvedFrom !== 'right' && resolvedFrom !== 'random') {
159
+ let maxExtraEnd = 0;
160
+ for (let c = endLength; c < animLength; c++) {
161
+ if (charEnds[c] > maxExtraEnd) maxExtraEnd = charEnds[c];
162
+ }
163
+ const targets = new Array(endLength);
164
+ for (let c = 0; c < endLength; c++) targets[c] = c;
165
+ targets.sort((a, b) => charOrder[a] - charOrder[b]);
166
+ const targetSpacing = (1 - maxExtraEnd) / endLength;
167
+ for (let i = 0; i < endLength; i++) {
168
+ const revealTime = maxExtraEnd + i * targetSpacing;
169
+ if (revealTime > charEnds[targets[i]]) {
170
+ charEnds[targets[i]] = revealTime;
171
+ }
172
+ }
173
+ }
174
+ // charCache holds the current scramble character for each position, refreshed at settleRate
175
+ const charCache = new Array(animLength);
176
+ for (let c = 0; c < animLength; c++) {
177
+ charCache[c] = characters[rng(0, totalChars)];
178
+ }
179
+ // overrideCache holds scramble characters for the starting text (override: true or custom string)
180
+ const overrideCache = overrideChars ? (overrideChars === characters ? charCache : new Array(animLength)) : null;
181
+ if (overrideCache && overrideCache !== charCache) {
182
+ for (let c = 0; c < animLength; c++) {
183
+ overrideCache[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, overrideChars.length - 1)];
184
+ }
185
+ }
186
+ // Build the initial display text based on override mode
187
+ let fillStartText = startingText;
188
+ if (!prevTween) {
189
+ if (override === '') {
190
+ fillStartText = '';
191
+ } else if (overrideChars) {
192
+ fillStartText = '';
193
+ for (let c = 0; c < startLength; c++) {
194
+ fillStartText += startingText[c] === ' ' ? ' ' : /** @type {Array<String>} */(overrideCache)[c];
195
+ }
196
+ }
197
+ }
198
+
199
+ let lastValue = -1;
200
+ let lastStep = -1;
201
+ let scrambled = '';
202
+ const hasOverride = override !== '';
203
+ const hasOverrideChars = !!overrideChars;
204
+ const hasCursor = cursorLen > 0;
205
+
206
+ return {
207
+ from: 0,
208
+ to: 1,
209
+ duration: computedDuration,
210
+ delay: delay,
211
+ ease: 'linear',
212
+ modifier: (v) => {
213
+ if (v === lastValue) return scrambled;
214
+ lastValue = v;
215
+ if (delay > 0 && v <= 0) { scrambled = startingText; return startingText; }
216
+ if (v <= 0) { scrambled = fillStartText; return fillStartText; }
217
+ if (v >= 1) { scrambled = settledText; return settledText; }
218
+ scrambled = '';
219
+ // Only refresh scramble characters when we cross a settleRate step boundary
220
+ const currentStep = (v / stepRatio) | 0;
221
+ const refreshChars = currentStep !== lastStep;
222
+ if (refreshChars) lastStep = currentStep;
223
+ // Subtract delay ratio to get the effective animation progress
224
+ const linear = revealDelayRatio > 0 ? (v - revealDelayRatio) / (1 - revealDelayRatio) : v;
225
+ const t = linear > 0 ? easeFn(linear) : 0;
226
+ for (let c = 0; c < animLength; c++) {
227
+ // Each character has its own start/end window based on its reveal order
228
+ const charStart = charStarts[c];
229
+ const charEnd = charEnds[c];
230
+ // Settled zone: character has finished its transition
231
+ if (t >= charEnd) {
232
+ if (c < endLength) scrambled += settledText[c];
233
+ continue;
234
+ }
235
+ // Pre-transition zone: reveal wave hasn't reached this character yet
236
+ if (t <= 0 || t < charStart) {
237
+ if (hasOverride && c < startLength) {
238
+ if (hasOverrideChars) {
239
+ if (startingText[c] === ' ') {
240
+ scrambled += ' ';
241
+ } else {
242
+ if (refreshChars) /** @type {Array<String>} */(overrideCache)[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, totalOverrideChars)];
243
+ scrambled += /** @type {Array<String>} */(overrideCache)[c];
244
+ }
245
+ } else {
246
+ // Default (override: false): show the original starting text
247
+ scrambled += startingText[c];
248
+ }
249
+ }
250
+ continue;
251
+ }
252
+ // Active zone: character is between charStart and charEnd
253
+ const isSpace = (c < endLength && settledText[c] === ' ') || (c < startLength && startingText[c] === ' ');
254
+ if (isSpace) {
255
+ scrambled += ' ';
256
+ } else if (hasCursor && t - charStart < cursorZone) {
257
+ // Cursor sub-zone: show cursor character based on position within cursor width
258
+ scrambled += cursorChars[cursorLen - 1 - (((t - charStart) / settleSpacing) | 0)];
259
+ } else {
260
+ // Scramble zone: show cycling random characters
261
+ if (refreshChars) charCache[c] = characters[rng(0, totalChars)];
262
+ scrambled += charCache[c];
263
+ }
264
+ }
265
+ if (refreshChars) onChange(scrambled, t);
266
+ return scrambled;
267
+ }
268
+ }
269
+ }
270
+ };
271
+
272
+ exports.scrambleText = scrambleText;
@@ -0,0 +1,3 @@
1
+ export function scrambleText(params?: ScrambleTextParams): FunctionValue;
2
+ import type { ScrambleTextParams } from '../types/index.js';
3
+ import type { FunctionValue } from '../types/index.js';
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Anime.js - text - ESM
3
+ * @version v4.4.0
4
+ * @license MIT
5
+ * @copyright 2026 - Julian Garnier
6
+ */
7
+
8
+ import { createSeededRandom } from '../utils/random.js';
9
+ import { globals } from '../core/globals.js';
10
+ import { round } from '../core/helpers.js';
11
+ import { parseEase } from '../easings/eases/parser.js';
12
+ import { noop } from '../core/consts.js';
13
+
14
+ /**
15
+ * @import {
16
+ * ScrambleTextParams,
17
+ * FunctionValue,
18
+ * } from '../types/index.js'
19
+ */
20
+
21
+ /**
22
+ * '-' is the range operator; place it at the start or end of the string to use it as a literal (e.g. '-abc' or 'abc-')
23
+ * @param {String} str
24
+ * @return {String}
25
+ */
26
+ const expandCharRanges = (str) => {
27
+ let result = '';
28
+ for (let i = 0, l = str.length; i < l; i++) {
29
+ if (i + 2 < l && str[i + 1] === '-' && str.charCodeAt(i) < str.charCodeAt(i + 2)) {
30
+ const start = str.charCodeAt(i);
31
+ const end = str.charCodeAt(i + 2);
32
+ for (let c = start; c <= end; c++) result += String.fromCharCode(c);
33
+ i += 2;
34
+ } else {
35
+ result += str[i];
36
+ }
37
+ }
38
+ return result;
39
+ };
40
+
41
+ const charSets = {
42
+ lowercase: 'a-z',
43
+ uppercase: 'A-Z',
44
+ numbers: '0-9',
45
+ symbols: '!%#_|*+=',
46
+ braille: '⠀-⣿',
47
+ blocks: '▀-▟',
48
+ shades: '░-▓',
49
+ };
50
+
51
+ const originalTexts = new WeakMap();
52
+
53
+ /**
54
+ * Returns a function-based tween value that scrambles the target's text content,
55
+ * progressively revealing the original text.
56
+ *
57
+ * @param {ScrambleTextParams} [params]
58
+ * @return {FunctionValue}
59
+ */
60
+ const scrambleText = (params = {}) => {
61
+ if (!params) params = {};
62
+ const charsParam = params.chars;
63
+ const easeFn = parseEase(params.ease || 'linear');
64
+ const text = params.text;
65
+ const fromParam = params.from;
66
+ const reversed = params.reversed || false;
67
+ const perturbation = params.perturbation || 0;
68
+ const cursorParam = params.cursor;
69
+ const cursorChars = cursorParam === true ? '_'
70
+ : typeof cursorParam === 'number' ? String.fromCharCode(cursorParam)
71
+ : typeof cursorParam === 'string' ? cursorParam
72
+ : '';
73
+ const cursorLen = cursorChars.length;
74
+ const seed = params.seed || 0;
75
+ const override = params.override !== undefined ? params.override : true;
76
+ const revealRate = params.revealRate || 60;
77
+ const interval = 1000 * globals.timeScale / revealRate;
78
+ const settleDuration = params.settleDuration || 300 * globals.timeScale;
79
+ const settleRate = params.settleRate || 30;
80
+ const durationParam = params.duration;
81
+ const revealDelayParam = params.revealDelay;
82
+ const delayParam = params.delay;
83
+ const onChange = params.onChange || noop;
84
+
85
+ return (target, index, targets, prevTween) => {
86
+ const rawChars = typeof charsParam === 'function' ? charsParam(target, index, targets) : (charsParam || 'a-zA-Z0-9!%#_');
87
+ const characters = expandCharRanges(charSets[rawChars] || rawChars);
88
+ const totalChars = characters.length - 1;
89
+ const duration = typeof durationParam === 'function' ? durationParam(target, index, targets) : durationParam;
90
+ const revealDelay = typeof revealDelayParam === 'function' ? revealDelayParam(target, index, targets) : (revealDelayParam || 0);
91
+ const delay = typeof delayParam === 'function' ? delayParam(target, index, targets) : (delayParam || 0);
92
+ const rng = seed ? createSeededRandom(seed) : createSeededRandom();
93
+ if (!originalTexts.has(target)) originalTexts.set(target, target.textContent);
94
+ const startingText = prevTween ? prevTween._value : target.textContent;
95
+ const targetText = text !== undefined
96
+ ? (typeof text === 'function' ? text(target, index, targets) : text)
97
+ : prevTween ? prevTween._value
98
+ : originalTexts.get(target);
99
+ const settledText = targetText === ' ' || targetText === '&nbsp;' ? ' ' : targetText;
100
+ const startLength = startingText === ' ' ? 0 : startingText.length;
101
+ const endLength = settledText.length;
102
+ const overrideChars = override === true ? characters
103
+ : typeof override === 'string' && override.length > 0 ? expandCharRanges(charSets[/** @type {String} */(override)] || /** @type {String} */(override))
104
+ : null;
105
+ const totalOverrideChars = overrideChars ? overrideChars.length - 1 : 0;
106
+ // Space override uses &nbsp; so the browser doesn't collapse consecutive spaces in innerHTML
107
+ const overrideChar = override === ' ' ? ' ' : null;
108
+ // When starting from blank, only animate the target text length to avoid padding beyond it
109
+ const animLength = override === '' ? endLength : Math.max(startLength, endLength);
110
+ // Compute total duration from interval spacing and settle time, or use the explicit duration
111
+ const animDuration = duration > 0 ? duration : (animLength - 1) * interval + settleDuration;
112
+ const computedDuration = round((animDuration + revealDelay) / globals.timeScale, 0) * globals.timeScale;
113
+ const revealDelayRatio = revealDelay > 0 ? round(revealDelay / computedDuration, 12) : 0;
114
+ // Auto-resolve reveal direction: shrinking text reveals from right, growing from left
115
+ const resolvedFrom = fromParam === undefined || fromParam === 'auto' ? (endLength < startLength ? 'right' : 'left') : fromParam;
116
+ const charOrder = new Int32Array(animLength);
117
+ if (resolvedFrom === 'random') {
118
+ for (let i = 0; i < animLength; i++) charOrder[i] = i;
119
+ for (let i = animLength - 1; i > 0; i--) {
120
+ const j = rng(0, i);
121
+ const t = charOrder[i]; charOrder[i] = charOrder[j]; charOrder[j] = t;
122
+ }
123
+ } else {
124
+ const ref = resolvedFrom === 'right' ? (override === '' || !startLength ? animLength : startLength) - 1
125
+ : resolvedFrom === 'center' ? ((override === '' || !startLength ? animLength : startLength) - 1) / 2
126
+ : typeof resolvedFrom === 'number' ? resolvedFrom
127
+ : 0;
128
+ const abs = Math.abs;
129
+ const indices = new Array(animLength);
130
+ for (let i = 0; i < animLength; i++) indices[i] = i;
131
+ indices.sort((a, b) => abs(a - ref) - abs(b - ref));
132
+ for (let i = 0; i < animLength; i++) charOrder[indices[i]] = i;
133
+ }
134
+ if (reversed) {
135
+ const last = animLength - 1;
136
+ for (let i = 0; i < animLength; i++) charOrder[i] = last - charOrder[i];
137
+ }
138
+ // settleRatio is the fraction of the animation each character spends in the active scrambling zone
139
+ const settleRatio = round(settleDuration / animDuration, 12);
140
+ // settleSpacing is the time gap between consecutive characters entering the active zone
141
+ const settleSpacing = round((1 - settleRatio) / animLength, 12);
142
+ const cursorZone = cursorLen * settleSpacing;
143
+ // stepRatio controls how often scramble characters refresh (based on settleRate)
144
+ const stepRatio = round(1000 * globals.timeScale / (settleRate * computedDuration), 12);
145
+ // Pre-compute per-character start and settle times
146
+ const charStarts = new Float32Array(animLength);
147
+ const charEnds = new Float32Array(animLength);
148
+ const scale = perturbation > 0 ? perturbation * settleRatio : 0;
149
+ for (let c = 0; c < animLength; c++) {
150
+ const so = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
151
+ const eo = scale > 0 ? (rng(0, 2000) - 1000) / 1000 * scale : 0;
152
+ charStarts[c] = charOrder[c] * settleSpacing + so;
153
+ charEnds[c] = Math.ceil((charStarts[c] + settleRatio + eo) / stepRatio) * stepRatio;
154
+ }
155
+ // When text shrinks with non-sequential from modes, delay target settle times past all extras
156
+ if (endLength < animLength && resolvedFrom !== 'left' && resolvedFrom !== 'right' && resolvedFrom !== 'random') {
157
+ let maxExtraEnd = 0;
158
+ for (let c = endLength; c < animLength; c++) {
159
+ if (charEnds[c] > maxExtraEnd) maxExtraEnd = charEnds[c];
160
+ }
161
+ const targets = new Array(endLength);
162
+ for (let c = 0; c < endLength; c++) targets[c] = c;
163
+ targets.sort((a, b) => charOrder[a] - charOrder[b]);
164
+ const targetSpacing = (1 - maxExtraEnd) / endLength;
165
+ for (let i = 0; i < endLength; i++) {
166
+ const revealTime = maxExtraEnd + i * targetSpacing;
167
+ if (revealTime > charEnds[targets[i]]) {
168
+ charEnds[targets[i]] = revealTime;
169
+ }
170
+ }
171
+ }
172
+ // charCache holds the current scramble character for each position, refreshed at settleRate
173
+ const charCache = new Array(animLength);
174
+ for (let c = 0; c < animLength; c++) {
175
+ charCache[c] = characters[rng(0, totalChars)];
176
+ }
177
+ // overrideCache holds scramble characters for the starting text (override: true or custom string)
178
+ const overrideCache = overrideChars ? (overrideChars === characters ? charCache : new Array(animLength)) : null;
179
+ if (overrideCache && overrideCache !== charCache) {
180
+ for (let c = 0; c < animLength; c++) {
181
+ overrideCache[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, overrideChars.length - 1)];
182
+ }
183
+ }
184
+ // Build the initial display text based on override mode
185
+ let fillStartText = startingText;
186
+ if (!prevTween) {
187
+ if (override === '') {
188
+ fillStartText = '';
189
+ } else if (overrideChars) {
190
+ fillStartText = '';
191
+ for (let c = 0; c < startLength; c++) {
192
+ fillStartText += startingText[c] === ' ' ? ' ' : /** @type {Array<String>} */(overrideCache)[c];
193
+ }
194
+ }
195
+ }
196
+
197
+ let lastValue = -1;
198
+ let lastStep = -1;
199
+ let scrambled = '';
200
+ const hasOverride = override !== '';
201
+ const hasOverrideChars = !!overrideChars;
202
+ const hasCursor = cursorLen > 0;
203
+
204
+ return {
205
+ from: 0,
206
+ to: 1,
207
+ duration: computedDuration,
208
+ delay: delay,
209
+ ease: 'linear',
210
+ modifier: (v) => {
211
+ if (v === lastValue) return scrambled;
212
+ lastValue = v;
213
+ if (delay > 0 && v <= 0) { scrambled = startingText; return startingText; }
214
+ if (v <= 0) { scrambled = fillStartText; return fillStartText; }
215
+ if (v >= 1) { scrambled = settledText; return settledText; }
216
+ scrambled = '';
217
+ // Only refresh scramble characters when we cross a settleRate step boundary
218
+ const currentStep = (v / stepRatio) | 0;
219
+ const refreshChars = currentStep !== lastStep;
220
+ if (refreshChars) lastStep = currentStep;
221
+ // Subtract delay ratio to get the effective animation progress
222
+ const linear = revealDelayRatio > 0 ? (v - revealDelayRatio) / (1 - revealDelayRatio) : v;
223
+ const t = linear > 0 ? easeFn(linear) : 0;
224
+ for (let c = 0; c < animLength; c++) {
225
+ // Each character has its own start/end window based on its reveal order
226
+ const charStart = charStarts[c];
227
+ const charEnd = charEnds[c];
228
+ // Settled zone: character has finished its transition
229
+ if (t >= charEnd) {
230
+ if (c < endLength) scrambled += settledText[c];
231
+ continue;
232
+ }
233
+ // Pre-transition zone: reveal wave hasn't reached this character yet
234
+ if (t <= 0 || t < charStart) {
235
+ if (hasOverride && c < startLength) {
236
+ if (hasOverrideChars) {
237
+ if (startingText[c] === ' ') {
238
+ scrambled += ' ';
239
+ } else {
240
+ if (refreshChars) /** @type {Array<String>} */(overrideCache)[c] = overrideChar || /** @type {String} */(overrideChars)[rng(0, totalOverrideChars)];
241
+ scrambled += /** @type {Array<String>} */(overrideCache)[c];
242
+ }
243
+ } else {
244
+ // Default (override: false): show the original starting text
245
+ scrambled += startingText[c];
246
+ }
247
+ }
248
+ continue;
249
+ }
250
+ // Active zone: character is between charStart and charEnd
251
+ const isSpace = (c < endLength && settledText[c] === ' ') || (c < startLength && startingText[c] === ' ');
252
+ if (isSpace) {
253
+ scrambled += ' ';
254
+ } else if (hasCursor && t - charStart < cursorZone) {
255
+ // Cursor sub-zone: show cursor character based on position within cursor width
256
+ scrambled += cursorChars[cursorLen - 1 - (((t - charStart) / settleSpacing) | 0)];
257
+ } else {
258
+ // Scramble zone: show cycling random characters
259
+ if (refreshChars) charCache[c] = characters[rng(0, totalChars)];
260
+ scrambled += charCache[c];
261
+ }
262
+ }
263
+ if (refreshChars) onChange(scrambled, t);
264
+ return scrambled;
265
+ }
266
+ }
267
+ }
268
+ };
269
+
270
+ export { scrambleText };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - text - CJS
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -197,7 +197,7 @@ const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, d
197
197
  */
198
198
  class TextSplitter {
199
199
  /**
200
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
200
+ * @param {Element|NodeList|String|Array<Element>} target
201
201
  * @param {TextSplitterParams} [parameters]
202
202
  */
203
203
  constructor(target, parameters = {}) {
@@ -269,11 +269,11 @@ class TextSplitter {
269
269
  }
270
270
 
271
271
  /**
272
- * @param {(...args: any[]) => Tickable | (() => void)} effect
272
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
273
273
  * @return this
274
274
  */
275
275
  addEffect(effect) {
276
- if (!helpers.isFnc(effect)) return console.warn('Effect must return a function.');
276
+ if (!helpers.isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
277
277
  const refreshableEffect = time.keepTime(effect);
278
278
  this.effects.push(refreshableEffect);
279
279
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -479,7 +479,7 @@ class TextSplitter {
479
479
  }
480
480
 
481
481
  /**
482
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
482
+ * @param {Element|NodeList|String|Array<Element>} target
483
483
  * @param {TextSplitterParams} [parameters]
484
484
  * @return {TextSplitter}
485
485
  */
@@ -4,10 +4,10 @@
4
4
  */
5
5
  export class TextSplitter {
6
6
  /**
7
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
7
+ * @param {Element|NodeList|String|Array<Element>} target
8
8
  * @param {TextSplitterParams} [parameters]
9
9
  */
10
- constructor(target: HTMLElement | NodeList | string | Array<HTMLElement>, parameters?: TextSplitterParams);
10
+ constructor(target: Element | NodeList | string | Array<Element>, parameters?: TextSplitterParams);
11
11
  debug: boolean;
12
12
  includeSpaces: boolean;
13
13
  accessible: boolean;
@@ -31,10 +31,10 @@ export class TextSplitter {
31
31
  resizeTimeout: NodeJS.Timeout;
32
32
  resizeObserver: ResizeObserver;
33
33
  /**
34
- * @param {(...args: any[]) => Tickable | (() => void)} effect
34
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
35
35
  * @return this
36
36
  */
37
- addEffect(effect: (...args: any[]) => Tickable | (() => void)): void | this;
37
+ addEffect(effect: (...args: any[]) => Tickable | (() => void) | void): this;
38
38
  revert(): this;
39
39
  /**
40
40
  * Recursively processes a node and its children
@@ -48,7 +48,7 @@ export class TextSplitter {
48
48
  split(clearCache?: boolean): this;
49
49
  refresh(): void;
50
50
  }
51
- export function splitText(target: HTMLElement | NodeList | string | Array<HTMLElement>, parameters?: TextSplitterParams): TextSplitter;
51
+ export function splitText(target: Element | NodeList | string | Array<Element>, parameters?: TextSplitterParams): TextSplitter;
52
52
  export function split(target: HTMLElement | NodeList | string | Array<HTMLElement>, parameters?: TextSplitterParams): TextSplitter;
53
53
  export type Segment = {
54
54
  segment: string;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - text - ESM
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -195,7 +195,7 @@ const processHTMLTemplate = (htmlTemplate, store, node, $parentFragment, type, d
195
195
  */
196
196
  class TextSplitter {
197
197
  /**
198
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
198
+ * @param {Element|NodeList|String|Array<Element>} target
199
199
  * @param {TextSplitterParams} [parameters]
200
200
  */
201
201
  constructor(target, parameters = {}) {
@@ -267,11 +267,11 @@ class TextSplitter {
267
267
  }
268
268
 
269
269
  /**
270
- * @param {(...args: any[]) => Tickable | (() => void)} effect
270
+ * @param {(...args: any[]) => Tickable | (() => void) | void} effect
271
271
  * @return this
272
272
  */
273
273
  addEffect(effect) {
274
- if (!isFnc(effect)) return console.warn('Effect must return a function.');
274
+ if (!isFnc(effect)) { console.warn('Effect must return a function.'); return this; }
275
275
  const refreshableEffect = keepTime(effect);
276
276
  this.effects.push(refreshableEffect);
277
277
  if (this.ready) this.effectsCleanups[this.effects.length - 1] = refreshableEffect(this);
@@ -477,7 +477,7 @@ class TextSplitter {
477
477
  }
478
478
 
479
479
  /**
480
- * @param {HTMLElement|NodeList|String|Array<HTMLElement>} target
480
+ * @param {Element|NodeList|String|Array<Element>} target
481
481
  * @param {TextSplitterParams} [parameters]
482
482
  * @return {TextSplitter}
483
483
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - timeline - CJS
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Anime.js - timeline - ESM
3
- * @version v4.3.5
3
+ * @version v4.4.0
4
4
  * @license MIT
5
5
  * @copyright 2026 - Julian Garnier
6
6
  */