1/** 2 * TOAF Homepage â GSAP Animation System 3 * 4 * Scroll-triggered animations for the homepage sections. 5 * Uses ScrollTrigger for viewport-based reveals. 6 * Animation language aligned with Collectors Collective page. 7 * 8 * @package BDS Child Theme 9 * @author Umami Studio <[email protected]> 10 * @link https://umami.studio/ 11 */ 12 13(function () { 14 'use strict'; 15 16 if (typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') { 17 return; 18 } 19 20 gsap.registerPlugin(ScrollTrigger); 21 22 var SPEED = 1.0; 23 var EASE = 'power3.out'; 24 25 var CONFIG = { 26 header: { 27 wordY: 40, 28 wordDuration: 1 * SPEED, 29 wordStagger: 0.04, 30 contentY: 32, 31 contentDuration: 1 * SPEED, 32 overlineOffset: 0.32 * SPEED, 33 bodyOffset: 0.64 * SPEED, 34 linksOffset: 1.12 * SPEED 35 }, 36 block: { 37 y: 48, 38 duration: 1 * SPEED, 39 stagger: 0.16 40 }, 41 scroll: { 42 triggerMobile: 'top 70%', 43 triggerDesktop: 'top 80%' 44 }, 45 hero: { 46 parallaxScale: 1.1 47 }, 48 revealImages: { 49 initialOffset: 25, 50 finalOffset: 0, 51 durationMultiplier: 1.2, 52 delay: 0.1 53 } 54 }; 55 56 function isMobile() { 57 return window.innerWidth <= 768; 58 } 59 60 function getScrollTriggerStart() { 61 return isMobile() ? CONFIG.scroll.triggerMobile : CONFIG.scroll.triggerDesktop; 62 } 63 64 /** 65 * Split heading text into individual word spans for staggered animation. 66 */ 67 function splitHeadingIntoWords(heading) { 68 if (!heading || heading.dataset.wordsSplit === 'true') { 69 return heading ? Array.from(heading.querySelectorAll('.word')) : []; 70 } 71 72 var words = []; 73 74 function processNode(node) { 75 if (node.nodeType === Node.TEXT_NODE) { 76 var fragment = document.createDocumentFragment(); 77 var lines = node.textContent.split(/\r\n|\r|\n/); 78 79 lines.forEach(function (line, lineIndex) { 80 if (lineIndex > 0) { 81 fragment.appendChild(document.createElement('br')); 82 } 83 84 var parts = line.split(/(\s+)/); 85 parts.forEach(function (part) { 86 if (part.trim() === '') { 87 if (part) fragment.appendChild(document.createTextNode(part)); 88 } else { 89 var span = document.createElement('span'); 90 span.className = 'word'; 91 span.style.display = 'inline-block'; 92 span.textContent = part; 93 fragment.appendChild(span); 94 words.push(span); 95 } 96 }); 97 }); 98 99 node.parentNode.replaceChild(fragment, node); 100 } else if (node.nodeType === Node.ELEMENT_NODE) { 101 if (node.classList.contains('word')) { 102 words.push(node); 103 return; 104 } 105 Array.from(node.childNodes).forEach(processNode); 106 } 107 } 108 109 processNode(heading); 110 heading.dataset.wordsSplit = 'true'; 111 return words; 112 } 113 114 /** 115 * Set initial animation states for header elements. 116 */ 117 function setHeaderInitialStates(header) { 118 if (!header) return; 119 var overline = header.querySelector('.overline'); 120 var body = header.querySelector('.body'); 121 var links = header.querySelector('.links'); 122 123 if (overline) gsap.set(overline, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 124 if (body) gsap.set(body, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 125 if (links) gsap.set(links, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 126 } 127 128 /** 129 * Animate the hero header immediately on load (no ScrollTrigger). 130 */ 131 function animateHeroHeader(header) { 132 if (!header) return; 133 134 var overline = header.querySelector('.overline'); 135 var heading = header.querySelector('.heading'); 136 var body = header.querySelector('.body'); 137 var links = header.querySelector('.links'); 138 139 var words = heading ? splitHeadingIntoWords(heading) : []; 140 141 var tl = gsap.timeline(); 142 143 if (overline) { 144 tl.to(overline, { 145 opacity: 1, 146 y: 0, 147 duration: CONFIG.header.contentDuration, 148 ease: EASE, 149 onComplete: function () { gsap.set(overline, { willChange: 'auto' }); } 150 }, CONFIG.header.overlineOffset); 151 } 152 153 if (words.length) { 154 gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 155 tl.to(words, { 156 opacity: 1, 157 y: 0, 158 duration: CONFIG.header.wordDuration, 159 stagger: CONFIG.header.wordStagger, 160 ease: EASE, 161 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 162 }, CONFIG.header.overlineOffset + 0.1); 163 } 164 165 if (body) { 166 tl.to(body, { 167 opacity: 1, 168 y: 0, 169 duration: CONFIG.header.contentDuration, 170 ease: EASE, 171 onComplete: function () { gsap.set(body, { willChange: 'auto' }); } 172 }, CONFIG.header.bodyOffset); 173 } 174 175 if (links) { 176 tl.to(links, {
177 opacity: 1, 178 y: 0, 179 duration: CONFIG.header.contentDuration, 180 ease: EASE, 181 onComplete: function () { gsap.set(links, { willChange: 'auto' }); } 182 }, CONFIG.header.linksOffset); 183 } 184 } 185 186 /** 187 * Animate a section header: overline, heading words, body, links. 188 */ 189 function animateSectionHeader(header, trigger) { 190 if (!header) return; 191 192 var overline = header.querySelector('.overline'); 193 var heading = header.querySelector('.heading'); 194 var body = header.querySelector('.body'); 195 var links = header.querySelector('.links'); 196 197 var words = heading ? splitHeadingIntoWords(heading) : []; 198 199 var tl = gsap.timeline({ 200 scrollTrigger: { 201 trigger: trigger || header, 202 start: getScrollTriggerStart(), 203 toggleActions: 'play none play none' 204 } 205 }); 206 207 if (overline) { 208 tl.to(overline, { 209 opacity: 1, 210 y: 0, 211 duration: CONFIG.header.contentDuration, 212 ease: EASE, 213 onComplete: function () { gsap.set(overline, { willChange: 'auto' }); } 214 }, CONFIG.header.overlineOffset); 215 } 216 217 if (words.length) { 218 gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 219 tl.to(words, { 220 opacity: 1, 221 y: 0, 222 duration: CONFIG.header.wordDuration, 223 stagger: CONFIG.header.wordStagger, 224 ease: EASE, 225 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 226 }, CONFIG.header.overlineOffset + 0.1); 227 } 228 229 if (body) { 230 tl.to(body, { 231 opacity: 1, 232 y: 0, 233 duration: CONFIG.header.contentDuration, 234 ease: EASE, 235 onComplete: function () { gsap.set(body, { willChange: 'auto' }); } 236 }, CONFIG.header.bodyOffset); 237 } 238 239 if (links) { 240 tl.to(links, { 241 opacity: 1, 242 y: 0, 243 duration: CONFIG.header.contentDuration, 244 ease: EASE, 245 onComplete: function () { gsap.set(links, { willChange: 'auto' }); } 246 }, CONFIG.header.linksOffset); 247 } 248 } 249 250 /** 251 * Animate generic blocks with fade-up on scroll. 252 * Only handles elements still using data-animate-block. 253 */ 254 function animateBlocks(container) { 255 var blocks = container.querySelectorAll('[data-animate-block]'); 256 if (!blocks.length) return; 257 258 gsap.set(blocks, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 259 260 blocks.forEach(function (block) { 261 var revealImg = block.querySelector('.img-reveal img'); 262 if (revealImg) { 263 gsap.set(revealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 264 } 265 266 gsap.to(block, { 267 opacity: 1, 268 y: 0, 269 duration: CONFIG.block.duration, 270 ease: EASE, 271 scrollTrigger: { 272 trigger: block, 273 start: getScrollTriggerStart(), 274 toggleActions: 'play none play none' 275 }, 276 onComplete: function () { gsap.set(block, { willChange: 'auto' }); } 277 }); 278 279 if (revealImg) { 280 gsap.to(revealImg, { 281 yPercent: CONFIG.revealImages.finalOffset, 282 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 283 ease: 'power3.out', 284 scrollTrigger: { 285 trigger: block, 286 start: getScrollTriggerStart(), 287 toggleActions: 'play none play none' 288 }, 289 onComplete: function () { gsap.set(revealImg, { willChange: 'auto' }); } 290 }); 291 } 292 }); 293 } 294 295 /** 296 * Animate a group of cards/items with stagger and optional img-reveal. 297 */ 298 function animateCardGroup(cards, trigger, startOffset) { 299 if (!cards.length) return; 300 301 gsap.set(cards, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 302 303 cards.forEach(function (card) { 304 var revealImg = card.querySelector('.img-reveal img'); 305 if (revealImg) { 306 gsap.set(revealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 307 } 308 }); 309 310 var tl = gsap.timeline({ 311 scrollTrigger: { 312 trigger: trigger, 313 start: getScrollTriggerStart(), 314 toggleActions: 'play none play none' 315 } 316 }); 317 318 tl.to(cards, { 319 opacity: 1, 320 y: 0, 321 duration: CONFIG.block.duration, 322 stagger: CONFIG.block.stagger, 323 ease: EASE, 324 onComplete: function () { gsap.set(cards, { willChange: 'auto' }); } 325 }, startOffset || 0); 326 327 cards.forEach(function (card, index) { 328 var revealImg = card.querySelector('.img-reveal img'); 329 if (revealImg) { 330 tl.to(revealImg, { 331 yPercent: CONFIG.revealImages.finalOffset, 332 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 333 ease: 'power3.out', 334 onComplete: function () { gsap.set(revealImg, { willChange: 'auto' }); } 335 }, (startOffset || 0) + (index * CONFIG.block.stagger) + CONFIG.revealImages.delay); 336 } 337 }); 338 339 return tl; 340 } 341 342 /* ================================================================ 343 Hero Section 344 ================================================================ */ 345 346 function initHero() { 347 var section = document.querySelector('.hero-section'); 348 if (!section) return; 349 350 var header = section.querySelector('[data-animate-header]'); 351 setHeaderInitialStates(header); 352 animateHeroHeader(header); 353 354 var bg = section.querySelector('.hero-section__bg'); 355 if (bg && !bg.querySelector('iframe')) { 356 gsap.to(bg, { 357 scale: CONFIG.hero.parallaxScale, 358 ease: 'none', 359 scrollTrigger: { 360 trigger: section, 361 start: 'top top', 362 end: 'bottom top', 363 scrub: 0.5 364 } 365 }); 366 } 367 } 368 369 /* ================================================================ 370 What's Happening Section 371 ================================================================ */ 372 373 function initWhatsHappening() { 374 var section = document.querySelector('.whats-happening-section'); 375 if (!section) return; 376 377 var header = section.querySelector('[data-animate-header]'); 378 setHeaderInitialStates(header); 379 animateSectionHeader(header, section); 380 381 var cards = section.querySelectorAll('.wh-card'); 382 if (!cards.length) return; 383 384 gsap.set(cards, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 385 386 cards.forEach(function (card) { 387 var revealImg = card.querySelector('.img-reveal img'); 388 if (revealImg) { 389 gsap.set(revealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 390 } 391 }); 392 393 if (isMobile()) { 394 cards.forEach(function (card) { 395 gsap.to(card, { 396 opacity: 1, 397 y: 0, 398 duration: CONFIG.block.duration, 399 ease: EASE, 400 scrollTrigger: { 401 trigger: card, 402 start: CONFIG.scroll.triggerMobile, 403 toggleActions: 'play none play none' 404 }, 405 onComplete: function () { gsap.set(card, { willChange: 'auto' }); } 406 }); 407 408 var revealImg = card.querySelector('.img-reveal img'); 409 if (revealImg) { 410 gsap.to(revealImg, { 411 yPercent: CONFIG.revealImages.finalOffset, 412 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 413 ease: 'power3.out', 414 scrollTrigger: { 415 trigger: card, 416 start: CONFIG.scroll.triggerMobile, 417 toggleActions: 'play none play none' 418 }, 419 onComplete: function () { gsap.set(revealImg, { willChange: 'auto' }); } 420 }); 421 } 422 }); 423 } else { 424 animateCardGroup(cards, section, CONFIG.header.bodyOffset); 425 } 426 } 427 428 /* ================================================================ 429 In-Person Fair Section 430 ================================================================ */ 431 432 function initInPersonFair() { 433 var section = document.querySelector('.in-person-fair-section'); 434 if (!section) return; 435 436 var details = section.querySelector('.ipf-details');
437 if (!details) return; 438 439 var title = details.querySelector('.ipf-details__title'); 440 var date = details.querySelector('.ipf-details__date'); 441 var link = details.querySelector('.ipf-details__header .links'); 442 var headerItems = [title, date, link].filter(Boolean); 443 444 var scheduleRows = details.querySelectorAll('.ipf-schedule__row'); 445 var scheduleNote = details.querySelector('.ipf-schedule__note'); 446 var scheduleItems = Array.from(scheduleRows); 447 if (scheduleNote) scheduleItems.push(scheduleNote); 448 449 var allItems = headerItems.concat(scheduleItems); 450 gsap.set(allItems, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 451 452 var tl = gsap.timeline({ 453 scrollTrigger: { 454 trigger: section, 455 start: getScrollTriggerStart(), 456 toggleActions: 'play none play none' 457 } 458 }); 459 460 tl.to(headerItems, { 461 opacity: 1, 462 y: 0, 463 duration: CONFIG.block.duration, 464 stagger: CONFIG.block.stagger, 465 ease: EASE, 466 onComplete: function () { gsap.set(headerItems, { willChange: 'auto' }); } 467 }, 0); 468 469 var scheduleStart = CONFIG.block.stagger * headerItems.length; 470 tl.to(scheduleItems, { 471 opacity: 1, 472 y: 0, 473 duration: CONFIG.block.duration, 474 stagger: CONFIG.block.stagger, 475 ease: EASE, 476 onComplete: function () { gsap.set(scheduleItems, { willChange: 'auto' }); } 477 }, scheduleStart); 478 } 479 480 /* ================================================================ 481 Online Fair Section 482 ================================================================ */ 483 484 function initOnlineFair() { 485 var section = document.querySelector('.online-fair-section'); 486 if (!section) return; 487 488 var content = section.querySelector('.of-content'); 489 if (!content) return; 490 491 var overline = content.querySelector('.overline'); 492 var heading = content.querySelector('.of-heading'); 493 var body = content.querySelector('.of-body'); 494 var links = content.querySelector('.links'); 495 496 var words = heading ? splitHeadingIntoWords(heading) : []; 497 498 if (overline) gsap.set(overline, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 499 if (words.length) gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 500 if (body) gsap.set(body, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 501 if (links) gsap.set(links, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 502 503 var tl = gsap.timeline({ 504 scrollTrigger: { 505 trigger: section, 506 start: getScrollTriggerStart(), 507 toggleActions: 'play none play none' 508 } 509 }); 510 511 if (overline) { 512 tl.to(overline, { 513 opacity: 1, 514 y: 0, 515 duration: CONFIG.header.contentDuration, 516 ease: EASE, 517 onComplete: function () { gsap.set(overline, { willChange: 'auto' }); } 518 }, CONFIG.header.overlineOffset); 519 } 520 521 if (words.length) { 522 tl.to(words, { 523 opacity: 1, 524 y: 0, 525 duration: CONFIG.header.wordDuration, 526 stagger: CONFIG.header.wordStagger, 527 ease: EASE, 528 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 529 }, CONFIG.header.overlineOffset + 0.1); 530 } 531 532 if (body) { 533 tl.to(body, { 534 opacity: 1, 535 y: 0, 536 duration: CONFIG.header.contentDuration, 537 ease: EASE, 538 onComplete: function () { gsap.set(body, { willChange: 'auto' }); } 539 }, CONFIG.header.bodyOffset); 540 } 541 542 if (links) { 543 tl.to(links, { 544 opacity: 1, 545 y: 0, 546 duration: CONFIG.header.contentDuration, 547 ease: EASE, 548 onComplete: function () { gsap.set(links, { willChange: 'auto' }); } 549 }, CONFIG.header.linksOffset); 550 } 551 } 552 553 /* ================================================================ 554 Discover Art Section 555 ================================================================ */ 556 557 function initDiscoverArt() { 558 var section = document.querySelector('.discover-art-section'); 559 if (!section) return; 560 561 var header = section.querySelector('[data-animate-header]'); 562 if (header) { 563 var heading = header.querySelector('.heading'); 564 var button = header.querySelector('.button'); 565 566 var words = heading ? splitHeadingIntoWords(heading) : []; 567 568 if (words.length) { 569 gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 570 } 571 if (button) { 572 gsap.set(button, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 573 } 574 575 var tl = gsap.timeline({ 576 scrollTrigger: { 577 trigger: section, 578 start: getScrollTriggerStart(), 579 toggleActions: 'play none play none' 580 } 581 }); 582 583 if (words.length) { 584 tl.to(words, { 585 opacity: 1, 586 y: 0, 587 duration: CONFIG.header.wordDuration, 588 stagger: CONFIG.header.wordStagger, 589 ease: EASE, 590 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 591 }, 0); 592 } 593 594 if (button) { 595 tl.to(button, { 596 opacity: 1, 597 y: 0, 598 duration: CONFIG.header.contentDuration, 599 ease: EASE, 600 onComplete: function () { gsap.set(button, { willChange: 'auto' }); } 601 }, CONFIG.header.linksOffset); 602 } 603 } 604 605 var cards = section.querySelectorAll('.da-card'); 606 if (cards.length) { 607 animateCardGroup(cards, section, CONFIG.header.bodyOffset); 608 } 609 } 610 611 /* ================================================================ 612 Fair Programs Section 613 ================================================================ */ 614 615 function initFairPrograms() { 616 var section = document.querySelector('.fair-programs-section'); 617 if (!section) return; 618 619 var programLinks = section.querySelectorAll('.fp-program-name'); 620 var bodies = section.querySelectorAll('.fp-body'); 621 var images = section.querySelectorAll('.fp-image'); 622 623 // --- Staggered entrance animation --- 624 var overline = section.querySelector('.fp-header .overline'); 625 var divider = section.querySelector('.fp-divider'); 626 var bodyArea = section.querySelector('.fp-body-area'); 627 var fpRight = section.querySelector('.fp-right'); 628 629 var staggerTargets = []; 630 if (overline) staggerTargets.push(overline); 631 programLinks.forEach(function (l) { staggerTargets.push(l); }); 632 if (divider) staggerTargets.push(divider); 633 if (bodyArea) staggerTargets.push(bodyArea); 634 635 gsap.set(staggerTargets, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 636 if (fpRight) gsap.set(fpRight, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 637 638 var tl = gsap.timeline({ 639 scrollTrigger: { 640 trigger: section, 641 start: getScrollTriggerStart(), 642 toggleActions: 'play none play none' 643 } 644 }); 645 646 tl.to(staggerTargets, { 647 opacity: 1, 648 y: 0, 649 duration: CONFIG.block.duration, 650 stagger: CONFIG.block.stagger, 651 ease: EASE, 652 onComplete: function () { gsap.set(staggerTargets, { willChange: 'auto' }); } 653 }, 0); 654 655 if (fpRight) { 656 var fpRightRevealImg = fpRight.querySelector('.fp-image.is-active .img-reveal img'); 657 if (fpRightRevealImg) { 658 gsap.set(fpRightRevealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 659 } 660 661 tl.to(fpRight, { 662 opacity: 1, 663 y: 0, 664 duration: CONFIG.block.duration, 665 ease: EASE, 666 onComplete: function () { gsap.set(fpRight, { willChange: 'auto' }); } 667 }, CONFIG.block.stagger * 2); 668 669 if (fpRightRevealImg) { 670 tl.to(fpRightRevealImg, { 671 yPercent: CONFIG.revealImages.finalOffset, 672 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 673 ease: 'power3.out', 674 onComplete: function () { gsap.set(fpRightRevealImg, { willChange: 'auto' }); } 675 }, CONFIG.block.stagger * 2 + CONFIG.revealImages.delay); 676 } 677 } 678 679 // --- Hover interaction --- 680 function activateProgram(idx) { 681 programLinks.forEach(function (l) { l.classList.remove('is-active'); });
682 bodies.forEach(function (b) { b.classList.remove('is-active'); }); 683 images.forEach(function (b) { b.classList.remove('is-active'); }); 684 685 var target = section.querySelector('.fp-program-name[data-fp-index="' + idx + '"]'); 686 var body = section.querySelector('[data-fp-body="' + idx + '"]'); 687 var image = section.querySelector('[data-fp-image="' + idx + '"]'); 688 if (target) target.classList.add('is-active'); 689 if (body) body.classList.add('is-active'); 690 if (image) { 691 image.classList.add('is-active'); 692 var revealImg = image.querySelector('.img-reveal img'); 693 if (revealImg) gsap.set(revealImg, { yPercent: 0 }); 694 } 695 } 696 697 var isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; 698 699 programLinks.forEach(function (link) { 700 link.addEventListener('mouseenter', function () { 701 activateProgram(this.getAttribute('data-fp-index')); 702 }); 703 704 if (isTouchDevice) { 705 link.addEventListener('click', function (e) { 706 if (!this.classList.contains('is-active')) { 707 e.preventDefault(); 708 activateProgram(this.getAttribute('data-fp-index')); 709 } 710 }); 711 } 712 }); 713 } 714 715 /* ================================================================ 716 Collectors Collective Section 717 ================================================================ */ 718 719 function initCollectorsCollective() { 720 var section = document.querySelector('.collectors-collective-section'); 721 if (!section) return; 722 723 var logo = section.querySelector('.cc-card__logo'); 724 var heading = section.querySelector('.cc-card__heading'); 725 var desc = section.querySelector('.cc-card__desc'); 726 var actions = section.querySelector('.cc-card__actions'); 727 var right = section.querySelector('.cc-card__right'); 728 729 var words = heading ? splitHeadingIntoWords(heading) : []; 730 731 if (logo) gsap.set(logo, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 732 if (words.length) gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 733 if (desc) gsap.set(desc, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 734 if (actions) gsap.set(actions, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 735 if (right) gsap.set(right, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 736 737 var rightRevealImg = right ? right.querySelector('.img-reveal img') : null; 738 if (rightRevealImg) { 739 gsap.set(rightRevealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 740 } 741 742 var tl = gsap.timeline({ 743 scrollTrigger: { 744 trigger: section, 745 start: getScrollTriggerStart(), 746 toggleActions: 'play none play none' 747 } 748 }); 749 750 if (logo) { 751 tl.to(logo, { 752 opacity: 1, 753 y: 0, 754 duration: CONFIG.block.duration, 755 ease: EASE, 756 onComplete: function () { gsap.set(logo, { willChange: 'auto' }); } 757 }, 0); 758 } 759 760 if (words.length) { 761 tl.to(words, { 762 opacity: 1, 763 y: 0, 764 duration: CONFIG.header.wordDuration, 765 stagger: CONFIG.header.wordStagger, 766 ease: EASE, 767 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 768 }, CONFIG.header.overlineOffset); 769 } 770 771 if (desc) { 772 tl.to(desc, { 773 opacity: 1, 774 y: 0, 775 duration: CONFIG.header.contentDuration, 776 ease: EASE, 777 onComplete: function () { gsap.set(desc, { willChange: 'auto' }); } 778 }, CONFIG.header.bodyOffset); 779 } 780 781 if (actions) { 782 tl.to(actions, { 783 opacity: 1, 784 y: 0, 785 duration: CONFIG.header.contentDuration, 786 ease: EASE, 787 onComplete: function () { gsap.set(actions, { willChange: 'auto' }); } 788 }, CONFIG.header.linksOffset); 789 } 790 791 if (right) { 792 tl.to(right, { 793 opacity: 1, 794 y: 0, 795 duration: CONFIG.block.duration, 796 ease: EASE, 797 onComplete: function () { gsap.set(right, { willChange: 'auto' }); } 798 }, CONFIG.header.overlineOffset); 799 800 if (rightRevealImg) { 801 tl.to(rightRevealImg, { 802 yPercent: CONFIG.revealImages.finalOffset, 803 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 804 ease: 'power3.out', 805 onComplete: function () { gsap.set(rightRevealImg, { willChange: 'auto' }); } 806 }, CONFIG.header.overlineOffset + CONFIG.revealImages.delay); 807 } 808 } 809 } 810 811 /* ================================================================ 812 Artist Opportunities Section 813 ================================================================ */ 814 815 function initArtistOpportunities() { 816 var section = document.querySelector('.artist-opportunities-section'); 817 if (!section) return; 818 819 var header = section.querySelector('[data-animate-header]'); 820 setHeaderInitialStates(header); 821 animateSectionHeader(header, section); 822 823 var cards = section.querySelectorAll('.ao-card'); 824 if (cards.length) { 825 animateCardGroup(cards, section, CONFIG.header.bodyOffset); 826 } 827 } 828 829 /* ================================================================ 830 Curated Collections Section 831 ================================================================ */ 832 833 function initCuratedCollections() { 834 var section = document.querySelector('.curated-collections-section'); 835 if (!section) return; 836
837 // --- Intro: heading word-split + desc + button (triggers on intro entering viewport) --- 838 var intro = section.querySelector('.cc-intro'); 839 if (intro) { 840 var heading = intro.querySelector('.cc-intro__heading'); 841 var desc = intro.querySelector('.cc-intro__desc'); 842 var button = intro.querySelector('.button'); 843 844 var words = heading ? splitHeadingIntoWords(heading) : []; 845 846 if (words.length) gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 847 if (desc) gsap.set(desc, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 848 if (button) gsap.set(button, { opacity: 0, y: CONFIG.header.contentY, willChange: 'transform, opacity' }); 849 850 var introTl = gsap.timeline({ 851 scrollTrigger: { 852 trigger: intro, 853 start: getScrollTriggerStart(), 854 toggleActions: 'play none play none' 855 } 856 }); 857 858 if (words.length) { 859 introTl.to(words, { 860 opacity: 1, 861 y: 0, 862 duration: CONFIG.header.wordDuration, 863 stagger: CONFIG.header.wordStagger, 864 ease: EASE, 865 onComplete: function () { gsap.set(words, { willChange: 'auto' }); } 866 }, 0); 867 } 868 869 if (desc) { 870 introTl.to(desc, { 871 opacity: 1, 872 y: 0, 873 duration: CONFIG.header.contentDuration, 874 ease: EASE, 875 onComplete: function () { gsap.set(desc, { willChange: 'auto' }); } 876 }, CONFIG.header.bodyOffset); 877 } 878 879 if (button) { 880 introTl.to(button, { 881 opacity: 1, 882 y: 0, 883 duration: CONFIG.header.contentDuration, 884 ease: EASE, 885 onComplete: function () { gsap.set(button, { willChange: 'auto' }); } 886 }, CONFIG.header.linksOffset); 887 } 888 } 889 890 // --- Cards: stagger with img-reveal (separate ScrollTrigger on cards container) --- 891 var cards = section.querySelectorAll('.cc-collection-card'); 892 if (cards.length) { 893 var cardsContainer = section.querySelector('.cc-cards') || section; 894 895 gsap.set(cards, { opacity: 0, y: CONFIG.block.y, willChange: 'transform, opacity' }); 896 897 cards.forEach(function (card) { 898 var revealImg = card.querySelector('.cc-collection-card__image img'); 899 if (revealImg) { 900 gsap.set(revealImg, { yPercent: CONFIG.revealImages.initialOffset, willChange: 'transform' }); 901 } 902 }); 903 904 var cardsRevealed = false; 905 function revealCcCards(self) { 906 if (cardsRevealed) return; 907 cardsRevealed = true; 908 909 gsap.to(cards, { 910 opacity: 1, 911 y: 0, 912 duration: CONFIG.block.duration, 913 stagger: CONFIG.block.stagger, 914 ease: EASE, 915 onComplete: function () { gsap.set(cards, { willChange: 'auto' }); } 916 }); 917 918 cards.forEach(function (card, index) { 919 var revealImg = card.querySelector('.cc-collection-card__image img'); 920 if (revealImg) { 921 gsap.to(revealImg, { 922 yPercent: CONFIG.revealImages.finalOffset, 923 duration: CONFIG.block.duration * CONFIG.revealImages.durationMultiplier, 924 ease: 'power3.out', 925 delay: index * CONFIG.block.stagger + CONFIG.revealImages.delay, 926 onComplete: function () { gsap.set(revealImg, { willChange: 'auto' }); } 927 }); 928 } 929 }); 930 931 self.kill(); 932 } 933 934 ScrollTrigger.create({ 935 trigger: cardsContainer, 936 start: getScrollTriggerStart(), 937 onEnter: revealCcCards, 938 onEnterBack: revealCcCards 939 }); 940 } 941 } 942 943 /* ================================================================ 944 Init 945 ================================================================ */ 946 947 function init() { 948 // Set hero initial states immediately so content stays hidden behind the loader 949 var heroSection = document.querySelector('.hero-section'); 950 if (heroSection) { 951 var heroHeader = heroSection.querySelector('[data-animate-header]'); 952 setHeaderInitialStates(heroHeader); 953 954 // Split heading into word spans NOW so the CSS opacity:0 rule on .word takes effect 955 // before the loader finishes (prevents flash of unsplit text) 956 var heroHeading = heroHeader ? heroHeader.querySelector('.heading') : null; 957 if (heroHeading) { 958 var words = splitHeadingIntoWords(heroHeading); 959 if (words.length) { 960 gsap.set(words, { opacity: 0, y: CONFIG.header.wordY, willChange: 'transform, opacity' }); 961 } 962 } 963 } 964 965 initWhatsHappening(); 966 initInPersonFair(); 967 initOnlineFair(); 968 initDiscoverArt(); 969 initFairPrograms();
970 initCollectorsCollective(); 971 initArtistOpportunities(); 972 initCuratedCollections(); 973 974 document.querySelectorAll('[data-animate-section]').forEach(function (section) { 975 animateBlocks(section); 976 }); 977 978 // Defer hero animation until the global loader finishes 979 if (document.body.classList.contains('toaf-loader-complete')) { 980 initHero(); 981 } else { 982 window.addEventListener('toafLoaderComplete', function () { 983 initHero(); 984 }, { once: true }); 985 } 986 987 requestAnimationFrame(function () { 988 ScrollTrigger.refresh(); 989 }); 990 } 991 992 if (document.readyState === 'loading') { 993 document.addEventListener('DOMContentLoaded', init); 994 } else { 995 init(); 996 } 997})();
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.