1/** 2 * @file 3 * Grid item stagger animation behavior. 4 */ 5(function (Drupal, once) { 6 'use strict'; 7 8 const GRID_CONFIG = { 9 containerSelector: '.js-animate-grid', 10 itemSelector: '.js-animate-grid-item', 11 duration: 0.96, 12 stagger: 0.212, 13 yOffsetPercent: 50, 14 ease: "power2.out", 15 startLocationRow1: "top 80%", 16 startLocationRow2: "top 85%", 17 debug: false, 18 desktopBreakpoint: "(min-width: 768px)", 19 mobileBreakpoint: "(max-width: 767px)" 20 }; 21 22 Drupal.behaviors.animateGrid = { 23 attach: function (context) { 24 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 25 return; 26 } 27 28 gsap.registerPlugin(ScrollTrigger); 29 30 const gridContainers = once('animateGrid', GRID_CONFIG.containerSelector, context); 31 32 gridContainers.forEach((gridContainer) => { 33 34 // Skip GSAP animation completely for tabs 2 and 3 35 if (gridContainer.closest('.is-tab-two-or-three')) { 36 return; 37 } 38 39 const gridItems = Array.from(gridContainer.querySelectorAll(GRID_CONFIG.itemSelector)); 40 const isMobileFade = gridContainer.classList.contains('js-mobile-fade'); 41 42 if (gridItems.length === 7) { 43 44 const row1 = gridItems.slice(0, 3); // Items 1, 2, 3 45 const row2 = gridItems.slice(3, 7); // Items 4, 5, 6, 7 46 47 // Helper function for the standard desktop/default behavior 48 const createDefaultAnimations = () => { 49 // Animate Row 1 50 gsap.fromTo(row1, 51 { yPercent: GRID_CONFIG.yOffsetPercent, autoAlpha: 0 }, 52 { 53 scrollTrigger: { 54 trigger: row1[0], 55 start: GRID_CONFIG.startLocationRow1, 56 markers: GRID_CONFIG.debug, 57 }, 58 duration: GRID_CONFIG.duration, 59 yPercent: 0, 60 autoAlpha: 1, 61 stagger: GRID_CONFIG.stagger, 62 ease: GRID_CONFIG.ease, 63 } 64 ); 65 66 // Animate Row 2 67 gsap.fromTo(row2, 68 { yPercent: GRID_CONFIG.yOffsetPercent, autoAlpha: 0 }, 69 { 70 scrollTrigger: { 71 trigger: row2[0], 72 start: GRID_CONFIG.startLocationRow2, 73 markers: GRID_CONFIG.debug, 74 }, 75 duration: GRID_CONFIG.duration, 76 yPercent: 0, 77 autoAlpha: 1, 78 stagger: GRID_CONFIG.stagger, 79 ease: GRID_CONFIG.ease, 80 } 81 ); 82 }; 83 84 // If the grid needs the specific mobile fade effect
85 if (isMobileFade) { 86 let mm = gsap.matchMedia(); 87 88 mm.add({ 89 isDesktop: GRID_CONFIG.desktopBreakpoint, 90 isMobile: GRID_CONFIG.mobileBreakpoint 91 }, (ctx) => { 92 let { isMobile } = ctx.conditions; 93 94 if (isMobile) { 95 // MOBILE LOGIC: Simple fade-in for all items simultaneously 96 gsap.fromTo(gridItems, 97 { 98 autoAlpha: 0, 99 yPercent: 0 // Explicitly setting to 0 so there is no vertical jump 100 }, 101 { 102 scrollTrigger: { 103 // Triggering on the container is safer since they all animate together 104 trigger: gridContainer, 105 start: GRID_CONFIG.startLocationRow1, 106 markers: GRID_CONFIG.debug, 107 }, 108 duration: 1.6, 109 autoAlpha: 1, 110 yPercent: 0, 111 stagger: 0, // 0 ensures they all fade in exactly at the same time 112 ease: GRID_CONFIG.ease, 113 } 114 ); 115 } else { 116 // DESKTOP LOGIC: Run the standard staggered rows 117 createDefaultAnimations(); 118 } 119 }); 120 } else { 121 // FALLBACK: Run the default animations for all screen sizes if the flag class isn't present 122 createDefaultAnimations(); 123 } 124 } 125 }); 126 } 127 }; 128})(Drupal, once);
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.