Can't get scroll animation code to execute

1 week ago 11
ARTICLE AD BOX

Thanks for the help in advance. For the life of me, I cannot get a particular scroll animation to execute which utilizes gsap and lenis.

I've assembled the code to the best of my ability: https://codepen.io/Charles-Kent/pen/XJdZRWy

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>RedoMedia Split-Card Scroll Animation | Codegrid</title> <link rel="stylesheet" href="styles.css" /> </head> <body> <section class="intro"> <h1>Every idea begins as a single image</h1> </section> <section class="sticky"> <div class="sticky-header"> <h1>Three pillars with one purpose</h1> </div> <div class="card-container"> <div class="card" id="card-1"> <div class="card-front"> <img src="https://www.charlesthedesigner.com/images/card-images/card_cover_1.jpg" alt="" /> </div> <div class="card-back"> <span>( 01 )</span> <p>Interactive Web Experiences</p> </div> </div> <div class="card" id="card-2"> <div class="card-front"> <img src="https://www.charlesthedesigner.com/images/card-images/card_cover_2.jpg" alt="" /> </div> <div class="card-back"> <span>( 02 )</span> <p>Thoughtful Design Language</p> </div> </div> <div class="card" id="card-3"> <div class="card-front"> <img src="https://www.charlesthedesigner.com/images/card-images/card_cover_3.jpg" alt="" /> </div> <div class="card-back"> <span>( 03 )</span> <p>Visual Design Systems</p> </div> </div> </div> </section> <section class="outro"> <h1>Every transition leaves a trace</h1> </section> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js"></script> <script src="https://unpkg.com/[email protected]/dist/lenis.min.js"></script> </body> </html> @import url("https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&display=swap"); :root { --bg: #0f0f0f; --fg: #fff; --card-1: #b2b2b2; --card-2: #ce2017; --card-3: #2f2f2f; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: "Instrument Serif", sans-serif; } img { width: 100%; height: 100%; object-fit: cover; } h1 { font-size: 4rem; font-weight: 500; line-height: 1; } p { font-size: 2rem; font-weight: 500; line-height: 1; } section { position: relative; width: 100%; height: 100svh; padding: 2rem; background-color: var(--bg); color: var(--fg); } .intro, .outro { text-align: center; align-content: center; } .intro h1, .outro h1 { width: 30%; margin: 0 auto; } .sticky { position: relative; display: flex; justify-content: center; align-items: center; } .sticky-header { position: absolute; top: 20%; left: 50%; transform: translate(-50%, -50%); } .sticky-header h1 { position: relative; text-align: center; will-change: transform, opacity; transform: translateY(40px); opacity: 0; } .card-container { position: relative; width: 75%; display: flex; perspective: 1000px; transform: translateY(40px); will-change: width; } .card { position: relative; flex: 1; aspect-ratio: 5/7; transform-style: preserve-3d; transform-origin: top; } #card-1 { border-radius: 20px 0 0 20px; } #card-3 { border-radius: 0 20px 20px 0; } .card-front, .card-back { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; border-radius: inherit; overflow: hidden; } .card-back { display: flex; justify-content: center; align-items: center; text-align: center; transform: rotateY(180deg); padding: 2rem; } .card-back span { position: absolute; top: 2rem; left: 2rem; opacity: 0.4; } #card-1 .card-back { background-color: var(--card-1); color: var(--bg); } #card-2 .card-back { background-color: var(--card-2); } #card-3 .card-back { background-color: var(--card-3); } @media (max-width: 1000px) { h1 { font-size: 3rem; } .intro h1, .outro h1 { width: 100%; } .sticky { height: max-content; padding: 4rem 2rem; flex-direction: column; } .sticky-header { position: relative; top: 0; left: 0; transform: none; margin-bottom: 4rem; } .sticky-header h1 { opacity: 1; } .card-container { width: 100%; flex-direction: column; gap: 2rem; } .card { width: 100%; max-width: 400px; margin: 0 auto; border-radius: 20px !important; } #card-1, #card-2, #card-3, .card-back { transform: none; } } import { gsap } from 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js'; import { ScrollTrigger } from 'https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/ScrollTrigger.min.js'; import { Lenis } from 'https://unpkg.com/[email protected]/dist/lenis.min.js'; document.addEventListener("DOMContentLoaded", () => { gsap.registerPlugin(ScrollTrigger); const lenis = new Lenis(); lenis.on("scroll", ScrollTrigger.update); gsap.ticker.add((time) => { lenis.raf(time * 1000); }); gsap.ticker.lagSmoothing(0); const cardContainer = document.querySelector(".card-container"); const stickyHeader = document.querySelector(".sticky-header h1"); let isGapAnimationCompleted = false; let isFlipAnimationCompleted = false; function initAnimations() { ScrollTrigger.getAll().forEach((trigger) => trigger.kill()); const mm = gsap.matchMedia(); mm.add("(max-width: 999px)", () => { document .querySelectorAll(".card, .card-container, .sticky-header h1") .forEach((el) => (el.style = "")); return {}; }); mm.add("(min-width: 1000px)", () => { ScrollTrigger.create({ trigger: ".sticky", start: "top top", end: `+=${window.innerHeight * 4}px`, scrub: 1, pin: true, pinSpacing: true, onUpdate: (self) => { const progress = self.progress; if (progress >= 0.1 && progress <= 0.25) { const headerProgress = gsap.utils.mapRange( 0.1, 0.25, 0, 1, progress ); const yValue = gsap.utils.mapRange(0, 1, 40, 0, headerProgress); const opacityValue = gsap.utils.mapRange( 0, 1, 0, 1, headerProgress ); gsap.set(stickyHeader, { y: yValue, opacity: opacityValue, }); } else if (progress < 0.1) { gsap.set(stickyHeader, { y: 40, opacity: 0, }); } else if (progress > 0.25) { gsap.set(stickyHeader, { y: 0, opacity: 1, }); } if (progress <= 0.25) { const widthPercentage = gsap.utils.mapRange( 0, 0.25, 75, 60, progress ); gsap.set(cardContainer, { width: `${widthPercentage}%` }); } else { gsap.set(cardContainer, { width: "60%" }); } if (progress >= 0.35 && !isGapAnimationCompleted) { gsap.to(cardContainer, { gap: "20px", duration: 0.5, ease: "power3.out", }); gsap.to(["#card-1", "#card-2", "#card-3"], { borderRadius: "20px", duration: 0.5, ease: "power3.out", }); isGapAnimationCompleted = true; } else if (progress < 0.35 && isGapAnimationCompleted) { gsap.to(cardContainer, { gap: "0px", duration: 0.5, ease: "power3.out", }); gsap.to("#card-1", { borderRadius: "20px 0 0 20px", duration: 0.5, ease: "power3.out", }); gsap.to("#card-2", { borderRadius: "0px", duration: 0.5, ease: "power3.out", }); gsap.to("#card-3", { borderRadius: "0 20px 20px 0", duration: 0.5, ease: "power3.out", }); isGapAnimationCompleted = false; } if (progress >= 0.7 && !isFlipAnimationCompleted) { gsap.to(".card", { rotationY: 180, duration: 0.75, ease: "power3.inOut", stagger: 0.1, }); gsap.to(["#card-1", "#card-3"], { y: 30, rotationZ: (i) => [-15, 15][i], duration: 0.75, ease: "power3.inOut", }); isFlipAnimationCompleted = true; } else if (progress < 0.7 && isFlipAnimationCompleted) { gsap.to(".card", { rotationY: 0, duration: 0.75, ease: "power3.inOut", stagger: -0.1, }); gsap.to(["#card-1", "#card-3"], { y: 0, rotationZ: 0, duration: 0.75, ease: "power3.inOut", }); isFlipAnimationCompleted = false; } }, }); return () => {}; }); } initAnimations(); let resizeTimer; window.addEventListener("resize", () => { clearTimeout(resizeTimer); resizeTimer = setTimeout(() => { initAnimations(); }, 250); }); });

This is based on a CodeGrid tutorial I found on YouTube (which showcases how the final result should look): https://www.youtube.com/watch?v=_F1t2Ux-znk&t

Looks relatively simple on there but fails to execute for me in any way... just my luck, right? Where does the code go wrong for me? Thanks again.

Read Entire Article