fancoolo-fx 1.2.0 → 1.5.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/.distignore +15 -0
- package/README.md +41 -5
- package/package.json +13 -2
- package/src/fx.js +211 -6
- package/.github/workflows/release.yml +0 -27
- package/wp-plugin/fancoolo-fx/assets/ScrollTrigger.min.js +0 -11
- package/wp-plugin/fancoolo-fx/assets/SplitText.min.js +0 -11
- package/wp-plugin/fancoolo-fx/assets/fx.js +0 -573
- package/wp-plugin/fancoolo-fx/assets/gsap.min.js +0 -11
- package/wp-plugin/fancoolo-fx/fancoolo-fx.php +0 -449
- /package/{wp-plugin/fancoolo-fx/readme.txt → readme.txt} +0 -0
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fancoolo FX — A class-driven GSAP animation wrapper
|
|
3
|
-
*
|
|
4
|
-
* Load GSAP + plugins BEFORE this file:
|
|
5
|
-
* <script src="node_modules/gsap/dist/gsap.min.js"></script>
|
|
6
|
-
* <script src="node_modules/gsap/dist/ScrollTrigger.min.js"></script>
|
|
7
|
-
* <script src="node_modules/gsap/dist/SplitText.min.js"></script>
|
|
8
|
-
* <script src="src/fx.js"></script>
|
|
9
|
-
*
|
|
10
|
-
* Three ways to trigger animations:
|
|
11
|
-
*
|
|
12
|
-
* 1. Explicit classes with trigger suffix:
|
|
13
|
-
* .fx-text-reveal-pl (page load)
|
|
14
|
-
* .fx-text-reveal-st (scroll trigger)
|
|
15
|
-
*
|
|
16
|
-
* 2. Bare classes inside <section> — auto scroll-triggered:
|
|
17
|
-
* <section>
|
|
18
|
-
* <h2 class="fx-text-reveal">Auto scroll-triggered by section</h2>
|
|
19
|
-
* </section>
|
|
20
|
-
*
|
|
21
|
-
* 3. Tag-based auto-animation (zero classes):
|
|
22
|
-
* FX.config.tagMap = { 'h1,h2,h3': 'textReveal', 'img': 'reveal' }
|
|
23
|
-
*
|
|
24
|
-
* Modifier classes (Gutenberg-friendly):
|
|
25
|
-
* .fx-duration-[1.5] .fx-delay-[0.3] .fx-stagger-[0.2] .fx-ease-[power2.inOut]
|
|
26
|
-
*
|
|
27
|
-
* JS API:
|
|
28
|
-
* FX.textReveal(el, { trigger: 'scroll', delay: 0.2 })
|
|
29
|
-
*/
|
|
30
|
-
(function () {
|
|
31
|
-
'use strict';
|
|
32
|
-
|
|
33
|
-
if (typeof gsap === 'undefined' || typeof SplitText === 'undefined' || typeof ScrollTrigger === 'undefined') {
|
|
34
|
-
console.error('[FX] Missing dependencies. Load gsap, ScrollTrigger, and SplitText before fx.js');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
gsap.registerPlugin(ScrollTrigger, SplitText);
|
|
39
|
-
|
|
40
|
-
// ── Config ──────────────────────────────────
|
|
41
|
-
|
|
42
|
-
var config = {
|
|
43
|
-
/**
|
|
44
|
-
* CSS selector for section containers.
|
|
45
|
-
* Elements with bare .fx-* classes (no -pl/-st suffix) inside matching
|
|
46
|
-
* containers are auto scroll-triggered using the container as trigger.
|
|
47
|
-
*/
|
|
48
|
-
sectionSelector: 'section',
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Default ScrollTrigger start position.
|
|
52
|
-
* Format: "triggerPosition viewportPosition"
|
|
53
|
-
* Examples: 'top 85%', 'top center', 'top 90%', 'center center'
|
|
54
|
-
* See: https://gsap.com/docs/v3/Plugins/ScrollTrigger/
|
|
55
|
-
*/
|
|
56
|
-
scrollStart: 'top 85%',
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Whether scroll-triggered animations play only once.
|
|
60
|
-
* Set to false to replay every time the element enters the viewport.
|
|
61
|
-
*/
|
|
62
|
-
scrollOnce: true,
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Map of CSS selectors → effect names for zero-class auto-animation.
|
|
66
|
-
* Elements matching these selectors inside sections get animated automatically.
|
|
67
|
-
* Set to null/false to disable. Override before DOMContentLoaded or call FX.init().
|
|
68
|
-
*
|
|
69
|
-
* Example:
|
|
70
|
-
* FX.config.tagMap = {
|
|
71
|
-
* 'h1,h2,h3,h4,h5,h6': 'textReveal',
|
|
72
|
-
* 'p,blockquote': 'textReveal',
|
|
73
|
-
* 'img,video': 'reveal',
|
|
74
|
-
* }
|
|
75
|
-
*/
|
|
76
|
-
tagMap: null,
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// ── Defaults ────────────────────────────────
|
|
80
|
-
|
|
81
|
-
var EFFECT_DEFAULTS = {
|
|
82
|
-
textReveal: { duration: 1.2, ease: 'power3.out', stagger: 0.1 },
|
|
83
|
-
reveal: { duration: 1, ease: 'power3.out' },
|
|
84
|
-
spinReveal: { duration: 1.4, ease: 'power3.out' },
|
|
85
|
-
bgReveal: { duration: 1, ease: 'power3.out' },
|
|
86
|
-
scaleIn: { duration: 1, ease: 'power3.out' },
|
|
87
|
-
fadeIn: { duration: 1.4, ease: 'power1.out' },
|
|
88
|
-
blurIn: { duration: 1.2, ease: 'power2.out' },
|
|
89
|
-
clipUp: { duration: 1, ease: 'power3.inOut' },
|
|
90
|
-
clipDown: { duration: 1, ease: 'power3.inOut' },
|
|
91
|
-
tiltIn: { duration: 1.4, ease: 'power3.out' },
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// ── Helpers ──────────────────────────────────
|
|
95
|
-
|
|
96
|
-
function getClassModifier(el, name, fallback) {
|
|
97
|
-
var prefix = 'fx-' + name + '-[';
|
|
98
|
-
for (var i = 0; i < el.classList.length; i++) {
|
|
99
|
-
var cls = el.classList[i];
|
|
100
|
-
if (cls.indexOf(prefix) === 0 && cls.charAt(cls.length - 1) === ']') {
|
|
101
|
-
var val = cls.slice(prefix.length, -1);
|
|
102
|
-
var num = parseFloat(val);
|
|
103
|
-
return isNaN(num) ? val : num;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return fallback;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function resolveOptions(el, effectName, overrides) {
|
|
110
|
-
var d = EFFECT_DEFAULTS[effectName];
|
|
111
|
-
return {
|
|
112
|
-
duration: getClassModifier(el, 'duration', overrides.duration != null ? overrides.duration : d.duration),
|
|
113
|
-
ease: getClassModifier(el, 'ease', overrides.ease != null ? overrides.ease : d.ease),
|
|
114
|
-
stagger: getClassModifier(el, 'stagger', overrides.stagger != null ? overrides.stagger : (d.stagger || 0)),
|
|
115
|
-
delay: getClassModifier(el, 'delay', overrides.delay != null ? overrides.delay : 0),
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function buildScrollTrigger(el, scrollTriggerOpts) {
|
|
120
|
-
var defaults = {
|
|
121
|
-
start: config.scrollStart,
|
|
122
|
-
once: config.scrollOnce,
|
|
123
|
-
};
|
|
124
|
-
var st = { trigger: scrollTriggerOpts.trigger || el };
|
|
125
|
-
for (var key in defaults) st[key] = defaults[key];
|
|
126
|
-
for (var key2 in scrollTriggerOpts) st[key2] = scrollTriggerOpts[key2];
|
|
127
|
-
|
|
128
|
-
// Per-element override: fx-start-[top center] or fx-start-[top 70%]
|
|
129
|
-
var startOverride = getClassModifier(el, 'start', null);
|
|
130
|
-
if (startOverride !== null) st.start = startOverride;
|
|
131
|
-
|
|
132
|
-
return st;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ── Effects ──────────────────────────────────
|
|
136
|
-
|
|
137
|
-
function textReveal(el, opts) {
|
|
138
|
-
opts = opts || {};
|
|
139
|
-
var o = resolveOptions(el, 'textReveal', opts);
|
|
140
|
-
|
|
141
|
-
var split = new SplitText(el, { type: 'lines', linesClass: 'line-wrapper' });
|
|
142
|
-
|
|
143
|
-
split.lines.forEach(function (line) {
|
|
144
|
-
var wrapper = document.createElement('div');
|
|
145
|
-
wrapper.style.overflow = 'hidden';
|
|
146
|
-
line.parentNode.insertBefore(wrapper, line);
|
|
147
|
-
wrapper.appendChild(line);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
var tweenVars = {
|
|
151
|
-
y: '100%',
|
|
152
|
-
opacity: 0,
|
|
153
|
-
duration: o.duration,
|
|
154
|
-
ease: o.ease,
|
|
155
|
-
stagger: o.stagger,
|
|
156
|
-
delay: o.delay,
|
|
157
|
-
onComplete: function () {
|
|
158
|
-
split.lines.forEach(function (line) {
|
|
159
|
-
line.style.transform = '';
|
|
160
|
-
line.style.opacity = '';
|
|
161
|
-
});
|
|
162
|
-
},
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
166
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
gsap.from(split.lines, tweenVars);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function reveal(el, opts) {
|
|
173
|
-
opts = opts || {};
|
|
174
|
-
var o = resolveOptions(el, 'reveal', opts);
|
|
175
|
-
|
|
176
|
-
var tweenVars = {
|
|
177
|
-
y: opts.y != null ? opts.y : 80,
|
|
178
|
-
opacity: 0,
|
|
179
|
-
duration: o.duration,
|
|
180
|
-
ease: o.ease,
|
|
181
|
-
delay: o.delay,
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
185
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
gsap.from(el, tweenVars);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function spinReveal(el, opts) {
|
|
192
|
-
opts = opts || {};
|
|
193
|
-
var o = resolveOptions(el, 'spinReveal', opts);
|
|
194
|
-
|
|
195
|
-
var tweenVars = {
|
|
196
|
-
rotation: opts.rotation != null ? opts.rotation : -30,
|
|
197
|
-
scale: opts.scale != null ? opts.scale : 0.9,
|
|
198
|
-
opacity: 0,
|
|
199
|
-
duration: o.duration,
|
|
200
|
-
ease: o.ease,
|
|
201
|
-
delay: o.delay,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
205
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
gsap.from(el, tweenVars);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function bgReveal(el, opts) {
|
|
212
|
-
opts = opts || {};
|
|
213
|
-
var o = resolveOptions(el, 'bgReveal', opts);
|
|
214
|
-
|
|
215
|
-
var tweenVars = {
|
|
216
|
-
y: '100%',
|
|
217
|
-
opacity: 0,
|
|
218
|
-
duration: o.duration,
|
|
219
|
-
ease: o.ease,
|
|
220
|
-
delay: o.delay,
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
224
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
gsap.from(el, tweenVars);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function scaleIn(el, opts) {
|
|
231
|
-
opts = opts || {};
|
|
232
|
-
var o = resolveOptions(el, 'scaleIn', opts);
|
|
233
|
-
|
|
234
|
-
var tweenVars = {
|
|
235
|
-
scale: opts.scale != null ? opts.scale : 0.92,
|
|
236
|
-
opacity: 0,
|
|
237
|
-
duration: o.duration,
|
|
238
|
-
ease: o.ease,
|
|
239
|
-
delay: o.delay,
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
243
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
gsap.from(el, tweenVars);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function fadeIn(el, opts) {
|
|
250
|
-
opts = opts || {};
|
|
251
|
-
var o = resolveOptions(el, 'fadeIn', opts);
|
|
252
|
-
|
|
253
|
-
var tweenVars = {
|
|
254
|
-
opacity: 0,
|
|
255
|
-
scale: opts.scale != null ? opts.scale : 0.95,
|
|
256
|
-
duration: o.duration,
|
|
257
|
-
ease: o.ease,
|
|
258
|
-
delay: o.delay,
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
262
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
gsap.from(el, tweenVars);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function blurIn(el, opts) {
|
|
269
|
-
opts = opts || {};
|
|
270
|
-
var o = resolveOptions(el, 'blurIn', opts);
|
|
271
|
-
|
|
272
|
-
var tweenVars = {
|
|
273
|
-
filter: 'blur(' + (opts.blur != null ? opts.blur : 12) + 'px)',
|
|
274
|
-
opacity: 0,
|
|
275
|
-
duration: o.duration,
|
|
276
|
-
ease: o.ease,
|
|
277
|
-
delay: o.delay,
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
281
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
gsap.from(el, tweenVars);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
function clipUp(el, opts) {
|
|
288
|
-
opts = opts || {};
|
|
289
|
-
var o = resolveOptions(el, 'clipUp', opts);
|
|
290
|
-
|
|
291
|
-
var tweenVars = {
|
|
292
|
-
clipPath: 'inset(100% 0 0 0)',
|
|
293
|
-
duration: o.duration,
|
|
294
|
-
ease: o.ease,
|
|
295
|
-
delay: o.delay,
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
299
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
gsap.from(el, tweenVars);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function clipDown(el, opts) {
|
|
306
|
-
opts = opts || {};
|
|
307
|
-
var o = resolveOptions(el, 'clipDown', opts);
|
|
308
|
-
|
|
309
|
-
var tweenVars = {
|
|
310
|
-
clipPath: 'inset(0 0 100% 0)',
|
|
311
|
-
duration: o.duration,
|
|
312
|
-
ease: o.ease,
|
|
313
|
-
delay: o.delay,
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
317
|
-
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
gsap.from(el, tweenVars);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function tiltIn(el, opts) {
|
|
324
|
-
opts = opts || {};
|
|
325
|
-
var o = resolveOptions(el, 'tiltIn', opts);
|
|
326
|
-
|
|
327
|
-
gsap.fromTo(el, {
|
|
328
|
-
rotationX: opts.rotationX != null ? opts.rotationX : 45,
|
|
329
|
-
scale: opts.scale != null ? opts.scale : 0.8,
|
|
330
|
-
opacity: opts.opacity != null ? opts.opacity : 0,
|
|
331
|
-
transformPerspective: opts.perspective != null ? opts.perspective : 1000,
|
|
332
|
-
transformOrigin: opts.transformOrigin || 'center bottom',
|
|
333
|
-
}, {
|
|
334
|
-
rotationX: 0,
|
|
335
|
-
scale: 1,
|
|
336
|
-
opacity: 1,
|
|
337
|
-
transformPerspective: 1000,
|
|
338
|
-
ease: o.ease,
|
|
339
|
-
scrollTrigger: {
|
|
340
|
-
trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
|
|
341
|
-
start: config.scrollStart || 'top 85%',
|
|
342
|
-
end: opts.end || 'top 20%',
|
|
343
|
-
scrub: opts.scrub != null ? opts.scrub : 0.6,
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// ── Class-to-effect mapping ─────────────────
|
|
349
|
-
|
|
350
|
-
var effects = {
|
|
351
|
-
'fx-text-reveal': textReveal,
|
|
352
|
-
'fx-reveal': reveal,
|
|
353
|
-
'fx-spin-reveal': spinReveal,
|
|
354
|
-
'fx-bg-reveal': bgReveal,
|
|
355
|
-
'fx-scale-in': scaleIn,
|
|
356
|
-
'fx-fade-in': fadeIn,
|
|
357
|
-
'fx-blur-in': blurIn,
|
|
358
|
-
'fx-clip-up': clipUp,
|
|
359
|
-
'fx-clip-down': clipDown,
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
var effectsByName = {
|
|
363
|
-
textReveal: textReveal,
|
|
364
|
-
reveal: reveal,
|
|
365
|
-
spinReveal: spinReveal,
|
|
366
|
-
bgReveal: bgReveal,
|
|
367
|
-
scaleIn: scaleIn,
|
|
368
|
-
fadeIn: fadeIn,
|
|
369
|
-
blurIn: blurIn,
|
|
370
|
-
clipUp: clipUp,
|
|
371
|
-
clipDown: clipDown,
|
|
372
|
-
tiltIn: tiltIn,
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
// ── Helpers ──────────────────────────────────
|
|
376
|
-
|
|
377
|
-
function groupByParent(nodeList) {
|
|
378
|
-
var map = new Map();
|
|
379
|
-
nodeList.forEach(function (el) {
|
|
380
|
-
var parent = el.parentElement;
|
|
381
|
-
if (!map.has(parent)) map.set(parent, []);
|
|
382
|
-
map.get(parent).push(el);
|
|
383
|
-
});
|
|
384
|
-
var groups = [];
|
|
385
|
-
map.forEach(function (arr) { groups.push(arr); });
|
|
386
|
-
return groups;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function applyScrollGroup(fn, group, triggerEl) {
|
|
390
|
-
group.forEach(function (el, i) {
|
|
391
|
-
fn(el, {
|
|
392
|
-
trigger: 'scroll',
|
|
393
|
-
delay: i * 0.15,
|
|
394
|
-
scrollTrigger: { trigger: triggerEl },
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function camelToKebab(str) {
|
|
400
|
-
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
// ── Init ────────────────────────────────────
|
|
404
|
-
|
|
405
|
-
function init() {
|
|
406
|
-
var processed = new Set();
|
|
407
|
-
|
|
408
|
-
Object.keys(effects).forEach(function (name) {
|
|
409
|
-
var fn = effects[name];
|
|
410
|
-
|
|
411
|
-
// 1. Page-load variant: .fx-<name>-pl
|
|
412
|
-
var plGroups = groupByParent(document.querySelectorAll('.' + name + '-pl'));
|
|
413
|
-
plGroups.forEach(function (group) {
|
|
414
|
-
group.forEach(function (el, i) {
|
|
415
|
-
fn(el, { delay: i * 0.15 });
|
|
416
|
-
processed.add(el);
|
|
417
|
-
});
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
// 2. Explicit scroll-trigger variant: .fx-<name>-st
|
|
421
|
-
// Each element triggers itself (not the parent), so it works
|
|
422
|
-
// regardless of how deep it sits in the DOM.
|
|
423
|
-
var stEls = document.querySelectorAll('.' + name + '-st');
|
|
424
|
-
var stGroups = groupByParent(stEls);
|
|
425
|
-
stGroups.forEach(function (group) {
|
|
426
|
-
group.forEach(function (el, i) {
|
|
427
|
-
fn(el, {
|
|
428
|
-
trigger: 'scroll',
|
|
429
|
-
delay: i * 0.15,
|
|
430
|
-
scrollTrigger: { trigger: el },
|
|
431
|
-
});
|
|
432
|
-
processed.add(el);
|
|
433
|
-
});
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// 3. Bare class inside a section: .fx-<name> (no suffix)
|
|
437
|
-
// Only elements inside a matching section are picked up,
|
|
438
|
-
// but each element triggers itself (same as -st).
|
|
439
|
-
if (config.sectionSelector) {
|
|
440
|
-
document.querySelectorAll(config.sectionSelector).forEach(function (section) {
|
|
441
|
-
var bareEls = Array.from(section.querySelectorAll('.' + name))
|
|
442
|
-
.filter(function (el) { return !processed.has(el); });
|
|
443
|
-
if (bareEls.length === 0) return;
|
|
444
|
-
|
|
445
|
-
var groups = groupByParent(bareEls);
|
|
446
|
-
groups.forEach(function (group) {
|
|
447
|
-
group.forEach(function (el, i) {
|
|
448
|
-
fn(el, {
|
|
449
|
-
trigger: 'scroll',
|
|
450
|
-
delay: i * 0.15,
|
|
451
|
-
scrollTrigger: { trigger: el },
|
|
452
|
-
});
|
|
453
|
-
processed.add(el);
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
// 4. fx-tilt-in — scrub-based 3D perspective (always scroll-linked)
|
|
461
|
-
// Processed before tagMap so tilt elements aren't grabbed by tagMap first.
|
|
462
|
-
document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
|
|
463
|
-
if (!processed.has(el)) {
|
|
464
|
-
tiltIn(el);
|
|
465
|
-
processed.add(el);
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
// 5. Tag-based auto-animation inside sections
|
|
470
|
-
if (config.tagMap && config.sectionSelector) {
|
|
471
|
-
document.querySelectorAll(config.sectionSelector).forEach(function (section) {
|
|
472
|
-
Object.keys(config.tagMap).forEach(function (selector) {
|
|
473
|
-
var effectName = config.tagMap[selector];
|
|
474
|
-
var fn = effects['fx-' + camelToKebab(effectName)] || effectsByName[effectName];
|
|
475
|
-
if (!fn) return;
|
|
476
|
-
|
|
477
|
-
var els = Array.from(section.querySelectorAll(selector))
|
|
478
|
-
.filter(function (el) { return !processed.has(el); });
|
|
479
|
-
if (els.length === 0) return;
|
|
480
|
-
|
|
481
|
-
var groups = groupByParent(els);
|
|
482
|
-
groups.forEach(function (group) {
|
|
483
|
-
applyScrollGroup(fn, group, section);
|
|
484
|
-
group.forEach(function (el) { processed.add(el); });
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
// 5. fx-stagger-all-[selector] — target children, effect from sibling class
|
|
490
|
-
// Requires an effect class on the same element (e.g. fx-reveal-st).
|
|
491
|
-
document.querySelectorAll('[class*="fx-stagger-all-"]').forEach(function (container) {
|
|
492
|
-
// Parse selector from fx-stagger-all-[img,p]
|
|
493
|
-
var childSelector = null;
|
|
494
|
-
for (var ci = 0; ci < container.classList.length; ci++) {
|
|
495
|
-
var cls = container.classList[ci];
|
|
496
|
-
if (cls.indexOf('fx-stagger-all-[') === 0 && cls.charAt(cls.length - 1) === ']') {
|
|
497
|
-
childSelector = cls.slice('fx-stagger-all-['.length, -1);
|
|
498
|
-
break;
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
if (!childSelector) return;
|
|
502
|
-
|
|
503
|
-
// Find which effect class is on this container
|
|
504
|
-
var effectFn = null;
|
|
505
|
-
var effectName = null;
|
|
506
|
-
Object.keys(effects).forEach(function (name) {
|
|
507
|
-
if (container.classList.contains(name + '-st') ||
|
|
508
|
-
container.classList.contains(name + '-pl') ||
|
|
509
|
-
container.classList.contains(name)) {
|
|
510
|
-
effectFn = effects[name];
|
|
511
|
-
effectName = name;
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
if (!effectFn) return; // No effect class paired — do nothing
|
|
515
|
-
|
|
516
|
-
var isScroll = container.classList.contains(effectName + '-st') ||
|
|
517
|
-
container.classList.contains(effectName);
|
|
518
|
-
var children = Array.from(container.querySelectorAll(childSelector))
|
|
519
|
-
.filter(function (el) { return !processed.has(el); });
|
|
520
|
-
if (children.length === 0) return;
|
|
521
|
-
|
|
522
|
-
children.forEach(function (child, i) {
|
|
523
|
-
var opts = { delay: i * 0.15 };
|
|
524
|
-
if (isScroll) {
|
|
525
|
-
opts.trigger = 'scroll';
|
|
526
|
-
opts.scrollTrigger = { trigger: child };
|
|
527
|
-
}
|
|
528
|
-
effectFn(child, opts);
|
|
529
|
-
processed.add(child);
|
|
530
|
-
});
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// ── Boot ────────────────────────────────────
|
|
536
|
-
|
|
537
|
-
function applyPreConfig() {
|
|
538
|
-
var pre = window.__FX_CONFIG__;
|
|
539
|
-
if (!pre) return;
|
|
540
|
-
if (pre.sectionSelector !== undefined) config.sectionSelector = pre.sectionSelector;
|
|
541
|
-
if (pre.scrollStart !== undefined) config.scrollStart = pre.scrollStart;
|
|
542
|
-
if (pre.scrollOnce !== undefined) config.scrollOnce = pre.scrollOnce;
|
|
543
|
-
if (pre.tagMap !== undefined) config.tagMap = pre.tagMap;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function boot() {
|
|
547
|
-
applyPreConfig();
|
|
548
|
-
init();
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
if (document.readyState === 'loading') {
|
|
552
|
-
document.addEventListener('DOMContentLoaded', boot);
|
|
553
|
-
} else {
|
|
554
|
-
boot();
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// ── Public API ──────────────────────────────
|
|
558
|
-
|
|
559
|
-
window.FX = {
|
|
560
|
-
config: config,
|
|
561
|
-
textReveal: textReveal,
|
|
562
|
-
reveal: reveal,
|
|
563
|
-
spinReveal: spinReveal,
|
|
564
|
-
bgReveal: bgReveal,
|
|
565
|
-
scaleIn: scaleIn,
|
|
566
|
-
fadeIn: fadeIn,
|
|
567
|
-
blurIn: blurIn,
|
|
568
|
-
clipUp: clipUp,
|
|
569
|
-
clipDown: clipDown,
|
|
570
|
-
tiltIn: tiltIn,
|
|
571
|
-
init: init,
|
|
572
|
-
};
|
|
573
|
-
})();
|