fancoolo-fx 1.4.0 → 1.6.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 (2) hide show
  1. package/package.json +7 -2
  2. package/src/fx.js +208 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fancoolo-fx",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "A class-driven GSAP animation wrapper for WordPress and static sites.",
5
5
  "main": "src/fx.js",
6
6
  "homepage": "https://krstivoja.github.io/fancoolo-fx/",
@@ -14,10 +14,15 @@
14
14
  "keywords": ["gsap", "animation", "scrolltrigger", "splittext", "wordpress", "gutenberg"],
15
15
  "author": "Fancoolo",
16
16
  "scripts": {
17
- "sync": "cp src/fx.js assets/fx.js && cp src/fx.js docs/vendor/fx.js"
17
+ "sync": "cp src/fx.js assets/fx.js && cp src/fx.js docs/vendor/fx.js",
18
+ "build": "wp-scripts build src/editor/index.js --output-path=assets/editor",
19
+ "start": "wp-scripts start src/editor/index.js --output-path=assets/editor"
18
20
  },
19
21
  "license": "ISC",
20
22
  "dependencies": {
21
23
  "gsap": "^3.14.2"
24
+ },
25
+ "devDependencies": {
26
+ "@wordpress/scripts": "^30.0.0"
22
27
  }
23
28
  }
package/src/fx.js CHANGED
@@ -74,6 +74,19 @@
74
74
  * }
75
75
  */
76
76
  tagMap: null,
77
+
78
+ /** Disable all animations on mobile (window.innerWidth <= mobileBreakpoint). */
79
+ disableMobile: false,
80
+ mobileBreakpoint: 768,
81
+
82
+ /** Multiply all animation durations globally (e.g. 0.5 = half speed, 2 = double). */
83
+ speedMultiplier: 1,
84
+
85
+ /** Skip all animations when OS prefers-reduced-motion is enabled. */
86
+ respectReducedMotion: true,
87
+
88
+ /** CSS selector string — matching elements are never animated. */
89
+ excludeSelectors: '',
77
90
  };
78
91
 
79
92
  // ── Defaults ────────────────────────────────
@@ -89,6 +102,11 @@
89
102
  clipUp: { duration: 1, ease: 'power3.inOut' },
90
103
  clipDown: { duration: 1, ease: 'power3.inOut' },
91
104
  tiltIn: { duration: 1.4, ease: 'power3.out' },
105
+ typeWriter: { duration: 0.05, ease: 'none', stagger: 0.03 },
106
+ drawSVG: { duration: 2, ease: 'power2.inOut' },
107
+ parallax: { duration: 1, ease: 'none' },
108
+ splitWords: { duration: 0.8, ease: 'power3.out', stagger: 0.05 },
109
+ slideIn: { duration: 1, ease: 'power3.out' },
92
110
  };
93
111
 
94
112
  // ── Helpers ──────────────────────────────────
@@ -108,8 +126,12 @@
108
126
 
109
127
  function resolveOptions(el, effectName, overrides) {
110
128
  var d = EFFECT_DEFAULTS[effectName];
129
+ var dur = getClassModifier(el, 'duration', overrides.duration != null ? overrides.duration : d.duration);
130
+ if (config.speedMultiplier && config.speedMultiplier !== 1) {
131
+ dur = dur * config.speedMultiplier;
132
+ }
111
133
  return {
112
- duration: getClassModifier(el, 'duration', overrides.duration != null ? overrides.duration : d.duration),
134
+ duration: dur,
113
135
  ease: getClassModifier(el, 'ease', overrides.ease != null ? overrides.ease : d.ease),
114
136
  stagger: getClassModifier(el, 'stagger', overrides.stagger != null ? overrides.stagger : (d.stagger || 0)),
115
137
  delay: getClassModifier(el, 'delay', overrides.delay != null ? overrides.delay : 0),
@@ -348,6 +370,137 @@
348
370
  });
349
371
  }
350
372
 
