1const $ = window.jQuery; 2 3const SERVICES_MOBILE_BREAKPOINT = 750; 4 5function initServices() { 6 const cards = document.querySelectorAll(".js_service_list__card"); 7 8 if (cards.length === 0) return; 9 10 if (typeof gsap === "undefined" || typeof ScrollTrigger === "undefined") return; 11 gsap.registerPlugin(ScrollTrigger); 12 13 const windowWidth = window.innerWidth; 14 const isMobile = windowWidth <= SERVICES_MOBILE_BREAKPOINT; 15 16 const cardConfigs = []; 17 const scrollTriggers = []; 18 19 // ШÐÐ 1: ÐолÑÑаем вÑе кооÑдинаÑÑ Ð¸ вÑÑоÑÑ ÐÐ ÑвоÑаÑÐ¸Ð²Ð°Ð½Ð¸Ñ ÐºÐ°ÑÑоÑек 20 cards.forEach((card, index) => { 21 const inner = card.querySelector(".service_list__card__inner"); 22 if (!inner) return; 23 24 const cardRect = card.getBoundingClientRect(); 25 const scrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop; 26 const cardTop = cardRect.top + scrollY; 27 const cardBottom = cardRect.bottom + scrollY; 28 29 const getCardHeight = () => { 30 const originalMaxHeight = card.style.maxHeight; 31 const originalOverflow = card.style.overflow; 32 card.style.maxHeight = "none"; 33 card.style.overflow = "visible"; 34 const height = inner.offsetHeight + parseFloat(getComputedStyle(card).paddingTop || 0) + parseFloat(getComputedStyle(card).paddingBottom || 0); 35 card.style.maxHeight = originalMaxHeight; 36 card.style.overflow = originalOverflow; 37 return height; 38 }; 39 40 const targetHeight = getCardHeight(); 41 42 cardConfigs.push({ 43 card, 44 inner, 45 index, 46 cardTop, 47 cardBottom, 48 targetHeight, 49 getCardHeight 50 }); 51 }); 52 53 // ШÐÐ 2: УÑÑанавливаем ÑвоÑаÑивание Ð´Ð»Ñ Ð²ÑÐµÑ ÐºÐ°ÑÑоÑек 54 const windowHeight = window.innerHeight; 55 const collapsedHeight = windowWidth <= 1024 ? "2.5rem" : "3rem"; 56 cardConfigs.forEach((config) => { 57 gsap.set(config.card, { 58 maxHeight: collapsedHeight, 59 opacity: 1 60 }); 61 }); 62 63 // ÐобилÑÐ½Ð°Ñ Ð²ÐµÑÑиÑ: ÑазвоÑÐ¾Ñ Ð¿Ð¾ Ð¾Ð´Ð½Ð¾Ð¼Ñ ÑÐ°Ð·Ñ Ð¿Ñи поÑвлении в зоне видимоÑÑи (без scrub â не ÑоÑмозиÑ) 64 if (isMobile) { 65 cardConfigs.forEach((config) => { 66 const { card, targetHeight } = config; 67 const initialHeight = "2.5rem"; 68 gsap.fromTo( 69 card, 70 { maxHeight: initialHeight }, 71 { 72 maxHeight: targetHeight + "px", 73 ease: "power2.out", 74 duration: 0.5, 75 scrollTrigger: { 76 trigger: card, 77 start: "top 85%", 78 toggleActions: "play none none none", 79 once: true 80 } 81 } 82 ); 83 }); 84 ScrollTrigger.refresh(); 85 return; 86 } 87 88 // ШÐÐ 3: ÐеÑкÑоп â ScrollTrigger Ñ scrub Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ каÑÑоÑки 89 cardConfigs.forEach((config, index) => { 90 const { card, cardTop, targetHeight } = config; 91 92 const startOffset = windowHeight * 0.6; // 60% Ð¾Ñ Ð²ÐµÑÑ Ð° ÑкÑана Ð´Ð»Ñ start 93 const endOffset = windowHeight * 0.6; // 60% Ð¾Ñ Ð²ÐµÑÑ Ð° ÑкÑана Ð´Ð»Ñ end 94 95 let startPosition, endPosition; 96 97 if (index === 0) { 98 startPosition = cardTop - startOffset; 99 const expandedCardBottom = cardTop + targetHeight; 100 endPosition = expandedCardBottom - endOffset; 101 } else { 102 const prevST = scrollTriggers[index - 1]; 103 if (prevST) { 104 startPosition = prevST.end; 105 const expandedCardBottom = cardTop + targetHeight; 106 endPosition = expandedCardBottom - endOffset; 107 } else { 108 startPosition = cardTop - startOffset; 109 const expandedCardBottom = cardTop + targetHeight; 110 endPosition = expandedCardBottom - endOffset; 111 } 112 } 113 114 const initialHeight = windowWidth <= 1024 ? "2.5rem" : "3rem"; 115 116 // Создаем анимаÑÐ¸Ñ Ð´Ð»Ñ ÑазвоÑаÑÐ¸Ð²Ð°Ð½Ð¸Ñ ÐºÐ°ÑÑоÑки 117 const expandAnimation = gsap.fromTo(card, 118 { maxHeight: initialHeight }, 119 { 120 maxHeight: targetHeight + "px", 121 ease: "power2.out", 122 paused: true 123 } 124 ); 125 126 // Создаем ScrollTrigger 127 const st = ScrollTrigger.create({ 128 trigger: card, 129 start: startPosition, 130 end: endPosition, 131 scrub: 1.5, 132 animation: expandAnimation 133 }); 134 135 scrollTriggers[index] = st; 136 137 // ÐбновлÑем ScrollTrigger поÑле ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ ÑÑиггеÑа 138 ScrollTrigger.refresh(); 139 140 // ÐÐ»Ñ Ð¿Ð¾ÑледÑÑÑÐ¸Ñ ÐºÐ°ÑÑоÑек пеÑеÑÑиÑÑваем end Ñ ÑÑеÑом ÑекÑÑей позиÑии поÑле ÑазвоÑаÑÐ¸Ð²Ð°Ð½Ð¸Ñ Ð¿ÑедÑдÑÑÐ¸Ñ 141 if (index > 0 && scrollTriggers[index - 1]) { 142 // ÐолÑÑаем ÑекÑÑÑÑ Ð¿Ð¾Ð·Ð¸ÑÐ¸Ñ ÐºÐ°ÑÑоÑки (поÑле ÑазвоÑаÑÐ¸Ð²Ð°Ð½Ð¸Ñ Ð¿ÑедÑдÑÑÐ¸Ñ ) 143 const cardRect = card.getBoundingClientRect(); 144 const scrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop; 145 const currentCardTop = cardRect.top + scrollY; 146 const windowHeight = window.innerHeight; 147 const endOffset = windowHeight * 0.6; // 60% Ð¾Ñ Ð²ÐµÑÑ Ð° ÑкÑана Ð´Ð»Ñ end 148 149 // ÐеÑеÑÑиÑÑваем end: когда низ ÑазвеÑнÑÑой каÑÑоÑки доÑÑÐ¸Ð³Ð½ÐµÑ 60% Ð¾Ñ Ð²ÐµÑÑ Ð° ÑкÑана 150 const expandedCardBottom = currentCardTop + targetHeight; 151 const newEnd = expandedCardBottom - endOffset; 152 153 // ÐбновлÑем end позиÑÐ¸Ñ 154 st.setPositions(st.start, newEnd); 155 156 // ÐбновлÑем ScrollTrigger поÑле Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð·Ð¸Ñий 157 ScrollTrigger.refresh(); 158 } 159 160 // ÐбновлÑем вÑÑоÑÑ Ð¿Ñи изменении ÑазмеÑа окна 161 const updateHeight = () => { 162 const newTargetHeight = config.getCardHeight(); 163 const currentWindowHeight = window.innerHeight; 164 const newInitialHeight = currentWindowHeight <= 1024 ? "2.5rem" : "3rem"; 165 166 // ÐбновлÑем наÑалÑнÑÑ Ð²ÑÑоÑÑ Ð² анимаÑии 167 expandAnimation.vars.maxHeight = newTargetHeight + "px"; 168 expandAnimation.fromVars.maxHeight = newInitialHeight; 169 170 ScrollTrigger.refresh(); 171 }; 172 173 window.addEventListener("resize", updateHeight); 174 }); 175} 176 177 178function initHorizontalDragScroll(selector, draggingClass) { 179 const el = document.querySelector(selector); 180 if (!el) return; 181 182 let isDown = false;
183 let startX; 184 let scrollLeft; 185 186 el.addEventListener("mousedown", (e) => { 187 isDown = true; 188 el.classList.add(draggingClass); 189 startX = e.pageX; 190 scrollLeft = el.scrollLeft; 191 }); 192 193 el.addEventListener("mouseleave", () => { 194 isDown = false; 195 el.classList.remove(draggingClass); 196 }); 197 198 el.addEventListener("mouseup", () => { 199 isDown = false; 200 el.classList.remove(draggingClass); 201 }); 202 203 el.addEventListener("mousemove", (e) => { 204 if (!isDown) return; 205 e.preventDefault(); 206 const walk = (e.pageX - startX) * 1.2; 207 el.scrollLeft = scrollLeft - walk; 208 }); 209} 210 211function initWorkWithBigDragScroll() { 212 initHorizontalDragScroll( 213 ".work_with_big__images", 214 "work_with_big__images--dragging", 215 ); 216} 217 218function initIsolationImagesDragScroll() { 219 initHorizontalDragScroll( 220 ".isolation_images__line", 221 "isolation_images__line--dragging", 222 ); 223} 224 225export default function services() { 226 return { 227 init: function () { 228 $(document).ready(function () { 229 initWorkWithBigDragScroll(); 230 initIsolationImagesDragScroll(); 231 }); 232 233 // Ðдем загÑÑзки DOM и GSAP 234 if (typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined") { 235 // ÐеболÑÑÐ°Ñ Ð·Ð°Ð´ÐµÑжка Ð´Ð»Ñ Ð³Ð°ÑанÑии загÑÑзки вÑÐµÑ ÑÑилей 236 setTimeout(() => { 237 initServices(); 238 }, 100); 239 } else { 240 // ÐÑли GSAP еÑе не загÑÑжен, ждем 241 $(document).ready(function () { 242 const checkGSAP = setInterval(() => { 243 if (typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined") { 244 clearInterval(checkGSAP); 245 setTimeout(() => { 246 initServices(); 247 }, 100); 248 } 249 }, 50); 250 }); 251 } 252 }, 253 }; 254}
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.