gsap.registerPlugin(ScrollTrigger, Flip); // =============== THEME TOGGLER =============== const toggleButton = document.getElementById("theme-toggle"); const body = document.body; const setTheme = (theme) => { body.dataset.theme = theme; localStorage.setItem("preferred-theme", theme); }; const getTheme = () => { const stored = localStorage.getItem("preferred-theme"); return stored || "dark"; }; const toggleTheme = () => { const currentTheme = body.dataset.theme; setTheme(currentTheme === "light" ? "dark" : "light"); }; toggleButton.addEventListener("click", toggleTheme); setTheme(getTheme()); // =============== LOADER =============== function runLoaderAnimation() { const loaderText = document.querySelector(".loader-text"); loaderText.textContent = "0"; gsap.to(loaderText, { duration: 1, textContent: 100, snap: { textContent: 1 }, onComplete: () => { const loaderTl = gsap.timeline(); loaderTl .to(".loader-text", { duration: 1.5, yPercent: -800, opacity: 0, ease: "power3.inOut", }) .to( ".loader", { duration: 1.5, yPercent: 110, ease: "power3.inOut", }, 0 ) .from( ".slogan", { ease: "power3.out", filter: "blur(0.5rem)", yPercent: -100, opacity: 0, }, 0.5 ) .from( ".nav_item", { ease: "power3.out", filter: "blur(0.5rem)", yPercent: -100, opacity: 0, stagger: 0.05, }, 0.55 ); }, }); } runLoaderAnimation(); window.addEventListener("pageshow", function (event) { if (event.persisted) { console.log("Page was restored from bfcache. Resetting and re-running loader."); gsap.set("#logo", { yPercent: 0, filter: "blur(0)", opacity: 1 }); gsap.set(".loader", { yPercent: -101 }); } }); // =============== STRONA: PRZEJŚCIA MIĘDZY LINKAMI =============== (function initPageTransitions() { document.querySelectorAll("a").forEach((link) => { const isSameDomain = link.host === window.location.host; if (!isSameDomain) return; link.addEventListener("click", function (event) { event.preventDefault(); const targetUrl = this.getAttribute("href"); // Zakrycie loaderem od góry gsap.fromTo( ".loader", { yPercent: -101 }, { yPercent: 0, ease: "power3.inOut", duration: 0.5, onComplete: () => { window.location.href = targetUrl; }, } ); // Animacja logo gsap.to("#logo", { duration: 0.5, yPercent: -50, filter: "blur(1rem)", opacity: 0, ease: "power3.in", onComplete: () => { window.location.href = targetUrl; }, }); }); }); })(); // =============== KURSOR =============== const cursor = { dot: document.querySelector(".cursor"), stroke: document.querySelector(".cursor_stroke"), showreel: document.querySelector(".showreel_cursor") }; const elements = { linksAndButtons: document.querySelectorAll("a, button"), showreel: document.querySelector(".showreel"), showreelVideo: document.querySelector("#showreel-video"), showreelHero: document.querySelector(".hero_showreel") }; // Ustawienie początkowej pozycji kursorów gsap.set([cursor.dot, cursor.stroke, cursor.showreel], { xPercent: -50, yPercent: -50 }); // Optymalizacja śledzenia myszy const mouse = { x: 0, y: 0 }; const updateCursor = gsap.ticker.add(() => { gsap.set(cursor.dot, { x: mouse.x, y: mouse.y }); gsap.set(cursor.showreel, { x: mouse.x, y: mouse.y }); gsap.to(cursor.stroke, { duration: 0.2, x: mouse.x, y: mouse.y }); }); window.addEventListener("mousemove", (e) => { mouse.x = e.x; mouse.y = e.y; }, { passive: true }); // Obsługa hover efektów const handleHover = (element, { opacity = 0, scale = 1.75 } = {}) => { const hoverIn = () => { cursor.dot.style.opacity = opacity; cursor.stroke.style.opacity = opacity; gsap.to(cursor.stroke, { scale, duration: 0.4, ease: "power3.inOut" }); }; const hoverOut = () => { cursor.dot.style.opacity = 1; cursor.stroke.style.opacity = 1; gsap.to(cursor.stroke, { scale: 1, duration: 0.4, ease: "power3.inOut" }); }; element.addEventListener("mouseenter", hoverIn); element.addEventListener("mouseleave", hoverOut); }; // Aplikowanie efektów hover elements.linksAndButtons.forEach(element => handleHover(element)); handleHover(elements.showreel, { opacity: 0, scale: 2.25 }); // Obsługa kliknięć document.addEventListener("mousedown", () => { gsap.to(cursor.stroke, { scale: 1.5, ease: "power3.in" }); }); document.addEventListener("mouseup", () => { gsap.to(cursor.stroke, { scale: 1, delay: 0.3, ease: "power3.out" }); }); // Animacja showreel const showreelAnim = gsap.to(cursor.showreel, { paused: true, scale: 1, duration: 0.4, ease: "power3.inOut" }); elements.showreel.addEventListener("mouseenter", () => showreelAnim.play()); elements.showreel.addEventListener("mouseleave", () => showreelAnim.reverse()); // Obsługa showreel const handleShowreel = (() => { let state; const toggle = () => { elements.showreelVideo.muted = !elements.showreelVideo.muted; state = Flip.getState(".hero_showreel"); elements.showreelHero.classList.toggle("is-open"); body.classList.toggle("overflow"); cursor.showreel.classList.toggle("close"); animateTransition(); }; const close = () => { if (!elements.showreelHero.classList.contains("is-open")) return; elements.showreelVideo.muted = true; elements.showreelHero.classList.remove("is-open"); body.classList.remove("overflow"); cursor.showreel.classList.remove("close"); animateTransition(); }; const animateTransition = () => { Flip.from(state, { absolute: true, duration: 0.5, ease: "power3.out" }); }; // Event listeners elements.showreel.addEventListener("click", toggle); document.addEventListener("keydown", (e) => { if (e.key === "Escape") close(); }); })(); // =============== StartScript =============== window.addEventListener( "load", () => { // =============== NAVBAR SCROLL ANIMATIONS =============== (function initNavbar() { const navLoad = gsap.timeline(); navLoad.fromTo( "#logo path", { filter: "blur(1rem)", yPercent: -50, opacity: 0 }, { filter: "blur(0rem)", yPercent: 0, opacity: 1, ease: "power3.out", stagger: 0.05, } ); let navbarLogoWrapper = document.querySelector( ".navbar_logo_wrapper" ).offsetHeight; let startPosition = `top+=${navbarLogoWrapper}px top`; function navChange() { navbarLogoWrapper = document.querySelector( ".navbar_logo_wrapper" ).offsetHeight; startPosition = `top+=${navbarLogoWrapper}px top`; ScrollTrigger.refresh(); } window.addEventListener("resize", navChange); // Pojawianie się / znikanie Nav const showAnim = gsap .fromTo( ".nav_component", { yPercent: -150, opacity: 0 }, { yPercent: 0, opacity: 1, paused: true, duration: 0.45, ease: "power3.inOut", } ) .progress(1); let navTrigger = ScrollTrigger.create({ start: "top top", end: 99999, onUpdate: (self) => { self.direction === -1 ? showAnim.play() : showAnim.reverse(); }, }); // Po wejściu w obszar stopki ScrollTrigger.create({ trigger: ".footer", start: "top bottom", onEnter: () => { navTrigger.disable(); showAnim.restart(true); }, onLeaveBack: () => { navTrigger.enable(); showAnim.pause(); }, }); })(); // =============== Portfolio ANIMATIONS =============== let mm = gsap.matchMedia(); mm.add("(min-width: 768px)", () => { const portfolio = { section: document.querySelector(".portfolio-home-sticky"), container: document.querySelector(".portfolio-home-list"), slider: document.querySelector(".portfolio-home-slider"), slides: document.querySelectorAll(".portfolio-home-slide") }; // Implementacja funkcji debounce const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; const initPortfolioSlider = () => { const slideWidth = portfolio.slider.offsetWidth; const totalMove = portfolio.container.offsetWidth - slideWidth; // Główna animacja przewijania const scrollTween = gsap.fromTo( portfolio.container, { xPercent: 0 }, { x: -totalMove, ease: "none", scrollTrigger: { trigger: portfolio.section, start: "top 1.5rem", end: () => "+=" + window.innerHeight * 1.25, pin: true, scrub: 0.5, snap: { snapTo: (value) => { const slideCount = portfolio.slides.length; const snapPoint = 1 / (slideCount - 1); return Math.round(value / snapPoint) * snapPoint; }, duration: { min: 0.2, max: 0.3 }, ease: "power3.out", }, }, } ); // Animacje poszczególnych slajdów portfolio.slides.forEach((slide) => { const elements = { image: slide.querySelector(".portfolio-home-img"), thumb: slide.querySelector(".portfolio-home-thumb"), title: slide.querySelector(".portfolio-home-heading"), description: slide.querySelector(".home_portfolio_info") }; // Animacja paralaksy obrazu gsap.fromTo( elements.image, { xPercent: -20 }, { xPercent: 20, scrollTrigger: { containerAnimation: scrollTween, trigger: slide, scrub: 0.5, }, } ); // Animacja wejścia elementów gsap.timeline({ scrollTrigger: { containerAnimation: scrollTween, trigger: slide, start: "1 50%", toggleActions: "play none none reverse", } }) .from(elements.thumb, { scale: 0.9, ease: "power3.out", duration: 1, }) .from(elements.title, { y: 300, ease: "power3.out", duration: 1, }, 0.05) .from(elements.description, { y: 300, ease: "power3.out", duration: 1, }, 0.15); }); }; initPortfolioSlider(); window.addEventListener("resize", debounce(initPortfolioSlider, 250)); }); // =============== ANIMACJA IMBIR INFO =============== const splitAbout = new SplitType("#imbir-info", { types: "lines, chars", }); const tlInfo = gsap .timeline({ scrollTrigger: { trigger: ".section_home_about", start: "top 80%", end: "bottom 80%", scrub: 0.5, }, }) .from(splitAbout.chars, { opacity: 0.1, scale: 0.95, filter: "blur(0.5rem)", rotation: 15, stagger: 0.01, yPercent: 20, duration: 0.1, }); let splide = new Splide( '.splide', { type : 'loop', drag : 'free', gap: '1.5rem', pagination: false, arrows: false, autoWidth: true, autoScroll: { speed: 2, }, } ); splide.mount( window.splide.Extensions ); // =============== ANIMACJA CTA =============== const split = new SplitType( ".main_cta_component h2, .main_cta_component p", { types: "words, chars", } ); gsap .timeline({ scrollTrigger: { trigger: ".section_main_cta", start: "center 80%", end: "center center", scrub: 2, }, }) .from(split.chars, { opacity: 0.1, filter: "blur(1rem)", scale: 0.9, stagger: 0.01, yPercent: 10, duration: 0.1, }); }, "500" ); // =============== ANIMACJA LOGO W STOPCE =============== (function animateFooter() { gsap.from("#logoFooter path", { scrollTrigger: { trigger: ".footer", start: "center bottom", toggleActions: "play none none reverse", }, filter: "blur(1rem)", ease: "power3.out", yPercent: 50, opacity: 0, stagger: 0.1, }); })(); ScrollTrigger.refresh();