373
+ function typeWriter(el, opts) {
374
+ opts = opts || {};
375
+ var o = resolveOptions(el, 'typeWriter', opts);
376
+
377
+ var split = new SplitText(el, { type: 'chars' });
378
+ gsap.set(split.chars, { opacity: 0 });
379
+
380
+ var tweenVars = {
381
+ opacity: 1,
382
+ duration: o.duration,
383
+ ease: o.ease,
384
+ stagger: o.stagger,
385
+ delay: o.delay,
386
+ };
387
+
388
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
389
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
390
+ }
391
+
392
+ gsap.to(split.chars, tweenVars);
393
+ }
394
+
395
+ function drawSVG(el, opts) {
396
+ opts = opts || {};
397
+ var o = resolveOptions(el, 'drawSVG', opts);
398
+
399
+ var paths = el.tagName === 'path' || el.tagName === 'line' || el.tagName === 'circle' || el.tagName === 'polyline'
400
+ ? [el]
401
+ : el.querySelectorAll('path, line, circle, polyline, polygon, ellipse, rect');
402
+
403
+ if (!paths.length) return;
404
+
405
+ Array.prototype.forEach.call(paths, function(path) {
406
+ if (typeof path.getTotalLength === 'function') {
407
+ var len = path.getTotalLength();
408
+ gsap.set(path, { strokeDasharray: len, strokeDashoffset: len });
409
+ }
410
+ });
411
+
412
+ // Scrub mode: SVG draws as user scrolls (class fx-scrub-[0.6] or opts.scrub)
413
+ var scrubVal = getClassModifier(el, 'scrub', opts.scrub != null ? opts.scrub : null);
414
+ if (scrubVal !== null) {
415
+ gsap.to(paths, {
416
+ strokeDashoffset: 0,
417
+ ease: o.ease,
418
+ scrollTrigger: {
419
+ trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
420
+ start: config.scrollStart || 'top 85%',
421
+ end: opts.end || 'top 20%',
422
+ scrub: scrubVal === true || scrubVal === 'true' ? true : scrubVal,
423
+ },
424
+ });
425
+ return;
426
+ }
427
+
428
+ var tweenVars = {
429
+ strokeDashoffset: 0,
430
+ duration: o.duration,
431
+ ease: o.ease,
432
+ delay: o.delay,
433
+ };
434
+
435
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
436
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
437
+ }
438
+
439
+ gsap.to(paths, tweenVars);
440
+ }
441
+
442
+ function parallax(el, opts) {
443
+ opts = opts || {};
444
+ // Read y from modifier class fx-y-[80] or opts or default 50
445
+ var yShift = getClassModifier(el, 'y', opts.y != null ? opts.y : 50);
446
+
447
+ gsap.fromTo(el, {
448
+ y: -yShift,
449
+ }, {
450
+ y: yShift,
451
+ ease: 'none',
452
+ scrollTrigger: {
453
+ trigger: (opts.scrollTrigger && opts.scrollTrigger.trigger) || el,
454
+ start: config.scrollStart || 'top 85%',
455
+ end: opts.end || 'bottom top',
456
+ scrub: opts.scrub != null ? opts.scrub : true,
457
+ },
458
+ });
459
+ }
460
+
461
+ function splitWords(el, opts) {
462
+ opts = opts || {};
463
+ var o = resolveOptions(el, 'splitWords', opts);
464
+
465
+ var split = new SplitText(el, { type: 'words' });
466
+
467
+ var tweenVars = {
468
+ y: opts.y != null ? opts.y : 30,
469
+ opacity: 0,
470
+ duration: o.duration,
471
+ ease: o.ease,
472
+ stagger: o.stagger,
473
+ delay: o.delay,
474
+ };
475
+
476
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
477
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
478
+ }
479
+
480
+ gsap.from(split.words, tweenVars);
481
+ }
482
+
483
+ function slideIn(el, opts) {
484
+ opts = opts || {};
485
+ var o = resolveOptions(el, 'slideIn', opts);
486
+ var direction = opts.direction || 'left';
487
+ var xVal = opts.x != null ? opts.x : 100;
488
+
489
+ var tweenVars = {
490
+ x: direction === 'left' ? -xVal : xVal,
491
+ opacity: 0,
492
+ duration: o.duration,
493
+ ease: o.ease,
494
+ delay: o.delay,
495
+ };
496
+
497
+ if (opts.trigger === 'scroll' || opts.scrollTrigger) {
498
+ tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
499
+ }
500
+
501
+ gsap.from(el, tweenVars);
502
+ }
503
+
351
504
  // ── Class-to-effect mapping ─────────────────
352
505
 
353
506
  var effects = {
@@ -360,6 +513,11 @@
360
513
  'fx-blur-in': blurIn,
361
514
  'fx-clip-up': clipUp,
362
515
  'fx-clip-down': clipDown,
516
+ 'fx-type-writer': typeWriter,
517
+ 'fx-draw-svg': drawSVG,
518
+ 'fx-split-words': splitWords,
519
+ 'fx-slide-left': function(el, opts) { opts = opts || {}; opts.direction = 'left'; slideIn(el, opts); },
520
+ 'fx-slide-right': function(el, opts) { opts = opts || {}; opts.direction = 'right'; slideIn(el, opts); },
363
521
  };
364
522
 
365
523
  var effectsByName = {
@@ -373,6 +531,11 @@
373
531
  clipUp: clipUp,
374
532
  clipDown: clipDown,
375
533
  tiltIn: tiltIn,
534
+ typeWriter: typeWriter,
535
+ drawSVG: drawSVG,
536
+ parallax: parallax,
537
+ splitWords: splitWords,
538
+ slideIn: slideIn,
376
539
  };
377
540
 
378
541
  // ── Helpers ──────────────────────────────────
@@ -403,6 +566,11 @@
403
566
  return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
404
567
  }
405
568
 
569
+ function isExcluded(el) {
570
+ if (!config.excludeSelectors) return false;
571
+ try { return el.matches(config.excludeSelectors); } catch (e) { return false; }
572
+ }
573
+
406
574
  // ── Init ────────────────────────────────────
407
575
 
