// ========== SCENE 2: Dramatic rotation & scale + box color ========== const card2 = document.getElementById('card2'); const magicBox = document.getElementById('magicBox'); const rotateText = document.getElementById('rotateText'); // Complex timeline for scene2: card rotates, text scales, box color & scale changes const t2 = gsap.timeline(); t2.fromTo(card2, rotationY: 0, scale: 0.9, boxShadow: "0 0 0 rgba(255,106,61,0)" , rotationY: 15, scale: 1.05, boxShadow: "0 30px 40px -15px rgba(255,106,61,0.5)", duration: 0.8, ease: "power1.inOut" ); t2.fromTo(magicBox, backgroundColor: "#ff6a3d", scale: 1, borderRadius: "20px" , backgroundColor: "#ffbc3d", scale: 1.3, borderRadius: "40px", duration: 0.6, ease: "elastic.out(1, 0.4)" , 0 ); t2.fromTo(rotateText, rotation: 0, color: "#ffffff" , rotation: 5, color: "#ffb86b", duration: 0.5, ease: "back.out" , 0.2 ); new ScrollMagic.Scene( triggerElement: "#scene2", triggerHook: 0.2, duration: "70%" // animation spans 70% of scene2 height ) .setTween(t2) .addTo(controller); // additional parallax effect: move background shape? For demo we add subtle movement on badge const badge2 = document.querySelector('#scene2 .badge'); if(badge2) new ScrollMagic.Scene( triggerElement: "#scene2", duration: "100%", triggerHook: 0 ) .setTween(gsap.fromTo(badge2, x: 0, opacity: 0.6 , x: 30, opacity: 1, ease: "none", yoyo: false, repeat: -1, repeatDelay: 0 )) .addTo(controller); // But better use scroll-driven x movement relative to scroll progress: // remove previous and implement clean progress: const existing = controller.scene((s) => s.triggerElement() === "#scene2" && s); // hacky, just replace: const cleanParallax = new ScrollMagic.Scene( triggerElement: "#scene2", duration: "100%", triggerHook: 0 ) .setTween(gsap.to(badge2, x: 80, ease: "linear", scrollTrigger: false )) .addTo(controller);
// extra polish: each gallery item gets a continuous wiggle effect depending on scroll progress within scene3 // but for extra dynamic, we make color pulse on scroll: const galleryContainer = document.getElementById('gallery'); if(galleryContainer) const hueTween = gsap.to(galleryItems, backgroundColor: "#ff6a3d", duration: 0.4, stagger: 0.1, ease: "none", paused: true, overwrite: true ); const reverseTween = gsap.to(galleryItems, backgroundColor: "#1e2432", duration: 0.4, stagger: 0.1, ease: "none", paused: true ); // create scene that toggles between colors based on progress new ScrollMagic.Scene( triggerElement: "#scene3", duration: "100%", triggerHook: 0 ) .on("progress", (e) => const progress = e.progress; // use GSAP to mix background color effect: not perfect but fun if (progress > 0.2 && progress < 0.8) gsap.to(galleryItems, backgroundColor: "#ff6a3d55", duration: 0.1, overwrite: true ); else gsap.to(galleryItems, backgroundColor: "#1e2432", duration: 0.1, overwrite: true ); ) .addTo(controller); scroll magic mouse windows
.animated-box width: 100px; height: 100px; background: #ff6a3d; margin: 1.5rem auto 0; border-radius: 20px; box-shadow: 0 10px 25px -5px rgba(255,106,61,0.4); // ========== SCENE 2: Dramatic rotation & scale
h2 font-size: 2.2rem; font-weight: 500; margin-bottom: 1rem; letter-spacing: -0.01em; // if you need debug indicators uncomment: //
.flex-gallery display: flex; gap: 2rem; justify-content: center; margin-top: 2rem; flex-wrap: wrap; .gallery-item background: #1e2432; width: 120px; height: 120px; border-radius: 28px; display: flex; align-items: center; justify-content: center; font-size: 2.8rem; transition: all 0.2s;
// ========== SCENE 4: PINNING + progress counter ========== const pinCounterSpan = document.getElementById('pinCounter'); // pin scene with dynamic text update const pinScene = new ScrollMagic.Scene( triggerElement: "#scene4", triggerHook: 0, duration: "100%" // pin for the entire height of scene4 (100vh approx) ) .setPin("#scene4", pushFollowers: false ) .addTo(controller); // update pin progress percentage inside the card if(pinCounterSpan) new ScrollMagic.Scene( triggerElement: "#scene4", triggerHook: 0, duration: "100%" ) .on("progress", (e) => let percent = Math.floor(e.progress * 100); pinCounterSpan.innerText = percent + "%"; // optional: change color based on progress if(percent > 70) pinCounterSpan.style.color = "#ff3d6a"; else if(percent > 30) pinCounterSpan.style.color = "#ffbc3d"; else pinCounterSpan.style.color = "#ff6a3d"; ) .addTo(controller); // additional animation inside pinned area: scale card text while pinned const card4 = document.getElementById('card4'); if(card4) new ScrollMagic.Scene( triggerElement: "#scene4", duration: "100%", triggerHook: 0 ) .setTween(gsap.fromTo(card4.querySelector('p'), letterSpacing: "0px", filter: "blur(0px)" , letterSpacing: "2px", filter: "blur(0.6px)", duration: 1, ease: "none", yoyo: false, repeat: 1, repeatDelay: 0.2 )) .addTo(controller); // smoother: dynamic scale on pin progress new ScrollMagic.Scene( triggerElement: "#scene4", duration: "100%" ) .on("progress", (e) => const scaleVal = 1 + (e.progress * 0.1); gsap.set(card4, scale: scaleVal, transformOrigin: "center center" ); ) .addTo(controller); // ========== ADDITIONAL: global parallax on scroll? just for ambiance ========== // apply a background shift to scene2 as extra demonstration of Windows mouse performance const scene2Bg = document.querySelector('.scene-2'); if(scene2Bg) new ScrollMagic.Scene( triggerElement: "#scene2", duration: "200%", triggerHook: 0 ) .setTween(gsap.to(scene2Bg, backgroundPosition: "10% 30%", ease: "linear", duration: 2 )) .addTo(controller); // ========== Final: scroll wheel smooth behavior note ========== // To ensure that Windows mouse wheel feels crisp and no lag, ScrollMagic uses passive event listeners // Also we force a refresh on resize to avoid misalignments window.addEventListener('resize', () => controller.update(true); ); // optional small debug: remove indicators for cleaner production, but left as comment. // if you need debug indicators uncomment: // new ScrollMagic.Scene(triggerElement: "#scene4").addIndicators().addTo(controller); // little console greeting console.log("ScrollMagic master article ready )(); </script> </body> </html>