跪拜 Guibai
← All articles
Frontend · JavaScript · CSS

Building a Pinned Scroll Narrative with GSAP and Vue

By Mh ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Scroll-driven storytelling is a staple of high-end product pages, but getting the sequencing, video sync, and cleanup right requires careful orchestration across DOM layout, GSAP timelines, and framework lifecycle hooks. This walkthrough surfaces the concrete decisions — track-level movement, opacity vs. autoAlpha, empty tweens for pacing — that turn a demo into a production-safe component.

Summary

A pinned scroll narrative locks the screen while the user scrolls, converting scroll distance into timeline progress. Cards slide to center, enlarge, and fade out to reveal layered detail sections — all within a single sticky container. The technique uses a tall container to create scroll room, a sticky stage to hold the viewport, and a single GSAP timeline scrubbed by ScrollTrigger. Card tracks move as a whole unit so each card can center itself against the stage; detail panels crossfade in sequence with empty tweens providing reading pauses. Video playback is synchronized to panel opacity, pausing and restarting as sections enter and leave the visible threshold. Cleanup relies on gsap.context() to revert all animations and ScrollTriggers when the Vue component unmounts, preventing timeline leaks in single-page apps.

Takeaways
Use a tall container (e.g., 1200vh) to create scroll distance while a sticky stage holds the visible screen.
Move the entire card track, not individual cards, so each card can center itself against the stage by calculating stageCenter - cardCenter.
Prefer opacity over autoAlpha for individual card fades; autoAlpha's visibility toggle can hide cards during reverse scroll.
Insert empty tweens (`.to({}, { duration: 0.75 })`) into the timeline to create reading pauses without changing the DOM.
Restore a card to its original size before moving the track back to its initial position, so the transition reads as a smooth retraction.
Sync video playback to section opacity: play from start when opacity exceeds 0.65, pause all others, and avoid resetting currentTime if the video is already active.
Wrap all GSAP animations and ScrollTriggers in gsap.context() so a single revert() call cleans up on component unmount.
Call ScrollTrigger.refresh() inside requestAnimationFrame after mounting to recalculate positions after the DOM settles.
Conclusions

The distinction between opacity and autoAlpha is a small API choice with outsized consequences for reverse-scroll behavior — a detail easily missed when generating code from AI.

Empty tweens are an underused GSAP pattern that decouple pacing from DOM changes, letting the scroll position control reading rhythm without extra markup.

Video sync logic that checks activeVideo before resetting currentTime prevents a jarring replay loop during scrub, a subtlety that separates polished scroll experiences from janky ones.

The author's reflection on AI-generated code is notable: the real work shifts from writing lines to understanding structure, verifying logic, and making precise timing adjustments — a skill set that remains firmly in the developer's hands.

Concepts & terms
Pinned scroll narrative
A web animation pattern where the viewport appears frozen while scrolling; the scrollbar instead drives a timeline that reveals content in place, often used for product storytelling.
Scrub
A ScrollTrigger option that links the scroll position directly to animation progress, so scrolling forward and backward moves the timeline forward and backward.
autoAlpha
A GSAP property that combines opacity and visibility; setting autoAlpha to 0 makes an element invisible and also sets visibility: hidden, which can prevent it from reappearing during reverse scroll.
gsap.context()
A GSAP utility that collects all animations and ScrollTriggers created within a callback, enabling a single revert() call to clean them up — essential for framework lifecycle management.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