408
576
  function init() {
@@ -414,6 +582,7 @@
414
582
  // 1. Page-load variant: .fx-<name>-pl
415
583
  var plGroups = groupByParent(document.querySelectorAll('.' + name + '-pl'));
416
584
  plGroups.forEach(function (group) {
585
+ group = group.filter(function (el) { return !isExcluded(el); });
417
586
  group.forEach(function (el, i) {
418
587
  fn(el, { delay: i * 0.15 });
419
588
  processed.add(el);
@@ -426,6 +595,7 @@
426
595
  var stEls = document.querySelectorAll('.' + name + '-st');
427
596
  var stGroups = groupByParent(stEls);
428
597
  stGroups.forEach(function (group) {
598
+ group = group.filter(function (el) { return !isExcluded(el); });
429
599
  group.forEach(function (el, i) {
430
600
  fn(el, {
431
601
  trigger: 'scroll',
@@ -442,7 +612,7 @@
442
612
  if (config.sectionSelector) {
443
613
  document.querySelectorAll(config.sectionSelector).forEach(function (section) {
444
614
  var bareEls = Array.from(section.querySelectorAll('.' + name))
445
- .filter(function (el) { return !processed.has(el); });
615
+ .filter(function (el) { return !processed.has(el) && !isExcluded(el); });
446
616
  if (bareEls.length === 0) return;
447
617
 
448
618
  var groups = groupByParent(bareEls);
@@ -460,14 +630,25 @@
460
630
  }
461
631
  });
462
632
 
463
- // 4. fx-tilt-in — scrub-based 3D perspective (always scroll-linked)
464
- // Processed before tagMap so tilt elements aren't grabbed by tagMap first.
633
+ // 4. Scrub-based effects always scroll-linked, processed before tagMap.
465
634
  document.querySelectorAll('.fx-tilt-in-st, .fx-tilt-in-pl, .fx-tilt-in').forEach(function (el) {
466
- if (!processed.has(el)) {
635
+ if (!processed.has(el) && !isExcluded(el)) {
467
636
  tiltIn(el);
468
637
  processed.add(el);
469
638
  }
470
639
  });
640
+ document.querySelectorAll('.fx-parallax-st, .fx-parallax-pl, .fx-parallax').forEach(function (el) {
641
+ if (!processed.has(el) && !isExcluded(el)) {
642
+ parallax(el);
643
+ processed.add(el);
644
+ }
645
+ });
646
+ document.querySelectorAll('.fx-draw-svg-scrub').forEach(function (el) {
647
+ if (!processed.has(el) && !isExcluded(el)) {
648
+ drawSVG(el, { scrub: getClassModifier(el, 'scrub', 0.6) });
649
+ processed.add(el);
650
+ }
651
+ });
471
652
 
472
653
  // 5. Tag-based auto-animation inside sections
473
654
  if (config.tagMap && config.sectionSelector) {
@@ -478,7 +659,7 @@
478
659
  if (!fn) return;
479
660
 
480
661
  var els = Array.from(section.querySelectorAll(selector))
481
- .filter(function (el) { return !processed.has(el); });
662
+ .filter(function (el) { return !processed.has(el) && !isExcluded(el); });
482
663
  if (els.length === 0) return;
483
664
 
484
665
  var groups = groupByParent(els);
@@ -544,10 +725,26 @@
544
725
  if (pre.scrollStart !== undefined) config.scrollStart = pre.scrollStart;
545
726
  if (pre.scrollOnce !== undefined) config.scrollOnce = pre.scrollOnce;
546
727
  if (pre.tagMap !== undefined) config.tagMap = pre.tagMap;
728
+ if (pre.disableMobile !== undefined) config.disableMobile = pre.disableMobile;
729
+ if (pre.mobileBreakpoint !== undefined) config.mobileBreakpoint = pre.mobileBreakpoint;
730
+ if (pre.speedMultiplier !== undefined) config.speedMultiplier = pre.speedMultiplier;
731
+ if (pre.respectReducedMotion !== undefined) config.respectReducedMotion = pre.respectReducedMotion;
732
+ if (pre.excludeSelectors !== undefined) config.excludeSelectors = pre.excludeSelectors;
547
733
  }
548
734
 
549
735
  function boot() {
550
736
  applyPreConfig();
737
+
738
+ // Skip animations if OS reduced motion is enabled
739
+ if (config.respectReducedMotion && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
740
+ return;
741
+ }
742
+
743
+ // Skip animations on mobile
744
+ if (config.disableMobile && window.innerWidth <= config.mobileBreakpoint) {
745
+ return;
746
+ }
747
+
551
748
  init();
552
749
  }
553
750
 
@@ -571,6 +768,11 @@
571
768
  clipUp: clipUp,
572
769
  clipDown: clipDown,
573
770
  tiltIn: tiltIn,
771
+ typeWriter: typeWriter,
772
+ drawSVG: drawSVG,
773
+ parallax: parallax,
774
+ splitWords: splitWords,
775
+ slideIn: slideIn,
574
776
  init: init,
575
777
  };
576
778
  })();