fancoolo-fx 1.7.0 → 1.8.1
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 +26 -1
- package/package.json +1 -1
- package/readme.txt +18 -0
- package/src/fx.js +95 -154
package/README.md
CHANGED
|
@@ -169,11 +169,36 @@ Set `trigger: 'scroll'` to enable ScrollTrigger. Pass `scrollTrigger: { trigger:
|
|
|
169
169
|
|
|
170
170
|
**Resize handling:** Text-based effects (`textReveal`, `typeWriter`, `splitWords`) automatically re-split when the browser width changes. After one-shot animations complete, the SplitText DOM is reverted so text reflows naturally.
|
|
171
171
|
|
|
172
|
+
## Preventing Flash of Unstyled Content (FOUC)
|
|
173
|
+
|
|
174
|
+
FX uses GSAP's `autoAlpha` internally, so elements with `visibility: hidden` are revealed automatically when their animation starts. Add this CSS **before** any content renders to prevent the flash where elements appear briefly before JS loads:
|
|
175
|
+
|
|
176
|
+
```css
|
|
177
|
+
.fx-text-reveal-pl,.fx-text-reveal-st,.fx-text-reveal,
|
|
178
|
+
.fx-reveal-pl,.fx-reveal-st,.fx-reveal,
|
|
179
|
+
.fx-spin-reveal-pl,.fx-spin-reveal-st,.fx-spin-reveal,
|
|
180
|
+
.fx-bg-reveal-pl,.fx-bg-reveal-st,.fx-bg-reveal,
|
|
181
|
+
.fx-scale-in-pl,.fx-scale-in-st,.fx-scale-in,
|
|
182
|
+
.fx-fade-in-pl,.fx-fade-in-st,.fx-fade-in,
|
|
183
|
+
.fx-blur-in-pl,.fx-blur-in-st,.fx-blur-in,
|
|
184
|
+
.fx-clip-up-pl,.fx-clip-up-st,.fx-clip-up,
|
|
185
|
+
.fx-clip-down-pl,.fx-clip-down-st,.fx-clip-down,
|
|
186
|
+
.fx-tilt-in-st,.fx-tilt-in,
|
|
187
|
+
.fx-type-writer-pl,.fx-type-writer-st,.fx-type-writer,
|
|
188
|
+
.fx-draw-svg-pl,.fx-draw-svg-st,.fx-draw-svg,.fx-draw-svg-scrub,
|
|
189
|
+
.fx-split-words-pl,.fx-split-words-st,.fx-split-words,
|
|
190
|
+
.fx-slide-left-pl,.fx-slide-left-st,.fx-slide-left,
|
|
191
|
+
.fx-slide-right-pl,.fx-slide-right-st,.fx-slide-right{visibility:hidden}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**WordPress:** The plugin injects this CSS automatically in the `<head>` — no action needed.
|
|
195
|
+
|
|
172
196
|
## Using in a New Project
|
|
173
197
|
|
|
174
198
|
1. Copy this repo (or `npm install`)
|
|
175
199
|
2. Add the 4 script tags (gsap, ScrollTrigger, SplitText, fx.js)
|
|
176
|
-
3. Add
|
|
200
|
+
3. Add the FOUC prevention CSS in your `<head>` (see above)
|
|
201
|
+
4. Add `.fx-*` classes in your HTML
|
|
177
202
|
|
|
178
203
|
For compound sequences, create a project-specific JS file loaded after fx.js:
|
|
179
204
|
|
package/package.json
CHANGED
package/readme.txt
CHANGED
|
@@ -68,6 +68,24 @@ Yes. Use the `fx-start-[top center]` modifier class, or set `scrollStart` in the
|
|
|
68
68
|
|
|
69
69
|
== Changelog ==
|
|
70
70
|
|
|
71
|
+
= 1.8.1 =
|
|
72
|
+
* Fix: Removed split.revert() calls from textReveal, typeWriter, and splitWords — revert destroys JS state (event listeners, injected DOM) added after SplitText ran
|
|
73
|
+
* Fix: textReveal resize re-splitting fully handled by autoSplit, no manual revert needed
|
|
74
|
+
|
|
75
|
+
= 1.8.0 =
|
|
76
|
+
* Refactor: textReveal uses native SplitText `autoSplit`, `mask: "lines"`, and `onSplit` — removes manual overflow wrappers and resize handler
|
|
77
|
+
* Refactor: Responsive and reduced-motion handling via `gsap.matchMedia()` — animations auto-revert when conditions change
|
|
78
|
+
* Refactor: Idempotent `init()` using persistent WeakSet — safe to call multiple times without double-animating
|
|
79
|
+
* Refactor: Scrub effects (tiltIn, parallax, drawSVG scrub) routed through `buildScrollTrigger()` — now support debug markers and `fx-start-[...]` overrides
|
|
80
|
+
* Removed: Manual resize listener, `_splitRegistry`, `document.fonts.ready` blocking — all handled natively by GSAP
|
|
81
|
+
* Enhancement: `FX.refresh()` simplified to `ScrollTrigger.refresh()`
|
|
82
|
+
|
|
83
|
+
= 1.7.1 =
|
|
84
|
+
* Fix: FOUC prevention — all effects now use autoAlpha instead of opacity, elements start with visibility:hidden and are revealed by GSAP
|
|
85
|
+
* New: WordPress plugin injects visibility:hidden CSS automatically in the head
|
|
86
|
+
* New: Text-based effects set parent visibility before animating children to prevent flash
|
|
87
|
+
* Enhancement: clipUp/clipDown now include autoAlpha for consistent FOUC handling
|
|
88
|
+
|
|
71
89
|
= 1.7.0 =
|
|
72
90
|
* Fix: Text-based effects (textReveal, typeWriter, splitWords) now re-split on browser resize — line breaks stay correct at every viewport width
|
|
73
91
|
* New: SplitText is reverted after one-shot animations complete — text reflows naturally without extra DOM wrappers
|
package/src/fx.js
CHANGED
|
@@ -109,6 +109,11 @@
|
|
|
109
109
|
slideIn: { duration: 1, ease: 'power3.out' },
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
// ── State ───────────────────────────────────
|
|
113
|
+
|
|
114
|
+
var _animated = new WeakSet();
|
|
115
|
+
var _mm = gsap.matchMedia();
|
|
116
|
+
|
|
112
117
|
// ── Helpers ──────────────────────────────────
|
|
113
118
|
|
|
114
119
|
function getClassModifier(el, name, fallback) {
|
|
@@ -157,89 +162,36 @@
|
|
|
157
162
|
return st;
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
// ── SplitText resize handling ───────────────
|
|
161
|
-
|
|
162
|
-
var _splitRegistry = [];
|
|
163
|
-
var _lastWidth = window.innerWidth;
|
|
164
|
-
var _resizeTimer;
|
|
165
|
-
|
|
166
|
-
function registerSplit(entry) {
|
|
167
|
-
_splitRegistry.push(entry);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function unregisterSplit(entry) {
|
|
171
|
-
var idx = _splitRegistry.indexOf(entry);
|
|
172
|
-
if (idx > -1) _splitRegistry.splice(idx, 1);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function refreshSplits() {
|
|
176
|
-
if (_splitRegistry.length === 0) return;
|
|
177
|
-
|
|
178
|
-
var pending = [];
|
|
179
|
-
|
|
180
|
-
for (var i = _splitRegistry.length - 1; i >= 0; i--) {
|
|
181
|
-
var entry = _splitRegistry[i];
|
|
182
|
-
if (entry.tween) entry.tween.kill();
|
|
183
|
-
if (entry.split) entry.split.revert();
|
|
184
|
-
pending.push(entry);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
_splitRegistry.length = 0;
|
|
188
|
-
|
|
189
|
-
pending.forEach(function (entry) {
|
|
190
|
-
entry.effectFn(entry.el, entry.opts);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
ScrollTrigger.refresh();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
window.addEventListener('resize', function () {
|
|
197
|
-
if (window.innerWidth === _lastWidth) return;
|
|
198
|
-
_lastWidth = window.innerWidth;
|
|
199
|
-
clearTimeout(_resizeTimer);
|
|
200
|
-
_resizeTimer = setTimeout(refreshSplits, 200);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
165
|
// ── Effects ──────────────────────────────────
|
|
204
166
|
|
|
205
167
|
function textReveal(el, opts) {
|
|
206
168
|
opts = opts || {};
|
|
207
169
|
var o = resolveOptions(el, 'textReveal', opts);
|
|
170
|
+
var isScroll = opts.trigger === 'scroll' || opts.scrollTrigger;
|
|
171
|
+
|
|
172
|
+
gsap.set(el, { visibility: 'inherit' });
|
|
173
|
+
|
|
174
|
+
SplitText.create(el, {
|
|
175
|
+
type: 'lines',
|
|
176
|
+
mask: 'lines',
|
|
177
|
+
autoSplit: true,
|
|
178
|
+
onSplit: function (self) {
|
|
179
|
+
var tweenVars = {
|
|
180
|
+
y: '100%',
|
|
181
|
+
autoAlpha: 0,
|
|
182
|
+
duration: o.duration,
|
|
183
|
+
ease: o.ease,
|
|
184
|
+
stagger: o.stagger,
|
|
185
|
+
delay: o.delay,
|
|
186
|
+
};
|
|
208
187
|
|
|
209
|
-
|
|
188
|
+
if (isScroll) {
|
|
189
|
+
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
190
|
+
}
|
|
210
191
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
wrapper.style.overflow = 'hidden';
|
|
214
|
-
line.parentNode.insertBefore(wrapper, line);
|
|
215
|
-
wrapper.appendChild(line);
|
|
192
|
+
return gsap.from(self.lines, tweenVars);
|
|
193
|
+
},
|
|
216
194
|
});
|
|
217
|
-
|
|
218
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
219
|
-
var entry = { el: el, split: split, tween: null, effectFn: textReveal, opts: opts };
|
|
220
|
-
|
|
221
|
-
var tweenVars = {
|
|
222
|
-
y: '100%',
|
|
223
|
-
opacity: 0,
|
|
224
|
-
duration: o.duration,
|
|
225
|
-
ease: o.ease,
|
|
226
|
-
stagger: o.stagger,
|
|
227
|
-
delay: o.delay,
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
if (isOneShot) {
|
|
231
|
-
tweenVars.onComplete = function () {
|
|
232
|
-
split.revert();
|
|
233
|
-
unregisterSplit(entry);
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
238
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
entry.tween = gsap.from(split.lines, tweenVars);
|
|
242
|
-
registerSplit(entry);
|
|
243
195
|
}
|
|
244
196
|
|
|
245
197
|
function reveal(el, opts) {
|
|
@@ -248,7 +200,7 @@
|
|
|
248
200
|
|
|
249
201
|
var tweenVars = {
|
|
250
202
|
y: opts.y != null ? opts.y : 80,
|
|
251
|
-
|
|
203
|
+
autoAlpha: 0,
|
|
252
204
|
duration: o.duration,
|
|
253
205
|
ease: o.ease,
|
|
254
206
|
delay: o.delay,
|
|
@@ -268,7 +220,7 @@
|
|
|
268
220
|
var tweenVars = {
|
|
269
221
|
rotation: opts.rotation != null ? opts.rotation : -30,
|
|
270
222
|
scale: opts.scale != null ? opts.scale : 0.9,
|
|
271
|
-
|
|
223
|
+
autoAlpha: 0,
|
|
272
224
|
duration: o.duration,
|
|
273
225
|
ease: o.ease,
|
|
274
226
|
delay: o.delay,
|
|
@@ -287,7 +239,7 @@
|
|
|
287
239
|
|
|
288
240
|
var tweenVars = {
|
|
289
241
|
y: '100%',
|
|
290
|
-
|
|
242
|
+
autoAlpha: 0,
|
|
291
243
|
duration: o.duration,
|
|
292
244
|
ease: o.ease,
|
|
293
245
|
delay: o.delay,
|
|
@@ -306,7 +258,7 @@
|
|
|
306
258
|
|
|
307
259
|
var tweenVars = {
|
|
308
260
|
scale: opts.scale != null ? opts.scale : 0.92,
|
|
309
|
-
|
|
261
|
+
autoAlpha: 0,
|
|
310
262
|
duration: o.duration,
|
|
311
263
|
ease: o.ease,
|
|
312
264
|
delay: o.delay,
|
|
@@ -324,7 +276,7 @@
|
|
|
324
276
|
var o = resolveOptions(el, 'fadeIn', opts);
|
|
325
277
|
|
|
326
278
|
var tweenVars = {
|
|
327
|
-
|
|
279
|
+
autoAlpha: 0,
|
|
328
280
|
scale: opts.scale != null ? opts.scale : 0.95,
|
|
329
281
|
duration: o.duration,
|
|
330
282
|
ease: o.ease,
|
|
@@ -344,7 +296,7 @@
|
|
|
344
296
|
|
|
345
297
|
var tweenVars = {
|
|
346
298
|
filter: 'blur(' + (opts.blur != null ? opts.blur : 12) + 'px)',
|
|
347
|
-
|
|
299
|
+
autoAlpha: 0,
|
|
348
300
|
duration: o.duration,
|
|
349
301
|
ease: o.ease,
|
|
350
302
|
delay: o.delay,
|
|
@@ -363,6 +315,7 @@
|
|
|
363
315
|
|
|
364
316
|
var tweenVars = {
|
|
365
317
|
clipPath: 'inset(100% 0 0 0)',
|
|
318
|
+
autoAlpha: 0,
|
|
366
319
|
duration: o.duration,
|
|
367
320
|
ease: o.ease,
|
|
368
321
|
delay: o.delay,
|
|
@@ -381,6 +334,7 @@
|
|
|
381
334
|
|
|
382
335
|
var tweenVars = {
|
|
383
336
|
clipPath: 'inset(0 0 100% 0)',
|
|
337
|
+
autoAlpha: 0,
|
|
384
338
|
duration: o.duration,
|
|
385
339
|
ease: o.ease,
|
|
386
340
|
delay: o.delay,
|
|
@@ -397,24 +351,24 @@
|
|
|
397
351
|
opts = opts || {};
|
|
398
352
|
var o = resolveOptions(el, 'tiltIn', opts);
|
|
399
353
|
|
|
354
|
+
var st = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
355
|
+
st.end = opts.end || 'top 20%';
|
|
356
|
+
st.scrub = opts.scrub != null ? opts.scrub : 0.6;
|
|
357
|
+
delete st.once;
|
|
358
|
+
|
|
400
359
|
gsap.fromTo(el, {
|
|
401
360
|
rotationX: opts.rotationX != null ? opts.rotationX : 45,
|
|
402
361
|
scale: opts.scale != null ? opts.scale : 0.8,
|
|
403
|
-
|
|
362
|
+
autoAlpha: opts.opacity != null ? opts.opacity : 0,
|
|
404
363
|
transformPerspective: opts.perspective != null ? opts.perspective : 1000,
|
|
405
364
|
transformOrigin: opts.transformOrigin || 'center bottom',
|
|
406
365
|
}, {
|
|
407
366
|
rotationX: 0,
|
|
408
367
|
scale: 1,
|
|
409
|
-
|
|
368
|
+
autoAlpha: 1,
|
|
410
369
|
transformPerspective: 1000,
|
|
411
370
|
ease: o.ease,
|
|
412
|
-
scrollTrigger:
|
|
413
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
414
|
-
start: config.scrollStart || 'top 85%',
|
|
415
|
-
end: opts.end || 'top 20%',
|
|
416
|
-
scrub: opts.scrub != null ? opts.scrub : 0.6,
|
|
417
|
-
},
|
|
371
|
+
scrollTrigger: st,
|
|
418
372
|
});
|
|
419
373
|
}
|
|
420
374
|
|
|
@@ -422,38 +376,29 @@
|
|
|
422
376
|
opts = opts || {};
|
|
423
377
|
var o = resolveOptions(el, 'typeWriter', opts);
|
|
424
378
|
|
|
379
|
+
gsap.set(el, { visibility: 'inherit' });
|
|
425
380
|
var split = new SplitText(el, { type: 'chars' });
|
|
426
|
-
gsap.set(split.chars, {
|
|
427
|
-
|
|
428
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
429
|
-
var entry = { el: el, split: split, tween: null, effectFn: typeWriter, opts: opts };
|
|
381
|
+
gsap.set(split.chars, { autoAlpha: 0 });
|
|
430
382
|
|
|
431
383
|
var tweenVars = {
|
|
432
|
-
|
|
384
|
+
autoAlpha: 1,
|
|
433
385
|
duration: o.duration,
|
|
434
386
|
ease: o.ease,
|
|
435
387
|
stagger: o.stagger,
|
|
436
388
|
delay: o.delay,
|
|
437
389
|
};
|
|
438
390
|
|
|
439
|
-
if (isOneShot) {
|
|
440
|
-
tweenVars.onComplete = function () {
|
|
441
|
-
split.revert();
|
|
442
|
-
unregisterSplit(entry);
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
391
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
447
392
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
448
393
|
}
|
|
449
394
|
|
|
450
|
-
|
|
451
|
-
registerSplit(entry);
|
|
395
|
+
gsap.to(split.chars, tweenVars);
|
|
452
396
|
}
|
|
453
397
|
|
|
454
398
|
function drawSVG(el, opts) {
|
|
455
399
|
opts = opts || {};
|
|
456
400
|
var o = resolveOptions(el, 'drawSVG', opts);
|
|
401
|
+
gsap.set(el, { visibility: 'inherit' });
|
|
457
402
|
|
|
458
403
|
var paths = el.tagName === 'path' || el.tagName === 'line' || el.tagName === 'circle' || el.tagName === 'polyline'
|
|
459
404
|
? [el]
|
|
@@ -471,15 +416,15 @@
|
|
|
471
416
|
// Scrub mode: SVG draws as user scrolls (class fx-scrub-[0.6] or opts.scrub)
|
|
472
417
|
var scrubVal = getClassModifier(el, 'scrub', opts.scrub != null ? opts.scrub : null);
|
|
473
418
|
if (scrubVal !== null) {
|
|
419
|
+
var st = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
420
|
+
st.end = opts.end || 'top 20%';
|
|
421
|
+
st.scrub = scrubVal === true || scrubVal === 'true' ? true : scrubVal;
|
|
422
|
+
delete st.once;
|
|
423
|
+
|
|
474
424
|
gsap.to(paths, {
|
|
475
425
|
strokeDashoffset: 0,
|
|
476
426
|
ease: o.ease,
|
|
477
|
-
scrollTrigger:
|
|
478
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
479
|
-
start: config.scrollStart || 'top 85%',
|
|
480
|
-
end: opts.end || 'top 20%',
|
|
481
|
-
scrub: scrubVal === true || scrubVal === 'true' ? true : scrubVal,
|
|
482
|
-
},
|
|
427
|
+
scrollTrigger: st,
|
|
483
428
|
});
|
|
484
429
|
return;
|
|
485
430
|
}
|
|
@@ -503,17 +448,17 @@
|
|
|
503
448
|
// Read y from modifier class fx-y-[80] or opts or default 50
|
|
504
449
|
var yShift = getClassModifier(el, 'y', opts.y != null ? opts.y : 50);
|
|
505
450
|
|
|
451
|
+
var st = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
452
|
+
st.end = opts.end || 'bottom top';
|
|
453
|
+
st.scrub = opts.scrub != null ? opts.scrub : true;
|
|
454
|
+
delete st.once;
|
|
455
|
+
|
|
506
456
|
gsap.fromTo(el, {
|
|
507
457
|
y: -yShift,
|
|
508
458
|
}, {
|
|
509
459
|
y: yShift,
|
|
510
460
|
ease: 'none',
|
|
511
|
-
scrollTrigger:
|
|
512
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
513
|
-
start: config.scrollStart || 'top 85%',
|
|
514
|
-
end: opts.end || 'bottom top',
|
|
515
|
-
scrub: opts.scrub != null ? opts.scrub : true,
|
|
516
|
-
},
|
|
461
|
+
scrollTrigger: st,
|
|
517
462
|
});
|
|
518
463
|
}
|
|
519
464
|
|
|
@@ -521,33 +466,23 @@
|
|
|
521
466
|
opts = opts || {};
|
|
522
467
|
var o = resolveOptions(el, 'splitWords', opts);
|
|
523
468
|
|
|
469
|
+
gsap.set(el, { visibility: 'inherit' });
|
|
524
470
|
var split = new SplitText(el, { type: 'words' });
|
|
525
471
|
|
|
526
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
527
|
-
var entry = { el: el, split: split, tween: null, effectFn: splitWords, opts: opts };
|
|
528
|
-
|
|
529
472
|
var tweenVars = {
|
|
530
473
|
y: opts.y != null ? opts.y : 30,
|
|
531
|
-
|
|
474
|
+
autoAlpha: 0,
|
|
532
475
|
duration: o.duration,
|
|
533
476
|
ease: o.ease,
|
|
534
477
|
stagger: o.stagger,
|
|
535
478
|
delay: o.delay,
|
|
536
479
|
};
|
|
537
480
|
|
|
538
|
-
if (isOneShot) {
|
|
539
|
-
tweenVars.onComplete = function () {
|
|
540
|
-
split.revert();
|
|
541
|
-
unregisterSplit(entry);
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
|
|
545
481
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
546
482
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
547
483
|
}
|
|
548
484
|
|
|
549
|
-
|
|
550
|
-
registerSplit(entry);
|
|
485
|
+
gsap.from(split.words, tweenVars);
|
|
551
486
|
}
|
|
552
487
|
|
|
553
488
|
function slideIn(el, opts) {
|
|
@@ -558,7 +493,7 @@
|
|
|
558
493
|
|
|
559
494
|
var tweenVars = {
|
|
560
495
|
x: direction === 'left' ? -xVal : xVal,
|
|
561
|
-
|
|
496
|
+
autoAlpha: 0,
|
|
562
497
|
duration: o.duration,
|
|
563
498
|
ease: o.ease,
|
|
564
499
|
delay: o.delay,
|
|
@@ -644,7 +579,6 @@
|
|
|
644
579
|
// ── Init ────────────────────────────────────
|
|
645
580
|
|
|
646
581
|
function init() {
|
|
647
|
-
var processed = new Set();
|
|
648
582
|
|
|
649
583
|
Object.keys(effects).forEach(function (name) {
|
|
650
584
|
var fn = effects[name];
|
|
@@ -654,8 +588,9 @@
|
|
|
654
588
|
plGroups.forEach(function (group) {
|
|
655
589
|
group = group.filter(function (el) { return !isExcluded(el); });
|
|
656
590
|
group.forEach(function (el, i) {
|
|
591
|
+
if (_animated.has(el)) return;
|
|
657
592
|
fn(el, { delay: i * 0.15 });
|
|
658
|
-
|
|
593
|
+
_animated.add(el);
|
|
659
594
|
});
|
|
660
595
|
});
|
|
661
596
|
|
|
@@ -667,12 +602,13 @@
|
|
|
667
602
|
stGroups.forEach(function (group) {
|
|
668
603
|
group = group.filter(function (el) { return !isExcluded(el); });
|
|
669
604
|
group.forEach(function (el, i) {
|
|
605
|
+
if (_animated.has(el)) return;
|
|
670
606
|
fn(el, {
|
|
671
607
|
trigger: 'scroll',
|
|
672
608
|
delay: i * 0.15,
|
|
673
609
|
scrollTrigger: { trigger: el },
|
|
674
610
|
});
|
|
675
|
-
|
|
611
|
+
_animated.add(el);
|
|
676
612
|
});
|
|
677
613
|
});
|
|
678
614
|
|
|
@@ -682,18 +618,19 @@
|
|
|
682
618
|
if (config.sectionSelector) {
|
|
683
619
|
document.querySelectorAll(config.sectionSelector).forEach(function (section) {
|
|
684
620
|
var bareEls = Array.from(section.querySelectorAll('.' + name))
|
|
685
|
-
.filter(function (el) { return !
|
|
621
|
+
.filter(function (el) { return !_animated.has(el) && !isExcluded(el); });
|
|
686
622
|
if (bareEls.length === 0) return;
|
|
687
623
|
|
|
688
624
|
var groups = groupByParent(bareEls);
|
|
689
625
|
groups.forEach(function (group) {
|
|
690
626
|
group.forEach(function (el, i) {
|
|
627
|
+
if (_animated.has(el)) return;
|
|
691
628
|
fn(el, {
|
|
692
629
|
trigger: 'scroll',
|
|
693
630
|
delay: i * 0.15,
|
|
694
631
|
scrollTrigger: { trigger: el },
|
|
695
632
|
});
|
|
696
|
-
|
|
633
|
+
_animated.add(el);
|
|
697
634
|
});
|
|
698
635
|
});
|
|
699
636
|
});
|
|
@@ -702,21 +639,21 @@
|
|
|
702
639
|
|
|
703
640
|
// 4. Scrub-based effects — always scroll-linked, processed before tagMap.
|
|
704
641
|
document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
|
|
705
|
-
if (!
|
|
642
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
706
643
|
tiltIn(el);
|
|
707
|
-
|
|
644
|
+
_animated.add(el);
|
|
708
645
|
}
|
|
709
646
|
});
|
|
710
647
|
document.querySelectorAll('.fx-parallax-st, .fx-parallax-pl, .fx-parallax').forEach(function (el) {
|
|
711
|
-
if (!
|
|
648
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
712
649
|
parallax(el);
|
|
713
|
-
|
|
650
|
+
_animated.add(el);
|
|
714
651
|
}
|
|
715
652
|
});
|
|
716
653
|
document.querySelectorAll('.fx-draw-svg-scrub').forEach(function (el) {
|
|
717
|
-
if (!
|
|
654
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
718
655
|
drawSVG(el, { scrub: getClassModifier(el, 'scrub', 0.6) });
|
|
719
|
-
|
|
656
|
+
_animated.add(el);
|
|
720
657
|
}
|
|
721
658
|
});
|
|
722
659
|
|
|
@@ -729,18 +666,19 @@
|
|
|
729
666
|
if (!fn) return;
|
|
730
667
|
|
|
731
668
|
var els = Array.from(section.querySelectorAll(selector))
|
|
732
|
-
.filter(function (el) { return !
|
|
669
|
+
.filter(function (el) { return !_animated.has(el) && !isExcluded(el); });
|
|
733
670
|
if (els.length === 0) return;
|
|
734
671
|
|
|
735
672
|
var groups = groupByParent(els);
|
|
736
673
|
groups.forEach(function (group) {
|
|
737
674
|
applyScrollGroup(fn, group, section);
|
|
738
|
-
group.forEach(function (el) {
|
|
675
|
+
group.forEach(function (el) { _animated.add(el); });
|
|
739
676
|
});
|
|
740
677
|
});
|
|
741
678
|
});
|
|
742
679
|
}
|
|
743
|
-
|
|
680
|
+
|
|
681
|
+
// 6. fx-stagger-all-[selector] — target children, effect from sibling class
|
|
744
682
|
// Requires an effect class on the same element (e.g. fx-reveal-st).
|
|
745
683
|
document.querySelectorAll('[class*="fx-stagger-all-"]').forEach(function (container) {
|
|
746
684
|
// Parse selector from fx-stagger-all-[img,p]
|
|
@@ -770,7 +708,7 @@
|
|
|
770
708
|
var isScroll = container.classList.contains(effectName + '-st') ||
|
|
771
709
|
container.classList.contains(effectName);
|
|
772
710
|
var children = Array.from(container.querySelectorAll(childSelector))
|
|
773
|
-
.filter(function (el) { return !
|
|
711
|
+
.filter(function (el) { return !_animated.has(el); });
|
|
774
712
|
if (children.length === 0) return;
|
|
775
713
|
|
|
776
714
|
children.forEach(function (child, i) {
|
|
@@ -780,7 +718,7 @@
|
|
|
780
718
|
opts.scrollTrigger = { trigger: child };
|
|
781
719
|
}
|
|
782
720
|
effectFn(child, opts);
|
|
783
|
-
|
|
721
|
+
_animated.add(child);
|
|
784
722
|
});
|
|
785
723
|
});
|
|
786
724
|
|
|
@@ -805,17 +743,20 @@
|
|
|
805
743
|
function boot() {
|
|
806
744
|
applyPreConfig();
|
|
807
745
|
|
|
808
|
-
//
|
|
809
|
-
|
|
810
|
-
|
|
746
|
+
// Build media query from config — animations auto-revert when conditions stop matching
|
|
747
|
+
var parts = [];
|
|
748
|
+
if (config.disableMobile) {
|
|
749
|
+
parts.push('(min-width: ' + (config.mobileBreakpoint + 1) + 'px)');
|
|
811
750
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
if (config.disableMobile && window.innerWidth <= config.mobileBreakpoint) {
|
|
815
|
-
return;
|
|
751
|
+
if (config.respectReducedMotion) {
|
|
752
|
+
parts.push('(prefers-reduced-motion: no-preference)');
|
|
816
753
|
}
|
|
754
|
+
var conditions = parts.length > 0 ? parts.join(' and ') : 'all';
|
|
817
755
|
|
|
818
|
-
|
|
756
|
+
_mm.add(conditions, function () {
|
|
757
|
+
_animated = new WeakSet();
|
|
758
|
+
init();
|
|
759
|
+
});
|
|
819
760
|
}
|
|
820
761
|
|
|
821
762
|
if (document.readyState === 'loading') {
|
|
@@ -844,6 +785,6 @@
|
|
|
844
785
|
splitWords: splitWords,
|
|
845
786
|
slideIn: slideIn,
|
|
846
787
|
init: init,
|
|
847
|
-
refresh:
|
|
788
|
+
refresh: function () { ScrollTrigger.refresh(); },
|
|
848
789
|
};
|
|
849
790
|
})();
|