gsap-react-marquee 0.1.1 → 0.1.4

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/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import require$$0, { forwardRef, useRef, useState, useMemo } from 'react';
2
2
  import { useGSAP } from '@gsap/react';
3
- import gsap from 'gsap';
3
+ import gsap$1 from 'gsap';
4
4
 
5
5
  var jsxRuntime = {exports: {}};
6
6
 
@@ -421,6 +421,710 @@ if (process.env.NODE_ENV === 'production') {
421
421
 
422
422
  var jsxRuntimeExports = jsxRuntime.exports;
423
423
 
424
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
425
+
426
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); return Constructor; }
427
+
428
+ /*!
429
+ * Observer 3.13.0
430
+ * https://gsap.com
431
+ *
432
+ * @license Copyright 2008-2025, GreenSock. All rights reserved.
433
+ * Subject to the terms at https://gsap.com/standard-license
434
+ * @author: Jack Doyle, jack@greensock.com
435
+ */
436
+
437
+ /* eslint-disable */
438
+ var gsap,
439
+ _coreInitted,
440
+ _win,
441
+ _doc,
442
+ _docEl,
443
+ _body,
444
+ _isTouch,
445
+ _pointerType,
446
+ ScrollTrigger,
447
+ _root,
448
+ _normalizer,
449
+ _eventTypes,
450
+ _context,
451
+ _getGSAP = function _getGSAP() {
452
+ return gsap || typeof window !== "undefined" && (gsap = window.gsap) && gsap.registerPlugin && gsap;
453
+ },
454
+ _startup = 1,
455
+ _observers = [],
456
+ _scrollers = [],
457
+ _proxies = [],
458
+ _getTime = Date.now,
459
+ _bridge = function _bridge(name, value) {
460
+ return value;
461
+ },
462
+ _integrate = function _integrate() {
463
+ var core = ScrollTrigger.core,
464
+ data = core.bridge || {},
465
+ scrollers = core._scrollers,
466
+ proxies = core._proxies;
467
+ scrollers.push.apply(scrollers, _scrollers);
468
+ proxies.push.apply(proxies, _proxies);
469
+ _scrollers = scrollers;
470
+ _proxies = proxies;
471
+
472
+ _bridge = function _bridge(name, value) {
473
+ return data[name](value);
474
+ };
475
+ },
476
+ _getProxyProp = function _getProxyProp(element, property) {
477
+ return ~_proxies.indexOf(element) && _proxies[_proxies.indexOf(element) + 1][property];
478
+ },
479
+ _isViewport = function _isViewport(el) {
480
+ return !!~_root.indexOf(el);
481
+ },
482
+ _addListener = function _addListener(element, type, func, passive, capture) {
483
+ return element.addEventListener(type, func, {
484
+ passive: passive !== false,
485
+ capture: !!capture
486
+ });
487
+ },
488
+ _removeListener = function _removeListener(element, type, func, capture) {
489
+ return element.removeEventListener(type, func, !!capture);
490
+ },
491
+ _scrollLeft = "scrollLeft",
492
+ _scrollTop = "scrollTop",
493
+ _onScroll = function _onScroll() {
494
+ return _normalizer && _normalizer.isPressed || _scrollers.cache++;
495
+ },
496
+ _scrollCacheFunc = function _scrollCacheFunc(f, doNotCache) {
497
+ var cachingFunc = function cachingFunc(value) {
498
+ // since reading the scrollTop/scrollLeft/pageOffsetY/pageOffsetX can trigger a layout, this function allows us to cache the value so it only gets read fresh after a "scroll" event fires (or while we're refreshing because that can lengthen the page and alter the scroll position). when "soft" is true, that means don't actually set the scroll, but cache the new value instead (useful in ScrollSmoother)
499
+ if (value || value === 0) {
500
+ _startup && (_win.history.scrollRestoration = "manual"); // otherwise the new position will get overwritten by the browser onload.
501
+
502
+ var isNormalizing = _normalizer && _normalizer.isPressed;
503
+ value = cachingFunc.v = Math.round(value) || (_normalizer && _normalizer.iOS ? 1 : 0); //TODO: iOS Bug: if you allow it to go to 0, Safari can start to report super strange (wildly inaccurate) touch positions!
504
+
505
+ f(value);
506
+ cachingFunc.cacheID = _scrollers.cache;
507
+ isNormalizing && _bridge("ss", value); // set scroll (notify ScrollTrigger so it can dispatch a "scrollStart" event if necessary
508
+ } else if (doNotCache || _scrollers.cache !== cachingFunc.cacheID || _bridge("ref")) {
509
+ cachingFunc.cacheID = _scrollers.cache;
510
+ cachingFunc.v = f();
511
+ }
512
+
513
+ return cachingFunc.v + cachingFunc.offset;
514
+ };
515
+
516
+ cachingFunc.offset = 0;
517
+ return f && cachingFunc;
518
+ },
519
+ _horizontal = {
520
+ s: _scrollLeft,
521
+ p: "left",
522
+ p2: "Left",
523
+ os: "right",
524
+ os2: "Right",
525
+ d: "width",
526
+ d2: "Width",
527
+ a: "x",
528
+ sc: _scrollCacheFunc(function (value) {
529
+ return arguments.length ? _win.scrollTo(value, _vertical.sc()) : _win.pageXOffset || _doc[_scrollLeft] || _docEl[_scrollLeft] || _body[_scrollLeft] || 0;
530
+ })
531
+ },
532
+ _vertical = {
533
+ s: _scrollTop,
534
+ p: "top",
535
+ p2: "Top",
536
+ os: "bottom",
537
+ os2: "Bottom",
538
+ d: "height",
539
+ d2: "Height",
540
+ a: "y",
541
+ op: _horizontal,
542
+ sc: _scrollCacheFunc(function (value) {
543
+ return arguments.length ? _win.scrollTo(_horizontal.sc(), value) : _win.pageYOffset || _doc[_scrollTop] || _docEl[_scrollTop] || _body[_scrollTop] || 0;
544
+ })
545
+ },
546
+ _getTarget = function _getTarget(t, self) {
547
+ return (self && self._ctx && self._ctx.selector || gsap.utils.toArray)(t)[0] || (typeof t === "string" && gsap.config().nullTargetWarn !== false ? console.warn("Element not found:", t) : null);
548
+ },
549
+ _isWithin = function _isWithin(element, list) {
550
+ // check if the element is in the list or is a descendant of an element in the list.
551
+ var i = list.length;
552
+
553
+ while (i--) {
554
+ if (list[i] === element || list[i].contains(element)) {
555
+ return true;
556
+ }
557
+ }
558
+
559
+ return false;
560
+ },
561
+ _getScrollFunc = function _getScrollFunc(element, _ref) {
562
+ var s = _ref.s,
563
+ sc = _ref.sc;
564
+ // we store the scroller functions in an alternating sequenced Array like [element, verticalScrollFunc, horizontalScrollFunc, ...] so that we can minimize memory, maximize performance, and we also record the last position as a ".rec" property in order to revert to that after refreshing to ensure things don't shift around.
565
+ _isViewport(element) && (element = _doc.scrollingElement || _docEl);
566
+
567
+ var i = _scrollers.indexOf(element),
568
+ offset = sc === _vertical.sc ? 1 : 2;
569
+
570
+ !~i && (i = _scrollers.push(element) - 1);
571
+ _scrollers[i + offset] || _addListener(element, "scroll", _onScroll); // clear the cache when a scroll occurs
572
+
573
+ var prev = _scrollers[i + offset],
574
+ func = prev || (_scrollers[i + offset] = _scrollCacheFunc(_getProxyProp(element, s), true) || (_isViewport(element) ? sc : _scrollCacheFunc(function (value) {
575
+ return arguments.length ? element[s] = value : element[s];
576
+ })));
577
+ func.target = element;
578
+ prev || (func.smooth = gsap.getProperty(element, "scrollBehavior") === "smooth"); // only set it the first time (don't reset every time a scrollFunc is requested because perhaps it happens during a refresh() when it's disabled in ScrollTrigger.
579
+
580
+ return func;
581
+ },
582
+ _getVelocityProp = function _getVelocityProp(value, minTimeRefresh, useDelta) {
583
+ var v1 = value,
584
+ v2 = value,
585
+ t1 = _getTime(),
586
+ t2 = t1,
587
+ min = minTimeRefresh,
588
+ dropToZeroTime = Math.max(500, min * 3),
589
+ update = function update(value, force) {
590
+ var t = _getTime();
591
+
592
+ if (force || t - t1 > min) {
593
+ v2 = v1;
594
+ v1 = value;
595
+ t2 = t1;
596
+ t1 = t;
597
+ } else {
598
+ v1 += value;
599
+ }
600
+ },
601
+ reset = function reset() {
602
+ v2 = v1 = 0 ;
603
+ t2 = t1 = 0;
604
+ },
605
+ getVelocity = function getVelocity(latestValue) {
606
+ var tOld = t2,
607
+ vOld = v2,
608
+ t = _getTime();
609
+
610
+ (latestValue || latestValue === 0) && latestValue !== v1 && update(latestValue);
611
+ return t1 === t2 || t - t2 > dropToZeroTime ? 0 : (v1 + (vOld )) / ((t ) - tOld) * 1000;
612
+ };
613
+
614
+ return {
615
+ update: update,
616
+ reset: reset,
617
+ getVelocity: getVelocity
618
+ };
619
+ },
620
+ _getEvent = function _getEvent(e, preventDefault) {
621
+ preventDefault && !e._gsapAllow && e.preventDefault();
622
+ return e.changedTouches ? e.changedTouches[0] : e;
623
+ },
624
+ _getAbsoluteMax = function _getAbsoluteMax(a) {
625
+ var max = Math.max.apply(Math, a),
626
+ min = Math.min.apply(Math, a);
627
+ return Math.abs(max) >= Math.abs(min) ? max : min;
628
+ },
629
+ _setScrollTrigger = function _setScrollTrigger() {
630
+ ScrollTrigger = gsap.core.globals().ScrollTrigger;
631
+ ScrollTrigger && ScrollTrigger.core && _integrate();
632
+ },
633
+ _initCore = function _initCore(core) {
634
+ gsap = core || _getGSAP();
635
+
636
+ if (!_coreInitted && gsap && typeof document !== "undefined" && document.body) {
637
+ _win = window;
638
+ _doc = document;
639
+ _docEl = _doc.documentElement;
640
+ _body = _doc.body;
641
+ _root = [_win, _doc, _docEl, _body];
642
+ gsap.utils.clamp;
643
+
644
+ _context = gsap.core.context || function () {};
645
+
646
+ _pointerType = "onpointerenter" in _body ? "pointer" : "mouse"; // isTouch is 0 if no touch, 1 if ONLY touch, and 2 if it can accommodate touch but also other types like mouse/pointer.
647
+
648
+ _isTouch = Observer.isTouch = _win.matchMedia && _win.matchMedia("(hover: none), (pointer: coarse)").matches ? 1 : "ontouchstart" in _win || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 ? 2 : 0;
649
+ _eventTypes = Observer.eventTypes = ("ontouchstart" in _docEl ? "touchstart,touchmove,touchcancel,touchend" : !("onpointerdown" in _docEl) ? "mousedown,mousemove,mouseup,mouseup" : "pointerdown,pointermove,pointercancel,pointerup").split(",");
650
+ setTimeout(function () {
651
+ return _startup = 0;
652
+ }, 500);
653
+
654
+ _setScrollTrigger();
655
+
656
+ _coreInitted = 1;
657
+ }
658
+
659
+ return _coreInitted;
660
+ };
661
+
662
+ _horizontal.op = _vertical;
663
+ _scrollers.cache = 0;
664
+ var Observer = /*#__PURE__*/function () {
665
+ function Observer(vars) {
666
+ this.init(vars);
667
+ }
668
+
669
+ var _proto = Observer.prototype;
670
+
671
+ _proto.init = function init(vars) {
672
+ _coreInitted || _initCore(gsap) || console.warn("Please gsap.registerPlugin(Observer)");
673
+ ScrollTrigger || _setScrollTrigger();
674
+ var tolerance = vars.tolerance,
675
+ dragMinimum = vars.dragMinimum,
676
+ type = vars.type,
677
+ target = vars.target,
678
+ lineHeight = vars.lineHeight,
679
+ debounce = vars.debounce,
680
+ preventDefault = vars.preventDefault,
681
+ onStop = vars.onStop,
682
+ onStopDelay = vars.onStopDelay,
683
+ ignore = vars.ignore,
684
+ wheelSpeed = vars.wheelSpeed,
685
+ event = vars.event,
686
+ onDragStart = vars.onDragStart,
687
+ onDragEnd = vars.onDragEnd,
688
+ onDrag = vars.onDrag,
689
+ onPress = vars.onPress,
690
+ onRelease = vars.onRelease,
691
+ onRight = vars.onRight,
692
+ onLeft = vars.onLeft,
693
+ onUp = vars.onUp,
694
+ onDown = vars.onDown,
695
+ onChangeX = vars.onChangeX,
696
+ onChangeY = vars.onChangeY,
697
+ onChange = vars.onChange,
698
+ onToggleX = vars.onToggleX,
699
+ onToggleY = vars.onToggleY,
700
+ onHover = vars.onHover,
701
+ onHoverEnd = vars.onHoverEnd,
702
+ onMove = vars.onMove,
703
+ ignoreCheck = vars.ignoreCheck,
704
+ isNormalizer = vars.isNormalizer,
705
+ onGestureStart = vars.onGestureStart,
706
+ onGestureEnd = vars.onGestureEnd,
707
+ onWheel = vars.onWheel,
708
+ onEnable = vars.onEnable,
709
+ onDisable = vars.onDisable,
710
+ onClick = vars.onClick,
711
+ scrollSpeed = vars.scrollSpeed,
712
+ capture = vars.capture,
713
+ allowClicks = vars.allowClicks,
714
+ lockAxis = vars.lockAxis,
715
+ onLockAxis = vars.onLockAxis;
716
+ this.target = target = _getTarget(target) || _docEl;
717
+ this.vars = vars;
718
+ ignore && (ignore = gsap.utils.toArray(ignore));
719
+ tolerance = tolerance || 1e-9;
720
+ dragMinimum = dragMinimum || 0;
721
+ wheelSpeed = wheelSpeed || 1;
722
+ scrollSpeed = scrollSpeed || 1;
723
+ type = type || "wheel,touch,pointer";
724
+ debounce = debounce !== false;
725
+ lineHeight || (lineHeight = parseFloat(_win.getComputedStyle(_body).lineHeight) || 22); // note: browser may report "normal", so default to 22.
726
+
727
+ var id,
728
+ onStopDelayedCall,
729
+ dragged,
730
+ moved,
731
+ wheeled,
732
+ locked,
733
+ axis,
734
+ self = this,
735
+ prevDeltaX = 0,
736
+ prevDeltaY = 0,
737
+ passive = vars.passive || !preventDefault && vars.passive !== false,
738
+ scrollFuncX = _getScrollFunc(target, _horizontal),
739
+ scrollFuncY = _getScrollFunc(target, _vertical),
740
+ scrollX = scrollFuncX(),
741
+ scrollY = scrollFuncY(),
742
+ limitToTouch = ~type.indexOf("touch") && !~type.indexOf("pointer") && _eventTypes[0] === "pointerdown",
743
+ // for devices that accommodate mouse events and touch events, we need to distinguish.
744
+ isViewport = _isViewport(target),
745
+ ownerDoc = target.ownerDocument || _doc,
746
+ deltaX = [0, 0, 0],
747
+ // wheel, scroll, pointer/touch
748
+ deltaY = [0, 0, 0],
749
+ onClickTime = 0,
750
+ clickCapture = function clickCapture() {
751
+ return onClickTime = _getTime();
752
+ },
753
+ _ignoreCheck = function _ignoreCheck(e, isPointerOrTouch) {
754
+ return (self.event = e) && ignore && _isWithin(e.target, ignore) || isPointerOrTouch && limitToTouch && e.pointerType !== "touch" || ignoreCheck && ignoreCheck(e, isPointerOrTouch);
755
+ },
756
+ onStopFunc = function onStopFunc() {
757
+ self._vx.reset();
758
+
759
+ self._vy.reset();
760
+
761
+ onStopDelayedCall.pause();
762
+ onStop && onStop(self);
763
+ },
764
+ update = function update() {
765
+ var dx = self.deltaX = _getAbsoluteMax(deltaX),
766
+ dy = self.deltaY = _getAbsoluteMax(deltaY),
767
+ changedX = Math.abs(dx) >= tolerance,
768
+ changedY = Math.abs(dy) >= tolerance;
769
+
770
+ onChange && (changedX || changedY) && onChange(self, dx, dy, deltaX, deltaY); // in ScrollTrigger.normalizeScroll(), we need to know if it was touch/pointer so we need access to the deltaX/deltaY Arrays before we clear them out.
771
+
772
+ if (changedX) {
773
+ onRight && self.deltaX > 0 && onRight(self);
774
+ onLeft && self.deltaX < 0 && onLeft(self);
775
+ onChangeX && onChangeX(self);
776
+ onToggleX && self.deltaX < 0 !== prevDeltaX < 0 && onToggleX(self);
777
+ prevDeltaX = self.deltaX;
778
+ deltaX[0] = deltaX[1] = deltaX[2] = 0;
779
+ }
780
+
781
+ if (changedY) {
782
+ onDown && self.deltaY > 0 && onDown(self);
783
+ onUp && self.deltaY < 0 && onUp(self);
784
+ onChangeY && onChangeY(self);
785
+ onToggleY && self.deltaY < 0 !== prevDeltaY < 0 && onToggleY(self);
786
+ prevDeltaY = self.deltaY;
787
+ deltaY[0] = deltaY[1] = deltaY[2] = 0;
788
+ }
789
+
790
+ if (moved || dragged) {
791
+ onMove && onMove(self);
792
+
793
+ if (dragged) {
794
+ onDragStart && dragged === 1 && onDragStart(self);
795
+ onDrag && onDrag(self);
796
+ dragged = 0;
797
+ }
798
+
799
+ moved = false;
800
+ }
801
+
802
+ locked && !(locked = false) && onLockAxis && onLockAxis(self);
803
+
804
+ if (wheeled) {
805
+ onWheel(self);
806
+ wheeled = false;
807
+ }
808
+
809
+ id = 0;
810
+ },
811
+ onDelta = function onDelta(x, y, index) {
812
+ deltaX[index] += x;
813
+ deltaY[index] += y;
814
+
815
+ self._vx.update(x);
816
+
817
+ self._vy.update(y);
818
+
819
+ debounce ? id || (id = requestAnimationFrame(update)) : update();
820
+ },
821
+ onTouchOrPointerDelta = function onTouchOrPointerDelta(x, y) {
822
+ if (lockAxis && !axis) {
823
+ self.axis = axis = Math.abs(x) > Math.abs(y) ? "x" : "y";
824
+ locked = true;
825
+ }
826
+
827
+ if (axis !== "y") {
828
+ deltaX[2] += x;
829
+
830
+ self._vx.update(x, true); // update the velocity as frequently as possible instead of in the debounced function so that very quick touch-scrolls (flicks) feel natural. If it's the mouse/touch/pointer, force it so that we get snappy/accurate momentum scroll.
831
+
832
+ }
833
+
834
+ if (axis !== "x") {
835
+ deltaY[2] += y;
836
+
837
+ self._vy.update(y, true);
838
+ }
839
+
840
+ debounce ? id || (id = requestAnimationFrame(update)) : update();
841
+ },
842
+ _onDrag = function _onDrag(e) {
843
+ if (_ignoreCheck(e, 1)) {
844
+ return;
845
+ }
846
+
847
+ e = _getEvent(e, preventDefault);
848
+ var x = e.clientX,
849
+ y = e.clientY,
850
+ dx = x - self.x,
851
+ dy = y - self.y,
852
+ isDragging = self.isDragging;
853
+ self.x = x;
854
+ self.y = y;
855
+
856
+ if (isDragging || (dx || dy) && (Math.abs(self.startX - x) >= dragMinimum || Math.abs(self.startY - y) >= dragMinimum)) {
857
+ dragged = isDragging ? 2 : 1; // dragged: 0 = not dragging, 1 = first drag, 2 = normal drag
858
+
859
+ isDragging || (self.isDragging = true);
860
+ onTouchOrPointerDelta(dx, dy);
861
+ }
862
+ },
863
+ _onPress = self.onPress = function (e) {
864
+ if (_ignoreCheck(e, 1) || e && e.button) {
865
+ return;
866
+ }
867
+
868
+ self.axis = axis = null;
869
+ onStopDelayedCall.pause();
870
+ self.isPressed = true;
871
+ e = _getEvent(e); // note: may need to preventDefault(?) Won't side-scroll on iOS Safari if we do, though.
872
+
873
+ prevDeltaX = prevDeltaY = 0;
874
+ self.startX = self.x = e.clientX;
875
+ self.startY = self.y = e.clientY;
876
+
877
+ self._vx.reset(); // otherwise the t2 may be stale if the user touches and flicks super fast and releases in less than 2 requestAnimationFrame ticks, causing velocity to be 0.
878
+
879
+
880
+ self._vy.reset();
881
+
882
+ _addListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, passive, true);
883
+
884
+ self.deltaX = self.deltaY = 0;
885
+ onPress && onPress(self);
886
+ },
887
+ _onRelease = self.onRelease = function (e) {
888
+ if (_ignoreCheck(e, 1)) {
889
+ return;
890
+ }
891
+
892
+ _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true);
893
+
894
+ var isTrackingDrag = !isNaN(self.y - self.startY),
895
+ wasDragging = self.isDragging,
896
+ isDragNotClick = wasDragging && (Math.abs(self.x - self.startX) > 3 || Math.abs(self.y - self.startY) > 3),
897
+ // some touch devices need some wiggle room in terms of sensing clicks - the finger may move a few pixels.
898
+ eventData = _getEvent(e);
899
+
900
+ if (!isDragNotClick && isTrackingDrag) {
901
+ self._vx.reset();
902
+
903
+ self._vy.reset(); //if (preventDefault && allowClicks && self.isPressed) { // check isPressed because in a rare edge case, the inputObserver in ScrollTrigger may stopPropagation() on the press/drag, so the onRelease may get fired without the onPress/onDrag ever getting called, thus it could trigger a click to occur on a link after scroll-dragging it.
904
+
905
+
906
+ if (preventDefault && allowClicks) {
907
+ gsap.delayedCall(0.08, function () {
908
+ // some browsers (like Firefox) won't trust script-generated clicks, so if the user tries to click on a video to play it, for example, it simply won't work. Since a regular "click" event will most likely be generated anyway (one that has its isTrusted flag set to true), we must slightly delay our script-generated click so that the "real"/trusted one is prioritized. Remember, when there are duplicate events in quick succession, we suppress all but the first one. Some browsers don't even trigger the "real" one at all, so our synthetic one is a safety valve that ensures that no matter what, a click event does get dispatched.
909
+ if (_getTime() - onClickTime > 300 && !e.defaultPrevented) {
910
+ if (e.target.click) {
911
+ //some browsers (like mobile Safari) don't properly trigger the click event
912
+ e.target.click();
913
+ } else if (ownerDoc.createEvent) {
914
+ var syntheticEvent = ownerDoc.createEvent("MouseEvents");
915
+ syntheticEvent.initMouseEvent("click", true, true, _win, 1, eventData.screenX, eventData.screenY, eventData.clientX, eventData.clientY, false, false, false, false, 0, null);
916
+ e.target.dispatchEvent(syntheticEvent);
917
+ }
918
+ }
919
+ });
920
+ }
921
+ }
922
+
923
+ self.isDragging = self.isGesturing = self.isPressed = false;
924
+ onStop && wasDragging && !isNormalizer && onStopDelayedCall.restart(true);
925
+ dragged && update(); // in case debouncing, we don't want onDrag to fire AFTER onDragEnd().
926
+
927
+ onDragEnd && wasDragging && onDragEnd(self);
928
+ onRelease && onRelease(self, isDragNotClick);
929
+ },
930
+ _onGestureStart = function _onGestureStart(e) {
931
+ return e.touches && e.touches.length > 1 && (self.isGesturing = true) && onGestureStart(e, self.isDragging);
932
+ },
933
+ _onGestureEnd = function _onGestureEnd() {
934
+ return (self.isGesturing = false) || onGestureEnd(self);
935
+ },
936
+ onScroll = function onScroll(e) {
937
+ if (_ignoreCheck(e)) {
938
+ return;
939
+ }
940
+
941
+ var x = scrollFuncX(),
942
+ y = scrollFuncY();
943
+ onDelta((x - scrollX) * scrollSpeed, (y - scrollY) * scrollSpeed, 1);
944
+ scrollX = x;
945
+ scrollY = y;
946
+ onStop && onStopDelayedCall.restart(true);
947
+ },
948
+ _onWheel = function _onWheel(e) {
949
+ if (_ignoreCheck(e)) {
950
+ return;
951
+ }
952
+
953
+ e = _getEvent(e, preventDefault);
954
+ onWheel && (wheeled = true);
955
+ var multiplier = (e.deltaMode === 1 ? lineHeight : e.deltaMode === 2 ? _win.innerHeight : 1) * wheelSpeed;
956
+ onDelta(e.deltaX * multiplier, e.deltaY * multiplier, 0);
957
+ onStop && !isNormalizer && onStopDelayedCall.restart(true);
958
+ },
959
+ _onMove = function _onMove(e) {
960
+ if (_ignoreCheck(e)) {
961
+ return;
962
+ }
963
+
964
+ var x = e.clientX,
965
+ y = e.clientY,
966
+ dx = x - self.x,
967
+ dy = y - self.y;
968
+ self.x = x;
969
+ self.y = y;
970
+ moved = true;
971
+ onStop && onStopDelayedCall.restart(true);
972
+ (dx || dy) && onTouchOrPointerDelta(dx, dy);
973
+ },
974
+ _onHover = function _onHover(e) {
975
+ self.event = e;
976
+ onHover(self);
977
+ },
978
+ _onHoverEnd = function _onHoverEnd(e) {
979
+ self.event = e;
980
+ onHoverEnd(self);
981
+ },
982
+ _onClick = function _onClick(e) {
983
+ return _ignoreCheck(e) || _getEvent(e, preventDefault) && onClick(self);
984
+ };
985
+
986
+ onStopDelayedCall = self._dc = gsap.delayedCall(onStopDelay || 0.25, onStopFunc).pause();
987
+ self.deltaX = self.deltaY = 0;
988
+ self._vx = _getVelocityProp(0, 50);
989
+ self._vy = _getVelocityProp(0, 50);
990
+ self.scrollX = scrollFuncX;
991
+ self.scrollY = scrollFuncY;
992
+ self.isDragging = self.isGesturing = self.isPressed = false;
993
+
994
+ _context(this);
995
+
996
+ self.enable = function (e) {
997
+ if (!self.isEnabled) {
998
+ _addListener(isViewport ? ownerDoc : target, "scroll", _onScroll);
999
+
1000
+ type.indexOf("scroll") >= 0 && _addListener(isViewport ? ownerDoc : target, "scroll", onScroll, passive, capture);
1001
+ type.indexOf("wheel") >= 0 && _addListener(target, "wheel", _onWheel, passive, capture);
1002
+
1003
+ if (type.indexOf("touch") >= 0 && _isTouch || type.indexOf("pointer") >= 0) {
1004
+ _addListener(target, _eventTypes[0], _onPress, passive, capture);
1005
+
1006
+ _addListener(ownerDoc, _eventTypes[2], _onRelease);
1007
+
1008
+ _addListener(ownerDoc, _eventTypes[3], _onRelease);
1009
+
1010
+ allowClicks && _addListener(target, "click", clickCapture, true, true);
1011
+ onClick && _addListener(target, "click", _onClick);
1012
+ onGestureStart && _addListener(ownerDoc, "gesturestart", _onGestureStart);
1013
+ onGestureEnd && _addListener(ownerDoc, "gestureend", _onGestureEnd);
1014
+ onHover && _addListener(target, _pointerType + "enter", _onHover);
1015
+ onHoverEnd && _addListener(target, _pointerType + "leave", _onHoverEnd);
1016
+ onMove && _addListener(target, _pointerType + "move", _onMove);
1017
+ }
1018
+
1019
+ self.isEnabled = true;
1020
+ self.isDragging = self.isGesturing = self.isPressed = moved = dragged = false;
1021
+
1022
+ self._vx.reset();
1023
+
1024
+ self._vy.reset();
1025
+
1026
+ scrollX = scrollFuncX();
1027
+ scrollY = scrollFuncY();
1028
+ e && e.type && _onPress(e);
1029
+ onEnable && onEnable(self);
1030
+ }
1031
+
1032
+ return self;
1033
+ };
1034
+
1035
+ self.disable = function () {
1036
+ if (self.isEnabled) {
1037
+ // only remove the _onScroll listener if there aren't any others that rely on the functionality.
1038
+ _observers.filter(function (o) {
1039
+ return o !== self && _isViewport(o.target);
1040
+ }).length || _removeListener(isViewport ? ownerDoc : target, "scroll", _onScroll);
1041
+
1042
+ if (self.isPressed) {
1043
+ self._vx.reset();
1044
+
1045
+ self._vy.reset();
1046
+
1047
+ _removeListener(isNormalizer ? target : ownerDoc, _eventTypes[1], _onDrag, true);
1048
+ }
1049
+
1050
+ _removeListener(isViewport ? ownerDoc : target, "scroll", onScroll, capture);
1051
+
1052
+ _removeListener(target, "wheel", _onWheel, capture);
1053
+
1054
+ _removeListener(target, _eventTypes[0], _onPress, capture);
1055
+
1056
+ _removeListener(ownerDoc, _eventTypes[2], _onRelease);
1057
+
1058
+ _removeListener(ownerDoc, _eventTypes[3], _onRelease);
1059
+
1060
+ _removeListener(target, "click", clickCapture, true);
1061
+
1062
+ _removeListener(target, "click", _onClick);
1063
+
1064
+ _removeListener(ownerDoc, "gesturestart", _onGestureStart);
1065
+
1066
+ _removeListener(ownerDoc, "gestureend", _onGestureEnd);
1067
+
1068
+ _removeListener(target, _pointerType + "enter", _onHover);
1069
+
1070
+ _removeListener(target, _pointerType + "leave", _onHoverEnd);
1071
+
1072
+ _removeListener(target, _pointerType + "move", _onMove);
1073
+
1074
+ self.isEnabled = self.isPressed = self.isDragging = false;
1075
+ onDisable && onDisable(self);
1076
+ }
1077
+ };
1078
+
1079
+ self.kill = self.revert = function () {
1080
+ self.disable();
1081
+
1082
+ var i = _observers.indexOf(self);
1083
+
1084
+ i >= 0 && _observers.splice(i, 1);
1085
+ _normalizer === self && (_normalizer = 0);
1086
+ };
1087
+
1088
+ _observers.push(self);
1089
+
1090
+ isNormalizer && _isViewport(target) && (_normalizer = self);
1091
+ self.enable(event);
1092
+ };
1093
+
1094
+ _createClass(Observer, [{
1095
+ key: "velocityX",
1096
+ get: function get() {
1097
+ return this._vx.getVelocity();
1098
+ }
1099
+ }, {
1100
+ key: "velocityY",
1101
+ get: function get() {
1102
+ return this._vy.getVelocity();
1103
+ }
1104
+ }]);
1105
+
1106
+ return Observer;
1107
+ }();
1108
+ Observer.version = "3.13.0";
1109
+
1110
+ Observer.create = function (vars) {
1111
+ return new Observer(vars);
1112
+ };
1113
+
1114
+ Observer.register = _initCore;
1115
+
1116
+ Observer.getAll = function () {
1117
+ return _observers.slice();
1118
+ };
1119
+
1120
+ Observer.getById = function (id) {
1121
+ return _observers.filter(function (o) {
1122
+ return o.vars.id === id;
1123
+ })[0];
1124
+ };
1125
+
1126
+ _getGSAP() && gsap.registerPlugin(Observer);
1127
+
424
1128
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);}else for(f in e)e[f]&&(n&&(n+=" "),n+=f);return n}function clsx(){for(var e,t,f=0,n="",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
425
1129
 
426
1130
  const CLASS_PART_SEPARATOR = '-';
@@ -2917,134 +3621,339 @@ const twMerge = /*#__PURE__*/createTailwindMerge(getDefaultConfig);
2917
3621
 
2918
3622
  /**
2919
3623
  * Utility function to merge Tailwind classes with clsx
3624
+ *
3625
+ * Combines clsx for conditional classes with tailwind-merge to handle
3626
+ * conflicting Tailwind classes by keeping the last occurrence.
3627
+ * This prevents issues like "p-4 p-2" where both would be applied.
3628
+ *
3629
+ * @param inputs - Array of class values (strings, conditionals, objects)
3630
+ * @returns Merged and deduplicated class string
2920
3631
  */
2921
3632
  const cn = (...inputs) => {
2922
3633
  return twMerge(clsx(inputs));
2923
3634
  };
2924
3635
  /**
2925
- * Sets up container styles and rotation handling
3636
+ * Sets up container styles and rotation handling for the marquee
3637
+ *
3638
+ * This function handles the complex styling requirements for different marquee orientations:
3639
+ *
3640
+ * 1. **Basic Setup**: Applies gap spacing and rotation for vertical marquees
3641
+ * 2. **Vertical Mode**: Rotates container 90° and adjusts width to parent height
3642
+ * 3. **Rotation Alignment**: Special mode for vertical text that remains readable
3643
+ *
3644
+ * @param containerMarquee - The main container element that holds all marquee instances
3645
+ * @param marquees - Array of individual marquee wrapper elements
3646
+ * @param marqueesChildren - Array of content container elements within each marquee
3647
+ * @param isVertical - Boolean indicating if marquee moves up/down instead of left/right
3648
+ * @param props - Configuration object containing spacing and alignment options
2926
3649
  */
2927
3650
  const setupContainerStyles = (containerMarquee, marquees, marqueesChildren, isVertical, props) => {
2928
3651
  const { spacing = 16, alignRotationWithY = false } = props;
2929
- gsap.set(containerMarquee, {
3652
+ /**
3653
+ * Apply base container styling
3654
+ * - gap: Space between marquee elements (prevents content overlap)
3655
+ * - rotate: 90° rotation for vertical movement (transforms horizontal motion to vertical)
3656
+ */
3657
+ gsap$1.set(containerMarquee, {
2930
3658
  gap: `${spacing}px`,
2931
3659
  rotate: isVertical ? 90 : "0",
2932
3660
  });
3661
+ /**
3662
+ * Handle vertical marquee specific adjustments
3663
+ * When isVertical is true, the container is rotated 90°, so we need to:
3664
+ * 1. Set container width to match parent height (since it's rotated)
3665
+ * 2. Allow content to overflow visible bounds for smooth transitions
3666
+ */
2933
3667
  if (isVertical) {
2934
- gsap.set(containerMarquee, { width: window.innerHeight });
3668
+ const parent = containerMarquee.parentNode;
3669
+ gsap$1.set(containerMarquee, {
3670
+ width: parent.offsetHeight, // Width becomes the vertical space available
3671
+ });
3672
+ gsap$1.set(marqueesChildren, {
3673
+ overflow: "visible", // Prevents clipping during animation
3674
+ });
2935
3675
  }
3676
+ /**
3677
+ * Handle special rotation alignment mode
3678
+ *
3679
+ * This creates a complex layout where:
3680
+ * - The main container is rotated for vertical movement
3681
+ * - Individual content is counter-rotated to remain readable
3682
+ * - Content is repositioned to align properly within the rotated space
3683
+ *
3684
+ * Use case: Vertical text marquee where text remains horizontally readable
3685
+ */
2936
3686
  if (alignRotationWithY && marquees.length > 0) {
2937
3687
  const marqueeHeight = marquees[0].offsetHeight;
2938
- gsap.set(containerMarquee, {
3688
+ // Center align items within the container
3689
+ gsap$1.set(containerMarquee, {
2939
3690
  alignItems: "center",
2940
3691
  });
2941
- gsap.set(marqueesChildren, {
2942
- rotate: -90,
3692
+ /**
3693
+ * Counter-rotate content and reposition for proper alignment
3694
+ *
3695
+ * - rotate: -90° counters the container's 90° rotation
3696
+ * - x: Horizontal offset to center content within rotated container
3697
+ * - width: Set to marquee height since dimensions are swapped after rotation
3698
+ * - flexWrap/wordBreak/whiteSpace: Handle text flow in constrained space
3699
+ */
3700
+ gsap$1.set(marqueesChildren, {
3701
+ rotate: -90, // Counter-rotate to keep text readable
3702
+ x: (containerMarquee.offsetWidth - spacing) / 2 - spacing, // Center horizontally
2943
3703
  display: "flex",
2944
- flexWrap: "wrap",
2945
- width: marqueeHeight,
2946
- wordBreak: "break-all",
2947
- whiteSpace: "break-spaces",
3704
+ flexWrap: "wrap", // Allow text to wrap within constrained width
3705
+ width: marqueeHeight, // Width constraint for wrapped text
3706
+ wordBreak: "break-all", // Force word breaking if necessary
3707
+ whiteSpace: "break-spaces", // Preserve spaces while allowing breaks
3708
+ });
3709
+ /**
3710
+ * Adjust marquee height to fit within the rotated container
3711
+ * Accounts for spacing to prevent overflow
3712
+ */
3713
+ gsap$1.set(marquees, {
3714
+ height: containerMarquee.offsetWidth - spacing,
2948
3715
  });
2949
3716
  }
2950
3717
  };
2951
3718
  /**
2952
- * Calculates the number of duplicates needed to fill the container
3719
+ * Calculates the number of content duplicates needed for seamless looping
3720
+ *
3721
+ * For smooth infinite scrolling, we need enough content copies to fill the visible area
3722
+ * plus buffer space. This prevents gaps when content loops back to the beginning.
3723
+ *
3724
+ * Algorithm:
3725
+ * 1. If not in fill mode, only one copy is needed (content already spans container)
3726
+ * 2. Determine target width (viewport height for vertical, container width for horizontal)
3727
+ * 3. Calculate how many copies fit in the target space, rounding up for complete coverage
3728
+ *
3729
+ * @param marqueeChildrenWidth - Width of a single content instance
3730
+ * @param containerMarqueeWidth - Width of the marquee container
3731
+ * @param isVertical - Whether the marquee scrolls vertically
3732
+ * @param props - Configuration object containing fill mode setting
3733
+ * @returns Number of content duplicates needed (minimum 1)
2953
3734
  */
2954
3735
  const calculateDuplicates = (marqueeChildrenWidth, containerMarqueeWidth, isVertical, props) => {
3736
+ // If not filling, content presumably already spans the container
2955
3737
  if (!props.fill)
2956
3738
  return 1;
3739
+ /**
3740
+ * Determine the space we need to fill
3741
+ * - Vertical: Use viewport height (since container is rotated 90°)
3742
+ * - Horizontal: Use container width
3743
+ */
2957
3744
  const targetWidth = isVertical ? window.innerHeight : containerMarqueeWidth;
3745
+ /**
3746
+ * Calculate required duplicates
3747
+ * Math.ceil ensures we have enough copies to fully cover the target width
3748
+ * Even if the last copy is partially visible, it prevents gaps during looping
3749
+ */
2958
3750
  return marqueeChildrenWidth < targetWidth
2959
3751
  ? Math.ceil(targetWidth / marqueeChildrenWidth)
2960
- : 1;
3752
+ : 1; // If content is already larger than target, one copy suffices
2961
3753
  };
2962
3754
  /**
2963
- * Determines the minimum width for marquee elements
3755
+ * Determines the minimum width for marquee elements based on content and container
3756
+ *
3757
+ * This function ensures marquee elements have appropriate dimensions for their content
3758
+ * and container context, handling different modes and orientations.
3759
+ *
3760
+ * Width determination logic:
3761
+ * 1. **Fill mode**: Auto width lets content size naturally
3762
+ * 2. **Rotation alignment**: Use content height as width (rotated dimensions)
3763
+ * 3. **Undersized content**: Stretch to 100% to fill container
3764
+ * 4. **Oversized content**: Use actual content width for overflow scrolling
3765
+ *
3766
+ * @param marqueesChildren - Array of content elements for dimension measurement
3767
+ * @param totalWidth - Combined width of all content elements
3768
+ * @param containerMarqueeWidth - Available container width
3769
+ * @param props - Configuration object containing fill and alignment settings
3770
+ * @returns CSS width value (string with units or number for pixels)
2964
3771
  */
2965
3772
  const getMinWidth = (marqueesChildren, totalWidth, containerMarqueeWidth, props) => {
2966
3773
  const { fill = false, alignRotationWithY = false } = props;
3774
+ // Fill mode: Let content size itself naturally
2967
3775
  if (fill)
2968
3776
  return "auto";
3777
+ /**
3778
+ * Rotation alignment mode: Use height as width
3779
+ * Since content is rotated 90°, height becomes the effective width
3780
+ */
2969
3781
  if (alignRotationWithY && marqueesChildren.length > 0) {
2970
3782
  return `${marqueesChildren[0].offsetHeight}px`;
2971
3783
  }
3784
+ /**
3785
+ * Content smaller than container: Stretch to fill
3786
+ * Prevents awkward gaps in the marquee display
3787
+ */
2972
3788
  if (totalWidth < containerMarqueeWidth)
2973
3789
  return "100%";
3790
+ /**
3791
+ * Content larger than container: Use actual content width
3792
+ * Allows content to overflow and scroll properly
3793
+ */
2974
3794
  return `${totalWidth}px`;
2975
3795
  };
2976
- /**
2977
- * Creates a simple infinite marquee animation
2978
- */
2979
- const simpleAnimation = (marquees, marqueeChildrenDimension, isReverse, props) => {
2980
- const { spacing = 16, speed = 100, loop = -1 } = props;
2981
- const tween = gsap.to(marquees, {
2982
- xPercent: (_, el) => {
2983
- const w = parseFloat(String(gsap.getProperty(el, "width", "px")));
2984
- const xPercent = ((w + spacing) / w) * 100;
2985
- return -xPercent;
2986
- },
2987
- duration: (marqueeChildrenDimension - spacing) / speed,
2988
- repeat: loop,
2989
- ease: "none",
2990
- });
2991
- tween.play();
2992
- if (isReverse) {
2993
- tween.totalTime(tween.rawTime() + tween.duration() * 100);
2994
- tween.reverse();
2995
- }
2996
- };
2997
3796
  /**
2998
3797
  * Creates a complex fill-based marquee animation with seamless looping
3798
+ *
3799
+ * This is the core animation engine that creates smooth, continuous scrolling.
3800
+ * It handles the complex math required for seamless looping by calculating
3801
+ * precise positions and durations for each content element.
3802
+ *
3803
+ * Animation Strategy:
3804
+ * 1. **Position Calculation**: Convert pixel positions to percentages for responsive scaling
3805
+ * 2. **Seamless Looping**: Calculate track length and loop points to prevent gaps
3806
+ * 3. **Staggered Animation**: Each element starts at different times for smooth flow
3807
+ * 4. **Direction Handling**: Support forward and reverse directions with proper timing
3808
+ *
3809
+ * Technical Details:
3810
+ * - Uses xPercent for percentage-based positioning (responsive to element width changes)
3811
+ * - Creates two-part animation: main movement + seamless loop reset
3812
+ * - Calculates precise durations based on distance and speed for consistent motion
3813
+ *
3814
+ * @param elementsToAnimate - Array of DOM elements to animate (content or containers)
3815
+ * @param startX - Starting X position reference point
3816
+ * @param tl - GSAP timeline to add animations to
3817
+ * @param isReverse - Whether animation should play in reverse direction
3818
+ * @param props - Configuration object with spacing, speed, delay, and other settings
2999
3819
  */
3000
- const fillAnimation = (marqueesChildren, startX, tl, isReverse, props) => {
3001
- const { spacing = 16, speed = 100 } = props;
3002
- const widths = [];
3003
- const xPercents = [];
3004
- const latestPos = marqueesChildren.length - 1;
3005
- // Set initial positions and calculate percentages
3006
- gsap.set(marqueesChildren, {
3820
+ const coreAnimation = (elementsToAnimate, startX, tl, isReverse, props) => {
3821
+ const { spacing = 16, speed = 100, delay = 0, paused = false, alignRotationWithY = false, } = props;
3822
+ // Arrays to store calculated values for each element
3823
+ const widths = []; // Element widths in pixels
3824
+ const xPercents = []; // Current positions as percentages
3825
+ const latestPos = elementsToAnimate.length - 1; // Index of last element
3826
+ /**
3827
+ * Initialize positions and calculate percentage values
3828
+ *
3829
+ * GSAP's xPercent property positions elements relative to their own width:
3830
+ * - 0% = element's left edge at current x position
3831
+ * - -100% = element's right edge at current x position
3832
+ * - 100% = element positioned one full width to the right
3833
+ *
3834
+ * This approach makes animations responsive to width changes
3835
+ */
3836
+ gsap$1.set(elementsToAnimate, {
3007
3837
  xPercent: (i, el) => {
3008
- const w = (widths[i] = parseFloat(String(gsap.getProperty(el, "width", "px"))));
3838
+ // Get element width and store for later calculations
3839
+ const w = (widths[i] = parseFloat(String(gsap$1.getProperty(el, "width", "px"))));
3840
+ /**
3841
+ * Calculate current position as percentage of element width
3842
+ * Combines pixel position with any existing percentage offset
3843
+ */
3009
3844
  xPercents[i] =
3010
- (parseFloat(String(gsap.getProperty(el, "x", "px"))) / w) * 100 +
3011
- Number(gsap.getProperty(el, "xPercent"));
3845
+ (parseFloat(String(gsap$1.getProperty(el, "x", "px"))) / w) * 100 +
3846
+ Number(gsap$1.getProperty(el, "xPercent"));
3012
3847
  return xPercents[i];
3013
3848
  },
3014
3849
  });
3015
- gsap.set(marqueesChildren, { x: 0 });
3016
- // Calculate the total track length for seamless looping
3017
- const trackLength = marqueesChildren[latestPos].offsetLeft +
3850
+ // Reset x position to 0 since we're now using xPercent for positioning
3851
+ gsap$1.set(elementsToAnimate, { x: 0 });
3852
+ /**
3853
+ * Calculate total track length for seamless looping
3854
+ *
3855
+ * Track length is the total distance content travels before looping back.
3856
+ * It includes:
3857
+ * - Distance from start to last element's left edge
3858
+ * - Last element's offset percentage in pixels
3859
+ * - Last element's full width
3860
+ * - Spacing gap after last element
3861
+ *
3862
+ * This ensures smooth transitions when content loops back to beginning
3863
+ */
3864
+ const trackLength = elementsToAnimate[latestPos].offsetLeft +
3018
3865
  (xPercents[latestPos] / 100) * widths[latestPos] -
3019
3866
  startX +
3020
- marqueesChildren[latestPos].offsetWidth +
3867
+ elementsToAnimate[latestPos].offsetWidth +
3021
3868
  spacing;
3022
- // Create animation timeline for each element
3023
- marqueesChildren.forEach((item, i) => {
3869
+ /**
3870
+ * Create staggered animation for each element
3871
+ *
3872
+ * Each element gets a two-part animation:
3873
+ * 1. Main movement: From start position to loop point
3874
+ * 2. Reset movement: From end of track back to start (seamless loop)
3875
+ */
3876
+ elementsToAnimate.forEach((item, i) => {
3877
+ // Current position in pixels
3024
3878
  const curX = (xPercents[i] / 100) * widths[i];
3879
+ // Distance from element to animation start point
3025
3880
  const distanceToStart = item.offsetLeft + curX - startX;
3026
- const distanceToLoop = distanceToStart + widths[i];
3881
+ /**
3882
+ * Calculate distance to complete loop point
3883
+ *
3884
+ * For rotation alignment mode, use height instead of width
3885
+ * since the element dimensions are effectively swapped
3886
+ */
3887
+ const distanceToLoop = alignRotationWithY
3888
+ ? distanceToStart + item.offsetHeight - spacing
3889
+ : distanceToStart + widths[i];
3890
+ /**
3891
+ * Part 1: Main animation - move from current position to loop point
3892
+ *
3893
+ * - Target: Position where element should loop back
3894
+ * - Duration: Based on distance and speed for consistent motion
3895
+ * - Start time: 0 (all elements start simultaneously but from different positions)
3896
+ */
3027
3897
  tl.to(item, {
3028
3898
  xPercent: ((curX - distanceToLoop) / widths[i]) * 100,
3029
3899
  duration: distanceToLoop / speed,
3030
- }, 0).fromTo(item, {
3900
+ }, 0 // Start immediately
3901
+ ).fromTo(item, {
3902
+ /**
3903
+ * Part 2 Start: Position element at end of track
3904
+ * This creates the illusion of seamless continuation
3905
+ */
3031
3906
  xPercent: ((curX - distanceToLoop + trackLength) / widths[i]) * 100,
3032
3907
  }, {
3908
+ /**
3909
+ * Part 2 End: Move back to original start position
3910
+ * Completes the seamless loop cycle
3911
+ */
3033
3912
  xPercent: xPercents[i],
3034
3913
  duration: (curX - distanceToLoop + trackLength - curX) / speed,
3035
- immediateRender: false,
3036
- }, distanceToLoop / speed);
3914
+ immediateRender: false, // Don't render start position immediately
3915
+ }, distanceToLoop / speed // Start after main animation completes
3916
+ );
3037
3917
  });
3038
- // Pre-render for performance optimization
3039
- tl.progress(1, true).progress(0, true);
3918
+ // Apply initial delay before starting animations
3919
+ tl.delay(delay);
3920
+ /**
3921
+ * Handle reverse direction animations
3922
+ *
3923
+ * For reverse marquees (right/down directions):
3924
+ * 1. Set timeline to end position
3925
+ * 2. Pause to prevent immediate playback
3926
+ * 3. Use delayed call to start reverse playback after initial delay
3927
+ * 4. Set up reverse completion handler for continuous looping
3928
+ */
3040
3929
  if (isReverse) {
3041
- tl.totalTime(tl.rawTime() + tl.duration() * 100);
3042
- tl.reverse();
3930
+ // If paused is requested, just pause and return
3931
+ if (paused) {
3932
+ tl.pause();
3933
+ return;
3934
+ }
3935
+ // Position timeline at end and pause
3936
+ tl.progress(1).pause();
3937
+ /**
3938
+ * Start reverse playback after delay
3939
+ * This creates the proper reverse scrolling effect
3940
+ */
3941
+ gsap$1.delayedCall(delay, () => {
3942
+ tl.reverse(); // Start playing backwards
3943
+ /**
3944
+ * Handle seamless looping in reverse direction
3945
+ * When reverse completes, restart from end position
3946
+ * This prevents new delay in continuous reverse scrolling
3947
+ */
3948
+ tl.eventCallback("onReverseComplete", () => {
3949
+ tl.totalTime(tl.rawTime() + tl.duration() * 100);
3950
+ });
3951
+ });
3043
3952
  }
3044
3953
  };
3045
3954
 
3046
3955
  const GSAPReactMarquee = forwardRef((props, ref) => {
3047
- const { children, className, dir = "left", loop = -1, paused = false, fill = false, alignRotationWithY, } = props;
3956
+ const { children, className, dir = "left", loop = -1, paused = false, fill = false, followScrollDir = false, scrollSpeed = 2.5, } = props;
3048
3957
  const rootRef = useRef(null) || ref;
3049
3958
  const containerRef = rootRef;
3050
3959
  const marqueeRef = useRef(null);
@@ -3055,14 +3964,14 @@ const GSAPReactMarquee = forwardRef((props, ref) => {
3055
3964
  if (!(marqueeRef === null || marqueeRef === void 0 ? void 0 : marqueeRef.current) || !containerRef.current)
3056
3965
  return;
3057
3966
  const containerMarquee = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current;
3058
- const marquees = gsap.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee"));
3059
- const marqueesChildren = gsap.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));
3967
+ const marquees = gsap$1.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee"));
3968
+ const marqueesChildren = gsap$1.utils.toArray(containerMarquee.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));
3060
3969
  const marquee = marqueeRef.current;
3061
3970
  if (!marquee || !marqueesChildren)
3062
3971
  return;
3063
- const tl = gsap.timeline({
3064
- repeat: loop,
3972
+ const tl = gsap$1.timeline({
3065
3973
  paused: paused,
3974
+ repeat: loop,
3066
3975
  defaults: { ease: "none" },
3067
3976
  onReverseComplete() {
3068
3977
  // start the animation from the end, when scrolling in reverse (up)
@@ -3073,29 +3982,78 @@ const GSAPReactMarquee = forwardRef((props, ref) => {
3073
3982
  setupContainerStyles(containerMarquee, marquees, marqueesChildren, isVertical, props);
3074
3983
  // Calculate dimensions and duplicates
3075
3984
  const containerMarqueeWidth = containerMarquee.offsetWidth;
3076
- const marqueeHeight = marquees[0].offsetHeight;
3077
3985
  const marqueeChildrenWidth = marqueesChildren[0].offsetWidth;
3078
3986
  const startX = marqueesChildren[0].offsetLeft;
3987
+ // Clamp scrollSpeed to valid range (1.1 to 4.0)
3988
+ const clampedScrollSpeed = Math.min(4, Math.max(1.1, scrollSpeed));
3079
3989
  setMarqueeDuplicates(calculateDuplicates(marqueeChildrenWidth, containerMarqueeWidth, isVertical, props));
3080
3990
  // Calculate total width and set marquee styles
3081
- const totalWidth = gsap.utils
3991
+ const totalWidth = gsap$1.utils
3082
3992
  .toArray(marquee.children)
3083
3993
  .map((child) => child.offsetWidth)
3084
3994
  .reduce((a, b) => a + b, 0);
3085
- gsap.set(marquees, {
3995
+ gsap$1.set(marquees, {
3086
3996
  minWidth: getMinWidth(marqueesChildren, totalWidth, containerMarqueeWidth, props),
3087
3997
  flex: fill ? "0 0 auto" : "1",
3088
3998
  });
3089
3999
  // Create appropriate animation based on fill setting
3090
- if (fill) {
3091
- fillAnimation(marqueesChildren, startX, tl, isReverse, props);
3092
- }
3093
- else {
3094
- simpleAnimation(marquees, isVertical && alignRotationWithY
3095
- ? marqueeHeight
3096
- : marqueeChildrenWidth, isReverse, props);
3097
- }
3098
- }, { dependencies: [marqueeDuplicates] });
4000
+ coreAnimation(fill ? marqueesChildren : marquees, startX, tl, isReverse, props);
4001
+ /**
4002
+ * GSAP Observer for scroll-based speed control
4003
+ *
4004
+ * This creates an interactive experience where users can control
4005
+ * the marquee speed and direction through mouse wheel scrolling.
4006
+ *
4007
+ * Behavior:
4008
+ * - Scroll down: Increases speed in normal direction
4009
+ * - Scroll up: Increases speed in reverse direction or slows normal direction
4010
+ * - Speed changes are smoothly animated with acceleration and deceleration phases
4011
+ * - ScrollSpeed multiplier is applied and clamped to valid range
4012
+ */
4013
+ Observer.create({
4014
+ onChangeY(self) {
4015
+ if (!followScrollDir)
4016
+ return;
4017
+ let factor = clampedScrollSpeed * (isReverse ? -1 : 1);
4018
+ if (self.deltaY < 0) {
4019
+ factor *= -1;
4020
+ }
4021
+ /**
4022
+ * Create smooth speed transition animation
4023
+ *
4024
+ * Phase 1: Quick acceleration to new speed (0.2s)
4025
+ * - timeScale: Controls timeline playback speed (higher = faster)
4026
+ * - factor * 2.5: Initial speed boost for responsive feel
4027
+ * - overwrite: Cancels any previous speed animations
4028
+ *
4029
+ * Phase 2: Gradual deceleration to sustained speed (1s delay + 1s duration)
4030
+ * - factor / 2.5: Settle to a more moderate sustained speed
4031
+ * - "+=0.3": Wait 0.3 seconds before starting deceleration
4032
+ */
4033
+ gsap$1
4034
+ .timeline({
4035
+ defaults: {
4036
+ ease: "none",
4037
+ },
4038
+ })
4039
+ .to(tl, {
4040
+ timeScale: factor * 2.5,
4041
+ duration: 0.2,
4042
+ overwrite: true,
4043
+ })
4044
+ .to(tl, { timeScale: factor / 2.5, duration: 1 }, "+=0.3");
4045
+ },
4046
+ });
4047
+ }, {
4048
+ dependencies: [marqueeDuplicates],
4049
+ });
4050
+ /**
4051
+ * Generate cloned marquee elements for seamless looping
4052
+ *
4053
+ * Creates multiple copies of the content based on calculated duplicates.
4054
+ * Each clone maintains the same structure and styling as the original.
4055
+ * Memoized to prevent unnecessary re-renders when dependencies haven't changed.
4056
+ */
3099
4057
  const clonedMarquees = useMemo(() => {
3100
4058
  if (!Number.isFinite(marqueeDuplicates) || marqueeDuplicates <= 0)
3101
4059
  return null;
@@ -3105,5 +4063,5 @@ const GSAPReactMarquee = forwardRef((props, ref) => {
3105
4063
  });
3106
4064
  GSAPReactMarquee.displayName = "GSAPReactMarquee";
3107
4065
 
3108
- export { calculateDuplicates, cn, GSAPReactMarquee as default, fillAnimation, getMinWidth, setupContainerStyles, simpleAnimation };
4066
+ export { calculateDuplicates, cn, coreAnimation, GSAPReactMarquee as default, getMinWidth, setupContainerStyles };
3109
4067
  //# sourceMappingURL=index.esm.js.map