1( function( $ ) { 2 3 function textAnimated($scope) { 4 const $elements = $scope.find('.text-animated'); 5 if (!$elements.length) return; 6 7 const getSplitAttrs = (type) => { 8 switch (type) { 9 case 'words': 10 return { type: 'words', wordsClass: 'word', tag: 'div' }; 11 case 'chars': 12 return { type: 'words,chars', wordsClass: 'word', charsClass: 'char', tag: 'div' }; 13 default: 14 return { type: 'lines', linesClass: 'line', tag: 'div' }; 15 } 16 }; 17 18 const getAnimateTarget = (splitText, type) => { 19 if (!splitText) return []; 20 if (type === 'chars') return splitText.chars || []; 21 if (type === 'words') return splitText.words || []; 22 return splitText.lines || []; 23 }; 24 25 const baseGsapParams = { 26 x: 0, 27 y: 0, 28 opacity: 1, 29 rotation: 0, 30 rotateX: 0, 31 duration: 0.5, 32 delay: 0, 33 scale: 1, 34 force3D: true, 35 scrollTrigger: { 36 start: "50px bottom", 37 end: "bottom bottom", 38 toggleActions: "play none none none", 39 scrub: false, 40 }, 41 }; 42 43 const applyEffects = (el) => { 44 const $el = $(el); 45 const $text = $el.find('> .pxl-title-text, > .pxl-subtitle-text'); 46 const splitType = $el.data('split-text') || 'lines'; 47 const elClass = $el.attr('class'); 48 49 if (!$text.length) return; 50 51 // reset SplitText nếu Äã có 52 if (el.splitTextInstance) { 53 el.splitTextInstance.revert(); 54 el.splitTextInstance = null; 55 } 56 57 // tạo SplitText 58 const splitText = new SplitText($text[0], getSplitAttrs(splitType)); 59 el.splitTextInstance = splitText; 60 $el.addClass('text-splitted'); 61 62 const animateTarget = getAnimateTarget(splitText, splitType); 63 if (!animateTarget.length) return; 64 65 // tÃnh stagger 66 let stagger = 0.02; 67 if (splitType === 'words') stagger *= 2; 68 if (splitType === 'lines') stagger *= 4; 69 70 // clone gsapParams 71 const gsapParams = { ...baseGsapParams, stagger, scrollTrigger: { ...baseGsapParams.scrollTrigger, trigger: el } }; 72 73 // xá» lý hiá»u ứng theo class 74 if (elClass.includes('text-fade-in')) { 75 gsapParams.opacity = 0; 76 if (elClass.includes('text-fade-in-right')) gsapParams.x = 30; 77 else if (elClass.includes('text-fade-in-left')) gsapParams.x = -30; 78 else if (elClass.includes('text-fade-in-up')) gsapParams.y = 30; 79 else if (elClass.includes('text-fade-in-down')) gsapParams.y = -30; 80 81 } else if (elClass.includes('text-explosion')) { 82 gsapParams.x = () => gsap.utils.random(-1000, 1000); 83 gsapParams.y = () => gsap.utils.random(-1000, 1000); 84 gsapParams.rotation = () => gsap.utils.random(-90, 90); 85 gsapParams.scale = () => gsap.utils.random(0.5, 1.5); 86 gsapParams.opacity = 0; 87 88 } else if (elClass.includes('text-zigzag-zoom')) { 89 animateTarget.forEach((target, i) => { 90 gsap.from(target, { 91 scale: (i % 2 === 0) ? 0 : 2, 92 opacity: 0, 93 duration: 0.75, 94 scrollTrigger: { 95 trigger: el, 96 start: "top 95%", 97 end: "top 60%", 98 toggleActions: "play none none none", 99 scrub: false, 100 // markers: true, 101 } 102 }); 103 }); 104 return; 105 106 } else if (elClass.includes('text-flip-x')) { 107 gsap.set($el[0], { perspective: '50px' }); 108 gsapParams.rotateX = 90; 109 gsapParams.opacity = 0; 110 111 } else if (elClass.includes('text-flip-y')) { 112 gsap.set($el[0], { perspective: '50px' }); 113 gsapParams.rotateY = 90; 114 gsapParams.opacity = 0; 115 116 } else if (elClass.includes('text-zoom-in')) { 117 gsapParams.scale = 0; 118 } 119 120 if (elClass.includes('text-reveal')) { 121 gsapParams.scrollTrigger.scrub = true; 122 } 123 124 // Äảm bảo hiá»n text trưá»c khi animate 125 $el.css('visibility', 'visible'); 126 127 // chạy animation 128 gsap.from(animateTarget, gsapParams); 129 }; 130 131 // khá»i tạo 132 $elements.each((i, el) => applyEffects(el)); 133 134 // handle resize + rebuild 135 let resizeTimeout; 136 $(window).on('resize', () => { 137 clearTimeout(resizeTimeout); 138 resizeTimeout = setTimeout(() => { 139 $elements.each((i, el) => { 140 if (el.splitTextInstance) { 141 el.splitTextInstance.revert(); 142 el.splitTextInstance = null; 143 } 144 $(el).removeClass('text-splitted'); 145 applyEffects(el); 146 }); 147 }, 300); 148 }); 149 } 150 151 152 $( window ).on( 'elementor/frontend/init', function() { 153 elementorFrontend.hooks.addAction( `frontend/element_ready/pxl_heading.default`, function( $scope ) { 154 textAnimated($scope); 155 }); 156 elementorFrontend.hooks.addAction( `frontend/element_ready/pxl_text_editor.default`, function( $scope ) { 157 textAnimated($scope); 158 }); 159 });
160 161})( jQuery );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.