Shots in this scene
Scroll Timelines in JavaScript
In one line
new ScrollTimeline({ source }) and new ViewTimeline({ subject }) make the same timelines from JavaScript, and element.animate(keyframes, { timeline, rangeStart, rangeEnd }) plays an animation on them. Use it for animations created at run time, and test for support first.
Think of it like this
A thermostat with a dial and a remote. The dial on the wall (CSS) is always there and does one job. The remote (JavaScript) can do the same job from anywhere, at any moment, for any room you point it at.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- ScrollTimeline
- The JavaScript class for a scroll progress timeline. Its
sourceis the scroller, itsaxisthe direction, itscurrentTimea percentage. - ViewTimeline
- The class for a view progress timeline. Its
subjectis the element followed. It also hasstartOffsetandendOffset. - rangeStart / rangeEnd
- Options of
animate()(and properties of the animation) that choose the part of the timeline used, likeanimation-rangein CSS. - feature detection
- Checking that the browser has a feature (
"ScrollTimeline" in window) before using it, instead of assuming.
Step by step
01Create the timelines
"The same timelines from code" tests for both classes, makes a ScrollTimeline and a ViewTimeline, and plays one animate() on each.
02Read the range back
The card's rangeStart prints as entry 0%, and the bar's duration option is auto.
03Remove the scroller
In "What happens when the source goes away": scaleX 0.60 half way, 0.10 and progress null when removed, progress 0% when it is back.
04Cancel
cancel() ends the animation: the meter returns to its own style and no animation is left.
Try it yourself
- 1
Give the view timeline an inset
Measured on a 100 px scroller with a 40 px subject starting at 300 px:
new ViewTimeline({ subject })runs from 200 px to 340 px. Withinset: "20px"it runs from 220 px to 320 px. With the list forminset: ["auto", CSS.px(20)]it is 220 px to 340 px: only the end inset, so the animation starts 20 px later and ends where it did.
Code & diagrams
Scroll the box: the bar follows the scroll, and the card fades in over its entry. The whole script is inside a feature test.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
progress timeline: ScrollTimeline · axis block · source is the scroller: true
journey timeline: ViewTimeline · subject is the card: true
card range starts at: entry 0%
bar duration option: autoThe meter's own style is scaleX(0.1). The animation goes from 0.2 to 1. The buttons repeat the removal by hand.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
scrolled half way: scaleX 0.60, timeline progress 50%
scroller removed: scaleX 0.10, timeline progress null, animation running
the timeline still points at it: true
scroller back: scaleX 0.20, progress 0%, scrollTop 0
after cancel(): scaleX 0.10, 0 animations leftBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Remove the scroller and keep going
In "What happens when the source goes away" remove the line run.cancel(); and read the last console line.
Break #2
Use ScrollTimeline without checking
In "The same timelines from code" add delete window.ScrollTimeline; as the first line of the script, and change the if line to if (true) {.
Myth vs fact
Myth
A JavaScript scroll timeline is the same as a scroll listener that sets a style.
Fact
The browser still applies the animation. Your code only creates it. No code runs per scroll event.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
One timeline object can drive many animations: create
new ScrollTimeline({ source })once and pass the same object astimelineto everyanimate()call. They are then guaranteed to agree.
Remember this
- 1
The problem this solves: CSS ties an animation to a timeline when the stylesheet loads. But some animations are created by code: a card the user just added, a list built from data, a range that depends on a measured height. For these you need to create the timeline and the animation yourself.
- 2
The two classes.
new ScrollTimeline({ source, axis }): the source is the scroller (default:document.scrollingElement), the axis"block"(default),"inline","x"or"y".new ViewTimeline({ subject, axis, inset }): the subject is the element to follow; the inset is a string like"20px"or a list ofCSS.px(...)values. The first example makes one of each. - 3
Playing an animation on one.
element.animate(keyframes, { timeline, fill: "both" }). Leave outduration: it staysauto, the whole range (the example printsauto). Choose the part of a view timeline withrangeStartandrangeEnd("entry 0%").animation.rangeStarthas arangeNameand anoffset. A bad range throwsTypeError: … Animation range must be a name <length-percent> pair.cancel()andgetAnimations()work as usual, and the timeline has nodestroy(): cancel what you started. - 4
Always test for support.
"ScrollTimeline" in windowis the check (CSS hasCSS.supports("animation-timeline", "scroll()")). Without it, a browser lacking the class fails withReferenceError: ScrollTimeline is not defined(measured by deleting the class) and the script stops. Decide the fallback first: nothing (a progress bar), anIntersectionObserver(a reveal, Topic 12.5), or a polyfill that imitates the timelines in JavaScript (it cannot run on the compositor). - 5
A timeline whose source disappears. Remove the scroller and the timeline goes inactive: progress
null, the animation stillrunningbut with no effect, the element back to its own style, andtimeline.sourcestill pointing at the removed element. Put the scroller back and the progress restarts from 0%, because a re-inserted element's scroll position resets (second example).
Explain it without notes
When would you create a scroll timeline in JavaScript rather than write animation-timeline in CSS?
What does a ScrollTimeline do when its source element is removed from the page?
Practice
Write a function follow(scroller, bar) that returns a cancel function, and does nothing (returns a no-op) if scroll timelines are missing.
A row of cards is added after load. Give each one a reveal on its own view timeline, from entry 0% to entry 100%.
Done when you can
You can create a
ScrollTimelineand aViewTimelineand playanimate()on themYou leave
durationout and setrangeStartandrangeEndas stringsYou test for support, and decide the fallback first
You cancel animations when their element is removed