gsap.registerPlugin(Draggable, InertiaPlugin); // SETUP const container = "[tf-draggable]"; const collectionWrapper = document.querySelector("[tf-collection-wrapper]"); const boundsElement = document.querySelector("[tf-home-content]"); const collectionList = document.querySelector("[tf-collection-list]"); const collectionItem = "[tf-collection-item]"; const itemElements = document.querySelectorAll(collectionItem); const postcardImages = Array.from( document.querySelectorAll("[tf-postcard-img]:not(.w-condition-invisible)") ); const preloader = document.querySelector("[tf-preloader]"); const preloaderText = document.querySelector("[tf-preloader-text]"); const navColorBtn = document.querySelector("[tf-nav-color-btn]"); const navAboutBtn = document.querySelector("[tf-nav-about-btn]"); const navThemeBtn = document.querySelector("[tf-nav-theme-btn]"); const dragExplainer = document.querySelector("[tf-drag-explainer]"); var postcardWidth = 22; const navLogo = document.querySelector("[tf-nav-logo-wrap]"); collectionList.style.width = `${postcardWidth * 10}rem`; let tween = undefined; const cardsInViewport = []; const cardsOutsideViewport = []; const windowWidth = window.innerWidth; // PRELOADER: ONLY SHOW THE VISIBLE CARDS IN THE STACK function stackToGridAnimation() { itemElements.forEach((element) => { const rect = element.getBoundingClientRect(); const isVisible = rect.top < window.innerHeight * 2 && rect.bottom > 0 && rect.left < window.innerWidth * 2 && rect.right > 0; if (isVisible) { cardsInViewport.push(element); } else { cardsOutsideViewport.push(element); } }); tween = gsap.from(cardsInViewport, { paused: true, duration: 1.6, ease: "back.inOut(1)", rotation: "random(-8, 8)", x: (i, target) => { const elRect = target.getBoundingClientRect(); const elX = elRect.left + elRect.width / 2; const centerX = window.innerWidth / 2; const distance = centerX - elX; return distance; }, y: (i, target) => { const elRect = target.getBoundingClientRect(); const elY = elRect.top + elRect.height / 2; const centerY = window.innerHeight / 2; const distance = centerY - elY; return distance; }, stagger: 0.02, }); } // Start stackToGridAnimation and release cards only if tween is initialized function releaseCards() { if (tween) { tween.play(); enterPage(); } else { console.log("Tween not initialized yet. Waiting..."); // Delay the release until tween is initialized setTimeout(releaseCards, 400); // Retry after 400ms } } // Mouse interaction on the visible cards function applyMouseInteraction() { preloader.addEventListener("mousemove", (event) => { const mouseX = event.clientX; const mouseY = event.clientY; gsap.to(collectionList, { x: mouseX / 50, y: mouseY / 50, ease: "power0", }); }); } // PRELOADER: Check if the preloader should be shown const seenHomepage = !sessionStorage.getItem("seenHomepage"); const preloaderBtn = document.querySelector(".preload-button"); function handlePreloader() { if (seenHomepage || !sessionStorage.getItem("seenHomepage")) { showPreloader(); } else { // Show postcard grid boundsElement.style.opacity = 1; releaseCards(); gsap.set(dragExplainer, { display: "none", }); } } document.addEventListener("DOMContentLoaded", () => { // Wait for all images to load before starting animations var imgLoad = imagesLoaded( // cardsInViewport.map((card) => card.querySelector(img)), postcardImages, function (instance) { stackToGridAnimation(); // Ensure collectionList is visible gsap.to(collectionList, { opacity: 1, duration: 1, }); handlePreloader(); } ); // Error handling imgLoad.on("fail", function (instance) { console.log("Error loading images:", instance); setTimeout(() => { stackToGridAnimation(); // Ensure collectionList is visible gsap.to(collectionList, { opacity: 1, duration: 1, }); handlePreloader(); }, 2000); }); }); function showPreloader() { animateIntroText(); if (windowWidth > 991) { applyMouseInteraction(); } var preloaderTL = gsap.timeline({ onComplete: () => { // Make Preloader Button hoverable gsap.set(preloaderBtn, { clearProps: "all" }); preloaderBtn.style.transition = "transform 0.4s"; }, }); preloaderTL.set(preloader, { display: "flex" }); preloaderTL.to(preloaderText, { opacity: 1, duration: 0.4 }); preloaderTL.from( boundsElement, { opacity: 0, duration: 2, delay: 0.1, ease: "power2.inOut" }, "<" ); preloaderTL.from( cardsInViewport, { scale: 0.4, rotate: 0, stagger: 0.05, duration: 2, ease: "power2.inOut", }, "<" ); preloaderTL.from(preloaderBtn, { y: "250%", duration: 1, ease: "power2.inOut", }); } function enterPage() { var enterPageTL = gsap.timeline({ onComplete: () => { // Make Buttons hoverable gsap.set(preloaderBtn, { clearProps: "all" }); preloaderBtn.style.transition = "transform 0.4s"; }, }); enterPageTL.to(preloaderText, { opacity: 0, duration: 0.4 }); enterPageTL.to( preloaderBtn, { y: "250%", duration: 0.6, ease: "power2.inOut", }, "<" ); enterPageTL.to(preloader, { opacity: 0, duration: 0.4 }); enterPageTL.to(preloader, { display: "none" }); enterPageTL.to(navColorBtn, { y: "0%", duration: 0.6, ease: "back.out(3)", }); enterPageTL.to( navThemeBtn, { y: "0%", duration: 0.6, delay: 0.1, ease: "back.out(3)", }, "<" ); enterPageTL.to( navAboutBtn, { y: "0%", duration: 0.6, delay: 0.2, ease: "back.out(3)", }, "<" ); enterPageTL.to( dragExplainer, { opacity: 1, duration: 0.4, delay: 1, }, "<" ); } // Hover Trigger to rotate cards in viewport preloaderBtn.addEventListener("mouseover", function () { gsap.to(cardsInViewport, { rotate: 0, stagger: 0.01, duration: 0.6, ease: "power2.inOut", }); }); // Trigger to show grid preloaderBtn.addEventListener("click", function () { // Release cards only after preloader button click on the first visit gsap.set(preloaderBtn, { clearProps: "all" }); releaseCards(); if (seenHomepage) { // Mark the first visit in session storage sessionStorage.setItem("seenHomepage", "true"); } }); const splitTypes = [...document.querySelectorAll("[animatedText]")]; // Preloader Text Animation function animateIntroText() { splitTypes.forEach((char, i) => { const text = new SplitType(char, { types: ["chars, words"] }); gsap.from(text.chars, { opacity: 0, stagger: 0.015, duration: 0.6, delay: 0.2, }); }); } // FOLLOW EXPLAINER MOUSE MOVE $(document).bind("mousemove", function (e) { $(dragExplainer).css({ left: e.pageX, top: e.pageY, }); }); // // PAGE TRANSITION GRID TO SINGLE POSTCARD // let centerX = window.innerWidth / 2; let centerY = window.innerHeight / 2; let resizeTimeout; // Helper Function to recalculate distances function updateCenterValues() { centerX = window.innerWidth / 2; centerY = window.innerHeight / 2; } // Debounced Resize Event function debounceResize() { clearTimeout(resizeTimeout); // Clear the previous timer resizeTimeout = setTimeout(updateCenterValues, 150); // Delay execution by 150ms } // Attach the debounced resize function window.addEventListener("resize", debounceResize); // Click Event Listener $("[tf-postcard-link]").click(function (e) { e.preventDefault(); // Fade out Send Postcard Button gsap.to("[tf-postcard-hover-btn]", { duration: 0.4, scale: 0, }); // Set opacity to 0 for all other collection items const itemToMove = $(this).closest(collectionItem); $(collectionItem) .not(itemToMove) .each(function () { gsap.to($(this), { duration: 0.4, opacity: 0, }); }); // Get the initial position of the postcard const rect = itemToMove[0].getBoundingClientRect(); const currentX = rect.left + rect.width / 2; const currentY = rect.top + rect.height / 2; // Get the center coordinates & calculate the distance to the center const distanceX = centerX - currentX; const distanceY = centerY - currentY; // Helper to avoid fash jump of card window.Webflow && window.Webflow.destroy(); // Move the clicked postcard to the center & then redirect to the page gsap.to(itemToMove, { duration: 0.8, ease: "power2.inOut", x: distanceX, y: distanceY, onComplete: function () { window.location = e.currentTarget.href; }, }); }); updateCenterValues(); // CUSTOM GRID WITH ORIENTATION AND SMALL OFFSET LAYOUT // Calculate random values function getRandomInt(max) { return Math.floor(Math.random() * max); } function createDraggable() { let scale = 1; // Initial scale value let options = { bounds: boundsElement, // Set bounds on the wrapper edgeResistance: 0.6, dragResistance: 0.2, zIndexBoost: false, inertia: true, onPress: function () { // if (window.innerWidth > 991) { // collectionWrapper.style.transform = `scale(${scale * 0.97})`; // } gsap.to(dragExplainer, { opacity: 0, duration: 0.4, }); }, // onRelease: function () { // if (window.innerWidth > 991) { // collectionWrapper.style.transform = `scale(${scale})`; // } // }, // onDrag: function () { // if (window.innerWidth > 991) { // collectionWrapper.style.transform = `scale(${scale * 0.97})`; // } // }, // onDragEnd: function () { // // Reset the scale to its original value // if (window.innerWidth > 991) { // collectionWrapper.style.transform = `scale(${scale})`; // } // }, }; Draggable.create(container, options); } function init() { gsap.set(container, { willChange: "transform" }); createDraggable(); } init(); // FIX: Browser back button – Force reload (flush cache) window.onpageshow = function (event) { if (event.persisted) { window.location.reload(); } }; // TOPIC MODAL: SWIPER const slides = document.getElementsByClassName("swiper-slide"); if (windowWidth > 568) { var MySwiper = new Swiper(".swiper", { observer: true, observeParents: true, speed: 600, direction: "vertical", mousewheel: { releaseOnEdges: false }, keyboard: { enabled: true }, followFinger: false, touchReleaseOnEdges: true, loop: true, initialSlide: 1, centeredSlides: true, longSwipes: false, slidesPerView: 3, }); }