motion · scroll
Scroll-triggered animation
also called animate on scroll (community), reveal on scroll (community), AOS (community), in-view animation (framer-motion), toggleActions (gsap), scroll reveal (community), animation-trigger (css), timeline-trigger (css)
An animation that scroll position only starts, after which it runs on its own clock and does not rewind when the reader scrolls back up.
Two very different effects get called scroll animation, and the difference is whether the scroll is a switch or a dial. Scroll-linked motion follows the position continuously: back up a little and it backs up with you, because the scrollbar is the playhead. That is parallax, a reading-progress bar, a collapsing toolbar. Scroll-triggered motion uses position only to fire: the element crosses a line, the animation starts, and from that moment it runs on time like any other animation. Scrolling on past it does not speed it up, and scrolling back does not rewind it.
The script version is a few lines and has not changed much in a decade. An
IntersectionObserver watches each element with a threshold and usually a negative
rootMargin, so the line sits comfortably inside the viewport rather than at its very
edge; when the entry reports intersecting, the element gets a class and the observer
stops watching it. The last step is the one people forget, and it is what the once
behaviour is made of. Libraries spell it in their own dialects: GSAP’s ScrollTrigger
calls it toggleActions: 'play none none none', Framer Motion has whileInView with
viewport={{ once: true }}, and the CSS working group is landing a declarative form so
that a threshold can start a keyframe animation with no observer at all.
The craft is mostly about not breaking the page. Anything that animates in has to already occupy its box, or the layout reflows as each element arrives and every threshold below it moves, which is how these effects end up firing at the wrong moment or not at all: opacity and transform are the safe pair for exactly that reason. The content should also be readable with the effect switched off, since an element parked at zero opacity waiting for a script that failed is content nobody will ever see, including print and, in some cases, search. And the trigger line belongs well inside the viewport, because an element that starts its entrance at the exact bottom edge has already been half read by the time it fades in.
The once rule is not just an optimization. A page whose sections re-animate every time the reader scrolls back over them never settles, and re-reading a paragraph should not cost a second entrance. So this is an entrance animation with a position for a cue, staggered if a group arrives together, played a single time. And because the reader did not ask for any of it, a stated prefers-reduced-motion should skip straight to the end state: the content still arrives, it simply does not travel to get there.
Which word?
| If you want | say |
|---|---|
| scrolling should start the motion, not steer it | scroll-triggered animation |
| the reader's scroll should drive the animation frame by frame | scroll-linked animation |
| the animation should track one element, not the whole page | view progress timeline |
Related
See also: Lazy loading · Quick return header