1 2 3 4/* ================================ 5 GSAP ANIMATIONS 6 ================================ */ 7 8 9/* ================================ 10 0. GSAP REGISTER & SETUP. REMEMBER YOU NEED TO LOAD THE GSAP AND SCROLLTRIGGER SCRIPTS FIRST 11================================ */ 12gsap.registerPlugin(ScrollTrigger, ScrollSmoother); 13 14 15 16/* --------------------------------- 17 1. Shrink Banner on Scroll 18--------------------------------- */ 19function shrinkBannerContents({ target, start = 100 } = {}) { 20 const banner = document.querySelector(target); 21 if (!banner) return; 22 23 gsap.registerPlugin(ScrollTrigger); 24 25 ScrollTrigger.create({ 26 trigger: document.body, 27 start: `${start} top`, 28 endTrigger: document.body, 29 end: "bottom top", // ensures trigger runs through full page 30 toggleClass: { targets: banner, className: "is-shrunk" }, 31 onLeaveBack: () => banner.classList.remove("is-shrunk"), 32 }); 33} 34 35/* --------------------------------- 36 2. Card Fade + Hover Animation 37--------------------------------- */ 38function animateCardImages({ 39 cardSelector = ".card", 40 imgSelector = ".card-img-top", 41 btnSelector = ".btn", 42 scrollY = 0, 43 fadeDuration = 1, 44 hoverScale = 1.01, 45 hoverDuration = 1.2, 46 btnLiftY = -0.5, 47 btnShadowFrom = "0 2px 6px rgba(0,0,0,0.10)", 48 btnShadowTo = "0 8px 18px rgba(0,0,0,0.14)", 49 shimmerDuration = 0.3 50} = {}) { 51 const cards = document.querySelectorAll(cardSelector); 52 if (!cards.length) return; 53 54 cards.forEach(card => { 55 const img = card.querySelector(imgSelector); 56 const btn = card.querySelector(btnSelector); 57 58 // IMAGE ANIMATION 59 if (img) { 60 gsap.set(img, { 61 opacity: 1, 62 y: 0, 63 clearProps: "transform" 64 }); 65 66 gsap.fromTo( 67 img, 68 { 69 opacity: 0, 70 y: scrollY 71 }, 72 { 73 opacity: 1, 74 y: 0, 75 duration: fadeDuration, 76 ease: "power2.out", 77 immediateRender: false, 78 clearProps: "opacity,transform", 79 scrollTrigger: { 80 trigger: card, 81 start: "top 85%", 82 toggleActions: "play none none none", 83 once: true, 84 invalidateOnRefresh: true 85 } 86 } 87 ); 88 89 const hoverTween = gsap.to(img, { 90 scale: hoverScale, 91 duration: hoverDuration, 92 paused: true, 93 ease: "power2.out", 94 transformOrigin: "center center" 95 }); 96 97 card.addEventListener("mouseenter", () => hoverTween.play()); 98 card.addEventListener("mouseleave", () => hoverTween.reverse()); 99 } 100 101 // BUTTON ENHANCEMENT 102 if (btn) { 103 btn.style.position = "relative"; 104 btn.style.overflow = "hidden"; 105 106 // set a soft resting shadow 107 gsap.set(btn, { 108 boxShadow: btnShadowFrom 109 }); 110 111 // add shimmer layer once 112 let shimmer = btn.querySelector(".btn-shimmer"); 113 if (!shimmer) { 114 shimmer = document.createElement("span"); 115 shimmer.className = "btn-shimmer"; 116 btn.appendChild(shimmer); 117 } 118 119 // make sure shimmer starts hidden off to the left 120 gsap.set(shimmer, { 121 xPercent: -140, 122 opacity: 1 123 }); 124 125 // button lift/shadow tween 126 const liftTween = gsap.to(btn, { 127 y: btnLiftY, 128 boxShadow: btnShadowTo, 129 duration: 0.25, 130 ease: "power2.out", 131 paused: true 132 }); 133 134 // shimmer sweep tween 135 const shimmerTween = gsap.fromTo( 136 shimmer, 137 { 138 xPercent: -140, 139 opacity: 1 140 }, 141 { 142 xPercent: 140, 143 opacity: 1, 144 duration: shimmerDuration, 145 ease: "power2.out", 146 paused: true 147 } 148 ); 149 150 // ENTER 151 card.addEventListener("mouseenter", () => { 152 liftTween.play(); 153 154 // kill anything currently running on shimmer 155 gsap.killTweensOf(shimmer); 156 157 // hard reset before starting again 158 gsap.set(shimmer, { 159 xPercent: -140, 160 opacity: 1 161 }); 162 163 // fresh shimmer 164 gsap.to(shimmer, { 165 xPercent: 140, 166 duration: shimmerDuration, 167 ease: "power2.out" 168 }); 169 }); 170 171 // LEAVE 172 card.addEventListener("mouseleave", () => { 173 liftTween.reverse(); 174 175 // stop any in-progress shimmer cleanly 176 gsap.killTweensOf(shimmer); 177 178 // soft fade out, then reset 179 gsap.to(shimmer, { 180 opacity: 0, 181 duration: 0.15, 182 ease: "power1.out", 183 onComplete: () => { 184 gsap.set(shimmer, { 185 xPercent: -140, 186 opacity: 1 187 }); 188 } 189 }); 190 }); 191 } 192 }); 193} 194 195/* --------------------------------- 196 3. Parallax for Carousel Images 197--------------------------------- */ 198function parallaxCarouselImage({ 199 selector, 200 y = 100, 201 start = "top top", 202 end = "bottom top" 203}) { 204 const imgs = document.querySelectorAll(selector); 205 if (!imgs.length) return; 206
207 imgs.forEach(img => { 208 gsap.to(img, { 209 y, 210 ease: "none", 211 scrollTrigger: { 212 trigger: img.closest("section"), 213 start, 214 end, 215 scrub: true, 216 invalidateOnRefresh: true 217 } 218 }); 219 }); 220} 221 222/* --------------------------------- 223 4. Parallax Backgrounds (.parallax) 224--------------------------------- */ 225function parallaxBackgrounds() { 226 document.querySelectorAll(".parallax").forEach(el => { 227 gsap.to(el, { 228 backgroundPositionY: "5%", 229 ease: "none", 230 scrollTrigger: { 231 trigger: el, 232 start: "top top", 233 end: "bottom bottom", 234 scrub: 0.5, 235 invalidateOnRefresh: true 236 } 237 }); 238 }); 239} 240 241 242/* --------------------------------- 243 5. Smooth Scroll Setup 244--------------------------------- */ 245function smoothScrollSetup() { 246 // Initialize smooth scrolling 247 ScrollSmoother.create({ 248 wrapper: ".smoothScrollContain", // your main smooth scroll container 249 content: ".smoothContent", // adjust if needed 250 smooth: 1.2, // scroll smoothing strength 251 effects: true, // enable effects (parallax etc) 252 normalizeScroll: true, // handles browser quirks 253 ignoreMobileResize: true, // improves mobile stability 254 fixedElements: ".sticky-top, .offcanvas, .cc-window, .cc-revoke" 255 }); 256} 257 258 259/* --------------------------------- 260 6. Animate Slide 261--------------------------------- */ 262function animateRowIn({ 263 targetRow = ".animateRow", // class or selector for rows to animate 264 distance = 80, // slide distance (px) 265 duration = 0.4, // animation duration (seconds) 266 delay = 0, // delay before animation starts (seconds) 267 direction = "lr", // slide direction: "lr" = leftâright, "rl" = rightâleft, "tb" = topâbottom, "bt" = bottomâtop 268 stagger = false // staggered fade-in inside each column (false or number) 269} = {}) { 270 if (!gsap || !ScrollTrigger) { 271 console.warn("GSAP or ScrollTrigger not loaded"); 272 return; 273 } 274 275 gsap.registerPlugin(ScrollTrigger); 276 277 const rows = document.querySelectorAll(targetRow); 278 if (!rows.length) return; 279 280 rows.forEach((row) => { 281 const cols = row.querySelectorAll(".col-md-6, .col-lg-6"); 282 if (!cols.length) return; 283 284 const tl = gsap.timeline({ 285 scrollTrigger: { 286 trigger: row, 287 start: "top 85%", 288 toggleActions: "play none none none", 289 once: true 290 }, 291 delay: delay 292 }); 293 294 let leftVars = {}, rightVars = {}; 295 296 // Set animation directions 297 switch (direction) { 298 case "lr": // left-right 299 leftVars = { x: -distance }; 300 rightVars = { x: distance }; 301 break; 302 case "rl": // right-left 303 leftVars = { x: distance }; 304 rightVars = { x: -distance }; 305 break; 306 case "tb": // top-bottom 307 leftVars = { y: -distance }; 308 rightVars = { y: distance }; 309 break; 310 case "bt": // bottom-top 311 leftVars = { y: distance }; 312 rightVars = { y: -distance }; 313 break; 314 default: 315 leftVars = { x: -distance }; 316 rightVars = { x: distance }; 317 } 318 319 // Animate both columns (or single col gracefully) 320 tl.from(cols[0], { 321 ...leftVars, 322 opacity: 0, 323 duration: duration, 324 ease: "power3.out" 325 }); 326 327 if (cols[1]) { 328 tl.from(cols[1], { 329 ...rightVars, 330 opacity: 0, 331 duration: duration, 332 ease: "power3.out" 333 }, `-=${duration / 2}`); // overlap slightly 334 } 335 336 // Optional staggered fade-ins inside the row 337 if (stagger) { 338 const fadeTargets = row.querySelectorAll("p, h1, h2, h3, h4, h5, h6, img"); 339 if (fadeTargets.length) { 340 tl.from(fadeTargets, { 341 y: 20, 342 opacity: 0, 343 duration: 0.6, 344 stagger: stagger, 345 ease: "power2.out" 346 }, "-=0.3"); 347 } 348 } 349 }); 350} 351 352 353function animatePanels({ 354 selectors = [".leadIns .leadIn", ".navImageGrid .navCard"], 355 revealY = 24, 356 revealScale = 0.985, 357 revealDuration = 0.8, 358 stagger = 0.12, 359 start = "top 85%" 360} = {}) { 361 gsap.registerPlugin(ScrollTrigger); 362
363 selectors.forEach(selector => { 364 document.querySelectorAll(selector).forEach(panel => { 365 gsap.set(panel, { 366 transformOrigin: "center center", 367 willChange: "transform, opacity" 368 }); 369 }); 370 371 const groups = new Map(); 372 373 document.querySelectorAll(selector).forEach(panel => { 374 const parentRow = panel.closest(".row, .navImageGrid"); 375 const key = parentRow || panel.parentNode; 376 377 if (!groups.has(key)) groups.set(key, []); 378 groups.get(key).push(panel); 379 }); 380 381 groups.forEach((panels, triggerEl) => { 382 gsap.fromTo( 383 panels, 384 { 385 opacity: 0, 386 y: revealY, 387 scale: revealScale 388 }, 389 { 390 opacity: 1, 391 y: 0, 392 scale: 1, 393 duration: revealDuration, 394 stagger: stagger, 395 ease: "power2.out", 396 scrollTrigger: { 397 trigger: triggerEl, 398 start: start, 399 toggleActions: "play none none none" 400 } 401 } 402 ); 403 }); 404 405 document.querySelectorAll(selector).forEach(panel => { 406 const img = panel.querySelector("img"); 407 408 panel.addEventListener("mouseenter", () => { 409 gsap.to(panel, { 410 y: -4, 411 scale: 1.01, 412 boxShadow: "0 12px 28px rgba(0,0,0,0.14)", 413 duration: 0.3, 414 ease: "power2.out", 415 overwrite: true 416 }); 417 418 if (img) { 419 gsap.to(img, { 420 scale: 1.04, 421 duration: 0.6, 422 ease: "power2.out", 423 overwrite: true 424 }); 425 } 426 }); 427 428 panel.addEventListener("mouseleave", () => { 429 gsap.to(panel, { 430 y: 0, 431 scale: 1, 432 boxShadow: "0 6px 18px rgba(0,0,0,0.08)", 433 duration: 0.3, 434 ease: "power2.out", 435 overwrite: true 436 }); 437 438 if (img) { 439 gsap.to(img, { 440 scale: 1, 441 duration: 0.6, 442 ease: "power2.out", 443 overwrite: true 444 }); 445 } 446 }); 447 }); 448 }); 449} 450 451 452/* --------------------------------- 453 7. Event Intro Hero Animation 454--------------------------------- */ 455function animateEventIntroHero({ 456 panelSelector = ".eventDetailIntroPanel", 457 imageSelector = ".eventHeroImage, .eventDetailIntroPanel img", 458 textSelector = ".summaryInfo > *", 459 imageRevealY = 2, 460 imageRevealScale = 1.1, 461 imageDuration = 0.2, 462 textRevealY = 22, 463 textDuration = 0.3, 464 textStagger = 0.1, 465 parallaxYPercent = 6, 466 parallaxStart = "top bottom", 467 parallaxEnd = "bottom top" 468} = {}) { 469 const panels = document.querySelectorAll(panelSelector); 470 if (!panels.length) return; 471 472 panels.forEach(panel => { 473 const image = panel.querySelector(imageSelector); 474 const textItems = panel.querySelectorAll(textSelector); 475 476 if (image) { 477 gsap.set(image, { 478 autoAlpha: 0, 479 y: imageRevealY, 480 scale: imageRevealScale, 481 transformOrigin: "center center", 482 willChange: "transform, opacity" 483 }); 484 } 485 486 if (textItems.length) { 487 gsap.set(textItems, { 488 autoAlpha: 0, 489 y: textRevealY, 490 willChange: "transform, opacity" 491 }); 492 } 493 494 const tl = gsap.timeline({ 495 defaults: { 496 ease: "power2.out" 497 } 498 }); 499 500 if (image) { 501 tl.to(image, { 502 autoAlpha: 1, 503 y: 0, 504 scale: 1, 505 duration: imageDuration 506 }); 507 } 508 509 if (textItems.length) { 510 tl.to(textItems, { 511 autoAlpha: 1, 512 y: 0, 513 duration: textDuration, 514 stagger: textStagger 515 }, image ? "-=0.7" : 0); 516 } 517 518 if (image) { 519 gsap.to(image, { 520 yPercent: parallaxYPercent, 521 ease: "none", 522 scrollTrigger: { 523 trigger: panel, 524 start: parallaxStart, 525 end: parallaxEnd, 526 scrub: 1, 527 invalidateOnRefresh: true 528 } 529 }); 530 } 531 }); 532} 533 534 535 536/* --------------------------------- 537 8. Banner Gradient Intro Animation 538--------------------------------- */ 539function animateBannerGradientIntro({ 540 selector = ".bannerSection", 541 duration = 1.8, 542 delay = 0.1, 543 angleFrom = 0, 544 angleTo = -50, 545 stop1From = 0, 546 stop1To = 20, 547 stop2From = 10, 548 stop2To = 30, 549 alpha1From = 0.8, 550 alpha1To = 0.7, 551 alpha2From = 0.05, 552 alpha2To = 0.2 553} = {}) { 554 const banners = document.querySelectorAll(selector); 555 if (!banners.length) return; 556
557 banners.forEach(banner => { 558 gsap.fromTo( 559 banner, 560 { 561 "--banner-angle": angleFrom + "deg", 562 "--banner-stop-1": stop1From + "%", 563 "--banner-stop-2": stop2From + "%", 564 "--banner-alpha-1": alpha1From, 565 "--banner-alpha-2": alpha2From 566 }, 567 { 568 "--banner-angle": angleTo + "deg", 569 "--banner-stop-1": stop1To + "%", 570 "--banner-stop-2": stop2To + "%", 571 "--banner-alpha-1": alpha1To, 572 "--banner-alpha-2": alpha2To, 573 duration: duration, 574 delay: delay, 575 ease: "power2.out" 576 } 577 ); 578 }); 579} 580 581 582 583/* --------------------------------- 584 Initialization & Helpers 585--------------------------------- */ 586function initGSAPAnimations() { 587 // Banner shrink 588 shrinkBannerContents({ target: ".topBannerContainer" }); 589 590 591 // Card fade & hover 592 animateCardImages({ 593 cardSelector: ".card", 594 imgSelector: ".card-img-top", 595 hoverScale: 1.04 596 }); 597 598 // Carousel parallax 599 parallaxCarouselImage({ 600 selector: "#fpImageDiv .carousel-item img", 601 y: 180 602 }); 603 604 // Background parallax 605 parallaxBackgrounds(); 606 607 animatePanels(); 608 609 animateBannerGradientIntro({ 610 selector: ".bannerSection", 611 duration: 1, 612 angleFrom: 30, 613 angleTo: 45, 614 stop1From: 0, 615 stop1To: 30, 616 stop2From: 20, 617 stop2To: 40, 618 alpha2From: 0.1, 619 alpha2To: 0 620 }); 621 622 animateEventIntroHero({ 623 panelSelector: ".introSection", 624 imageSelector: ".introSection img", 625 textSelector: ".introSection > *", 626 parallaxYPercent:2 627 }); 628 629 animateEventIntroHero({ 630 panelSelector: ".eventDetailIntroPanel", 631 imageSelector: ".eventHeroImage, .eventDetailIntroPanel img", 632 textSelector: ".summaryInfo > *", 633 parallaxYPercent:2 634 }); 635 636 //Smooth Scroll 637 //smoothScrollSetup(); 638 639 /* animateRowIn({ 640 targetRow: ".animateSlide", 641 direction: "rl", 642 distance: 20, 643 duration: 0.3, 644 stagger: 0.1 645 }); */ 646 647 // Final refresh to ensure sync 648 ScrollTrigger.refresh(true); 649 650} 651 652//CUSTOM BBF 653 654 655 656 657 658 659 660 661/* --------------------------------- 662 Global Event Listeners 663--------------------------------- */ 664 665// Wait until everything is ready (avoids Chrome flicker) 666window.addEventListener("load", () => { 667 requestAnimationFrame(initGSAPAnimations); 668}); 669 670// Debounced resize refresh 671let resizeTimer; 672window.addEventListener("resize", () => { 673 clearTimeout(resizeTimer); 674 resizeTimer = setTimeout(() => ScrollTrigger.refresh(), 250); 675}); 676 677// Recalculate if page is scrolled to top after reload 678window.addEventListener( 679 "scroll", 680 () => { 681 if (window.scrollY === 0) ScrollTrigger.refresh(); 682 }, 683 { passive: true } 684); 685 686 687 688
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.