fancoolo-fx 1.7.1 → 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/package.json +1 -1
- package/readme.txt +12 -0
- package/src/fx.js +76 -141
package/package.json
CHANGED
package/readme.txt
CHANGED
|
@@ -68,6 +68,18 @@ 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
|
+
|
|
71
83
|
= 1.7.1 =
|
|
72
84
|
* Fix: FOUC prevention — all effects now use autoAlpha instead of opacity, elements start with visibility:hidden and are revealed by GSAP
|
|
73
85
|
* New: WordPress plugin injects visibility:hidden CSS automatically in the head
|
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,90 +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;
|
|
208
171
|
|
|
209
172
|
gsap.set(el, { visibility: 'inherit' });
|
|
210
|
-
var split = new SplitText(el, { type: 'lines', linesClass: 'line-wrapper' });
|
|
211
|
-
|
|
212
|
-
split.lines.forEach(function (line) {
|
|
213
|
-
var wrapper = document.createElement('div');
|
|
214
|
-
wrapper.style.overflow = 'hidden';
|
|
215
|
-
line.parentNode.insertBefore(wrapper, line);
|
|
216
|
-
wrapper.appendChild(line);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
220
|
-
var entry = { el: el, split: split, tween: null, effectFn: textReveal, opts: opts };
|
|
221
|
-
|
|
222
|
-
var tweenVars = {
|
|
223
|
-
y: '100%',
|
|
224
|
-
autoAlpha: 0,
|
|
225
|
-
duration: o.duration,
|
|
226
|
-
ease: o.ease,
|
|
227
|
-
stagger: o.stagger,
|
|
228
|
-
delay: o.delay,
|
|
229
|
-
};
|
|
230
173
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
+
};
|
|
237
187
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
188
|
+
if (isScroll) {
|
|
189
|
+
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
190
|
+
}
|
|
241
191
|
|
|
242
|
-
|
|
243
|
-
|
|
192
|
+
return gsap.from(self.lines, tweenVars);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
244
195
|
}
|
|
245
196
|
|
|
246
197
|
function reveal(el, opts) {
|
|
@@ -400,6 +351,11 @@
|
|
|
400
351
|
opts = opts || {};
|
|
401
352
|
var o = resolveOptions(el, 'tiltIn', opts);
|
|
402
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
|
+
|
|
403
359
|
gsap.fromTo(el, {
|
|
404
360
|
rotationX: opts.rotationX != null ? opts.rotationX : 45,
|
|
405
361
|
scale: opts.scale != null ? opts.scale : 0.8,
|
|
@@ -412,12 +368,7 @@
|
|
|
412
368
|
autoAlpha: 1,
|
|
413
369
|
transformPerspective: 1000,
|
|
414
370
|
ease: o.ease,
|
|
415
|
-
scrollTrigger:
|
|
416
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
417
|
-
start: config.scrollStart || 'top 85%',
|
|
418
|
-
end: opts.end || 'top 20%',
|
|
419
|
-
scrub: opts.scrub != null ? opts.scrub : 0.6,
|
|
420
|
-
},
|
|
371
|
+
scrollTrigger: st,
|
|
421
372
|
});
|
|
422
373
|
}
|
|
423
374
|
|
|
@@ -429,9 +380,6 @@
|
|
|
429
380
|
var split = new SplitText(el, { type: 'chars' });
|
|
430
381
|
gsap.set(split.chars, { autoAlpha: 0 });
|
|
431
382
|
|
|
432
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
433
|
-
var entry = { el: el, split: split, tween: null, effectFn: typeWriter, opts: opts };
|
|
434
|
-
|
|
435
383
|
var tweenVars = {
|
|
436
384
|
autoAlpha: 1,
|
|
437
385
|
duration: o.duration,
|
|
@@ -440,19 +388,11 @@
|
|
|
440
388
|
delay: o.delay,
|
|
441
389
|
};
|
|
442
390
|
|
|
443
|
-
if (isOneShot) {
|
|
444
|
-
tweenVars.onComplete = function () {
|
|
445
|
-
split.revert();
|
|
446
|
-
unregisterSplit(entry);
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
391
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
451
392
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
452
393
|
}
|
|
453
394
|
|
|
454
|
-
|
|
455
|
-
registerSplit(entry);
|
|
395
|
+
gsap.to(split.chars, tweenVars);
|
|
456
396
|
}
|
|
457
397
|
|
|
458
398
|
function drawSVG(el, opts) {
|
|
@@ -476,15 +416,15 @@
|
|
|
476
416
|
// Scrub mode: SVG draws as user scrolls (class fx-scrub-[0.6] or opts.scrub)
|
|
477
417
|
var scrubVal = getClassModifier(el, 'scrub', opts.scrub != null ? opts.scrub : null);
|
|
478
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
|
+
|
|
479
424
|
gsap.to(paths, {
|
|
480
425
|
strokeDashoffset: 0,
|
|
481
426
|
ease: o.ease,
|
|
482
|
-
scrollTrigger:
|
|
483
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
484
|
-
start: config.scrollStart || 'top 85%',
|
|
485
|
-
end: opts.end || 'top 20%',
|
|
486
|
-
scrub: scrubVal === true || scrubVal === 'true' ? true : scrubVal,
|
|
487
|
-
},
|
|
427
|
+
scrollTrigger: st,
|
|
488
428
|
});
|
|
489
429
|
return;
|
|
490
430
|
}
|
|
@@ -508,17 +448,17 @@
|
|
|
508
448
|
// Read y from modifier class fx-y-[80] or opts or default 50
|
|
509
449
|
var yShift = getClassModifier(el, 'y', opts.y != null ? opts.y : 50);
|
|
510
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
|
+
|
|
511
456
|
gsap.fromTo(el, {
|
|
512
457
|
y: -yShift,
|
|
513
458
|
}, {
|
|
514
459
|
y: yShift,
|
|
515
460
|
ease: 'none',
|
|
516
|
-
scrollTrigger:
|
|
517
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
518
|
-
start: config.scrollStart || 'top 85%',
|
|
519
|
-
end: opts.end || 'bottom top',
|
|
520
|
-
scrub: opts.scrub != null ? opts.scrub : true,
|
|
521
|
-
},
|
|
461
|
+
scrollTrigger: st,
|
|
522
462
|
});
|
|
523
463
|
}
|
|
524
464
|
|
|
@@ -529,9 +469,6 @@
|
|
|
529
469
|
gsap.set(el, { visibility: 'inherit' });
|
|
530
470
|
var split = new SplitText(el, { type: 'words' });
|
|
531
471
|
|
|
532
|
-
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
533
|
-
var entry = { el: el, split: split, tween: null, effectFn: splitWords, opts: opts };
|
|
534
|
-
|
|
535
472
|
var tweenVars = {
|
|
536
473
|
y: opts.y != null ? opts.y : 30,
|
|
537
474
|
autoAlpha: 0,
|
|
@@ -541,19 +478,11 @@
|
|
|
541
478
|
delay: o.delay,
|
|
542
479
|
};
|
|
543
480
|
|
|
544
|
-
if (isOneShot) {
|
|
545
|
-
tweenVars.onComplete = function () {
|
|
546
|
-
split.revert();
|
|
547
|
-
unregisterSplit(entry);
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
|
|
551
481
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
552
482
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
553
483
|
}
|
|
554
484
|
|
|
555
|
-
|
|
556
|
-
registerSplit(entry);
|
|
485
|
+
gsap.from(split.words, tweenVars);
|
|
557
486
|
}
|
|
558
487
|
|
|
559
488
|
function slideIn(el, opts) {
|
|
@@ -650,7 +579,6 @@
|
|
|
650
579
|
// ── Init ────────────────────────────────────
|
|
651
580
|
|
|
652
581
|
function init() {
|
|
653
|
-
var processed = new Set();
|
|
654
582
|
|
|
655
583
|
Object.keys(effects).forEach(function (name) {
|
|
656
584
|
var fn = effects[name];
|
|
@@ -660,8 +588,9 @@
|
|
|
660
588
|
plGroups.forEach(function (group) {
|
|
661
589
|
group = group.filter(function (el) { return !isExcluded(el); });
|
|
662
590
|
group.forEach(function (el, i) {
|
|
591
|
+
if (_animated.has(el)) return;
|
|
663
592
|
fn(el, { delay: i * 0.15 });
|
|
664
|
-
|
|
593
|
+
_animated.add(el);
|
|
665
594
|
});
|
|
666
595
|
});
|
|
667
596
|
|
|
@@ -673,12 +602,13 @@
|
|
|
673
602
|
stGroups.forEach(function (group) {
|
|
674
603
|
group = group.filter(function (el) { return !isExcluded(el); });
|
|
675
604
|
group.forEach(function (el, i) {
|
|
605
|
+
if (_animated.has(el)) return;
|
|
676
606
|
fn(el, {
|
|
677
607
|
trigger: 'scroll',
|
|
678
608
|
delay: i * 0.15,
|
|
679
609
|
scrollTrigger: { trigger: el },
|
|
680
610
|
});
|
|
681
|
-
|
|
611
|
+
_animated.add(el);
|
|
682
612
|
});
|
|
683
613
|
});
|
|
684
614
|
|
|
@@ -688,18 +618,19 @@
|
|
|
688
618
|
if (config.sectionSelector) {
|
|
689
619
|
document.querySelectorAll(config.sectionSelector).forEach(function (section) {
|
|
690
620
|
var bareEls = Array.from(section.querySelectorAll('.' + name))
|
|
691
|
-
.filter(function (el) { return !
|
|
621
|
+
.filter(function (el) { return !_animated.has(el) && !isExcluded(el); });
|
|
692
622
|
if (bareEls.length === 0) return;
|
|
693
623
|
|
|
694
624
|
var groups = groupByParent(bareEls);
|
|
695
625
|
groups.forEach(function (group) {
|
|
696
626
|
group.forEach(function (el, i) {
|
|
627
|
+
if (_animated.has(el)) return;
|
|
697
628
|
fn(el, {
|
|
698
629
|
trigger: 'scroll',
|
|
699
630
|
delay: i * 0.15,
|
|
700
631
|
scrollTrigger: { trigger: el },
|
|
701
632
|
});
|
|
702
|
-
|
|
633
|
+
_animated.add(el);
|
|
703
634
|
});
|
|
704
635
|
});
|
|
705
636
|
});
|
|
@@ -708,21 +639,21 @@
|
|
|
708
639
|
|
|
709
640
|
// 4. Scrub-based effects — always scroll-linked, processed before tagMap.
|
|
710
641
|
document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
|
|
711
|
-
if (!
|
|
642
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
712
643
|
tiltIn(el);
|
|
713
|
-
|
|
644
|
+
_animated.add(el);
|
|
714
645
|
}
|
|
715
646
|
});
|
|
716
647
|
document.querySelectorAll('.fx-parallax-st, .fx-parallax-pl, .fx-parallax').forEach(function (el) {
|
|
717
|
-
if (!
|
|
648
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
718
649
|
parallax(el);
|
|
719
|
-
|
|
650
|
+
_animated.add(el);
|
|
720
651
|
}
|
|
721
652
|
});
|
|
722
653
|
document.querySelectorAll('.fx-draw-svg-scrub').forEach(function (el) {
|
|
723
|
-
if (!
|
|
654
|
+
if (!_animated.has(el) && !isExcluded(el)) {
|
|
724
655
|
drawSVG(el, { scrub: getClassModifier(el, 'scrub', 0.6) });
|
|
725
|
-
|
|
656
|
+
_animated.add(el);
|
|
726
657
|
}
|
|
727
658
|
});
|
|
728
659
|
|
|
@@ -735,18 +666,19 @@
|
|
|
735
666
|
if (!fn) return;
|
|
736
667
|
|
|
737
668
|
var els = Array.from(section.querySelectorAll(selector))
|
|
738
|
-
.filter(function (el) { return !
|
|
669
|
+
.filter(function (el) { return !_animated.has(el) && !isExcluded(el); });
|
|
739
670
|
if (els.length === 0) return;
|
|
740
671
|
|
|
741
672
|
var groups = groupByParent(els);
|
|
742
673
|
groups.forEach(function (group) {
|
|
743
674
|
applyScrollGroup(fn, group, section);
|
|
744
|
-
group.forEach(function (el) {
|
|
675
|
+
group.forEach(function (el) { _animated.add(el); });
|
|
745
676
|
});
|
|
746
677
|
});
|
|
747
678
|
});
|
|
748
679
|
}
|
|
749
|
-
|
|
680
|
+
|
|
681
|
+
// 6. fx-stagger-all-[selector] — target children, effect from sibling class
|
|
750
682
|
// Requires an effect class on the same element (e.g. fx-reveal-st).
|
|
751
683
|
document.querySelectorAll('[class*="fx-stagger-all-"]').forEach(function (container) {
|
|
752
684
|
// Parse selector from fx-stagger-all-[img,p]
|
|
@@ -776,7 +708,7 @@
|
|
|
776
708
|
var isScroll = container.classList.contains(effectName + '-st') ||
|
|
777
709
|
container.classList.contains(effectName);
|
|
778
710
|
var children = Array.from(container.querySelectorAll(childSelector))
|
|
779
|
-
.filter(function (el) { return !
|
|
711
|
+
.filter(function (el) { return !_animated.has(el); });
|
|
780
712
|
if (children.length === 0) return;
|
|
781
713
|
|
|
782
714
|
children.forEach(function (child, i) {
|
|
@@ -786,7 +718,7 @@
|
|
|
786
718
|
opts.scrollTrigger = { trigger: child };
|
|
787
719
|
}
|
|
788
720
|
effectFn(child, opts);
|
|
789
|
-
|
|
721
|
+
_animated.add(child);
|
|
790
722
|
});
|
|
791
723
|
});
|
|
792
724
|
|
|
@@ -811,17 +743,20 @@
|
|
|
811
743
|
function boot() {
|
|
812
744
|
applyPreConfig();
|
|
813
745
|
|
|
814
|
-
//
|
|
815
|
-
|
|
816
|
-
|
|
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)');
|
|
817
750
|
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
if (config.disableMobile && window.innerWidth <= config.mobileBreakpoint) {
|
|
821
|
-
return;
|
|
751
|
+
if (config.respectReducedMotion) {
|
|
752
|
+
parts.push('(prefers-reduced-motion: no-preference)');
|
|
822
753
|
}
|
|
754
|
+
var conditions = parts.length > 0 ? parts.join(' and ') : 'all';
|
|
823
755
|
|
|
824
|
-
|
|
756
|
+
_mm.add(conditions, function () {
|
|
757
|
+
_animated = new WeakSet();
|
|
758
|
+
init();
|
|
759
|
+
});
|
|
825
760
|
}
|
|
826
761
|
|
|
827
762
|
if (document.readyState === 'loading') {
|
|
@@ -850,6 +785,6 @@
|
|
|
850
785
|
splitWords: splitWords,
|
|
851
786
|
slideIn: slideIn,
|
|
852
787
|
init: init,
|
|
853
|
-
refresh:
|
|
788
|
+
refresh: function () { ScrollTrigger.refresh(); },
|
|
854
789
|
};
|
|
855
790
|
})();
|