1. Introduction
This specification defines mechanisms for driving the progress of an animation based on the scroll progress of a scroll container. These scroll-driven animations use a timeline based on scroll position, rather than one based on clock time. This module provides both an imperative API building on the Web Animations API as well as a declarative API building on CSS Animations. [WEB-ANIMATIONS-1]
There are two types of scroll-driven timelines:
-
Scroll Progress Timelines, which are linked to the scroll progress of a particular scroll container
-
View Progress Timelines, which are linked to the view progress of a particular box through a scrollport
Note: Scroll-driven animations, whose progress is linked to the scroll position, are distinct from scroll-triggered animations, which are triggered by a scroll position, but whose progress is driven by time.
1.1. Relationship to other specifications
Web Animations [WEB-ANIMATIONS-1] defines an abstract conceptual model for animations on the Web platform, with elements of the model including animations and their timelines, and associated programming interfaces. This specification extends the Web Animations model by defining scroll-driven timelines and allowing them to drive progress in animations to create scroll-driven animations.
This specification introduces both programming interfaces for interacting with these concepts, as well as CSS properties that apply these concepts to CSS Animations [CSS-ANIMATIONS-1]. To the extent the behavior of these CSS properties is described in terms of the programming interfaces, User agents that do not support scripting may still conform to this specification by implementing the CSS features to behave as if the underlying programming interfaces were in place.
Like most operations in CSS besides selector matching, features in this specification operate over the flattened element tree.
1.2. Relationship to asynchronous scrolling
Some user agents support scrolling that is asynchronous with respect to layout or script. This specification is intended to be compatible with such an architecture.
Specifically, this specification allows expressing scroll-driven effects in a way that does not require script to run each time the effect is sampled. User agents that support asynchronous scrolling are allowed (but not required) to sample such effects asynchronously as well.
1.3. Value Definitions
This specification follows the CSS property definition conventions from [CSS2] using the value definition syntax from [CSS-VALUES-3]. Value types not defined in this specification are defined in CSS Values & Units [CSS-VALUES-3]. Combination with other CSS modules may expand the definitions of these value types.
In addition to the property-specific values listed in their definitions, all properties defined in this specification also accept the CSS-wide keywords as their property value. For readability they have not been repeated explicitly.
2. Scroll Progress Timelines
Scroll progress timelines are timelines linked to progress in the scroll position of a scroll container along a scrollable axis. The startmost scroll position represents 0% progress and the endmost scroll position represents 100% progress.
Scroll progress timelines can be referenced in animation-timeline
anonymously using the scroll() functional notation
or by name (see § 4.2 Named Timeline Scoping and Lookup)
after declaring them using the scroll-timeline properties.
In the Web Animations API,
they can be represented anonymously by a ScrollTimeline object.
2.1. Calculating Progress for a Scroll Progress Timeline
Progress (the current time) for a scroll progress timeline is calculated as: scroll offset ÷ (scrollable overflow size − scroll container size)
If the 0% position and 100% position coincide (i.e. the denominator in the current time formula is zero), the timeline is inactive.
In paged media, scroll progress timelines that would otherwise reference the document viewport are also inactive.
2.2. Anonymous Scroll Progress Timelines
2.2.1. The scroll() notation
The scroll() functional notation can be used as a <single-animation-timeline> value in animation-timeline and specifies a scroll progress timeline. Its syntax is
<scroll()> = scroll( [ <scroller> || <axis> ]? ) <axis> = block | inline | x | y <scroller> = root | nearest | self
By default, scroll() references the block axis of the nearest ancestor block-axis scroll container. Its arguments modify this lookup as follows:
- block
- Specifies to use the measure of progress along the block axis of the block-axis scroll container. (Default.)
- inline
- Specifies to use the measure of progress along the inline axis of the inline-axis scroll container.
- x
- Specifies to use the measure of progress along the horizontal axis of the x-axis scroll container.
- y
- Specifies to use the measure of progress along the vertical axis of the y-axis scroll container.
- nearest
- Specifies to use the nearest ancestor scroll container with an appropriate scrollable axis (Default.)
- root
- Specifies to use the document viewport as the scroll container.
- self
- Specifies to use the element’s own principal box as the scroll container. If the principal box is not a scroll container in the appropriate axis, then the scroll progress timeline is inactive.
Note: Progress is in reference to the scroll origin, which can flip depending on writing mode, even when x or y is specified.
References to the root element propagate to the document viewport (which functions as its scroll container).
@keyframes change-background{ from{ background-color : aliceblue; } to{ background-color : cornflowerblue; } } .subject{ animation : change-background linear both; /* Use an anonymous scroll progress timeline to drive the animation */ animation-timeline:scroll (); }
< html > …< body > <!-- Note: .scroller is a scrollable box --> < div class = "scroller" > …< div class = "subject" > Animation Subject</ div > …</ div > </ body > </ html >
The .subject‘s animation is set up to be driven by
an anonymous Scroll progress timeline created with scroll().
Because no <scroller> or <axis> are passed into the function,
the default values of nearest and block respectively are used.
Because .scroller is the nearest ancestor scroll container,
this results in the .scroller element driving the animation:
as you scroll .scroller up and down, the subject’s animations progress
moves forwards or backwards in direct response.
This results in the background-color being
aliceblue when at the start of the scroller
and cornflowerblue when scrolled to the very end.
Intermediary scroll positions results in an interpolated value.
.subject{ animation : change-background linear both; animation-timeline : scroll ( root); }
/* These all are equivalent */ animation-timeline:scroll (); animation-timeline : scroll ( block); animation-timeline : scroll ( nearest); animation-timeline : scroll ( block nearest); animation-timeline : scroll ( nearest block);
This is because the default values for <scroller> and <axis> are nearest and block respectively.
@keyframes change-color{ from{ color : black; } to{ color : hotpink; } } .subject{ animation : change-color linear both; /* Use an anonymous scroll progress timeline to drive the animation */ animation-timeline:scroll ( self); }
Each use of scroll() corresponds to its own instance of ScrollTimeline
in the Web Animations API,
even if multiple elements use scroll() to refer
to the same scroll container with the same arguments.
2.2.2. The ScrollTimeline Interface
enum {ScrollAxis ,"block" ,"inline" ,"x" };"y" dictionary {ScrollTimelineOptions Element ?;source ScrollAxis = "block"; }; [axis Exposed =Window ]interface :ScrollTimeline AnimationTimeline {constructor (optional ScrollTimelineOptions = {});options readonly attribute Element ?source ;readonly attribute ScrollAxis axis ; };
A ScrollTimeline is an AnimationTimeline
that represents a scroll progress timeline.
It can be passed to
the Animation constructor or the animate() method
to link the animation to a scroll progress timeline.
source, of type Element, readonly, nullable-
The scroll container element whose scroll position drives the progress of the timeline.
axis, of type ScrollAxis, readonly-
The axis of scrolling that drives the progress of the timeline. See value definitions for <axis>, above.
Inherited attributes:
currentTime(inherited fromAnimationTimeline)-
Represents the scroll progress of the scroll container as a percentage CSSUnitValue, with 0% representing its startmost scroll position (in the writing mode of the scroll container). Null when the timeline is inactive.
While 0% will usually represent the scroll container’s initial scroll position, it might not depending on its content distribution. See CSS Box Alignment 3 § 5.3 Alignment Overflow and Scroll Containers. Is this what we want?
Add a note about whether currentTime
can be negative or > 100%.
ScrollTimeline(options)-
Creates a new
ScrollTimelineobject using the following procedure:-
Let timeline be the new
ScrollTimelineobject. -
Set the
sourceof timeline to:- If the
sourcemember of options is present, -
The
sourcemember of options. - Otherwise,
-
The
scrollingElementof theDocumentassociated with theWindowthat is the current global object.
- If the
-
Set the
axisproperty of timeline to the corresponding value from options.
-
If the source of a ScrollTimeline
is an element whose principal box does not exist
or is not a scroll container in the timeline’s axis,
or if there is no scrollable overflow,
then the ScrollTimeline is inactive.
A ScrollTimeline’s duration is 100%.
The values of source and currentTime
are both computed when either is requested or updated.
ScrollTimeline instance
that tracks the document viewport in the block direction
is created:
const myTimeline= new ScrollTimeline({ source: document. documentElement, });
The created ScrollTimeline instance can be used
to animate an element as follows:
const progressbar= document. querySelector( '#progress' ); progressbar. animate( { transform: [ 'scaleX(0)' , 'scaleX(1)' ], }, { fill: 'forwards' , timeline: myTimeline, } );
This makes the targeted #progress element
animate from a scaleX(0) transform when at the top of the page
to a scaleX(1) when at the bottom of the page.
.scroller element is the element
whose scroll position drives the progress of the timeline.
The timeline is set to track its scroll offset in the inline direction.
const scroller= document. querySelector( '.scroller' ); const myTimeline= new ScrollTimeline({ source: scroller, axis: 'inline' , });
Scrolling the root scroller has no effect here,
it is only when you scroll the .scroller element in the inline direcion
that the animation that uses the timeline will tick.
2.3. Named Scroll Progress Timelines
Scroll progress timelines can also be defined on the scroll container itself, and then referenced by name by elements within the name’s scope (see § 4.2 Named Timeline Scoping and Lookup).
Such named scroll progress timelines are declared in the coordinated value list constructed from the longhands of the scroll-timeline shorthand property, which form a coordinating list property group with scroll-timeline-name as the coordinating list base property. See CSS Values 4 § A Coordinating List-Valued Properties.
.subject’s animation is driven by
the named scroll progress timeline named --my-scroller.
This timeline is created on its .scroller ancestor and is set up to
measure progress along the inline axis:
.scroller{ scroll-timeline-name : --my-scroller; scroll-timeline-axis : inline; } .scroller .subject{ animation : grow linear both; /* Use the '--my-scroller' scroll progress timeline to drive the animation */ animation-timeline: --my-scroller; }
As you scroll horizontally through the .scroller element,
the grow animation on the contained .subject element
will move forwards or backwards in direct response.
2.3.1. Naming a Scroll Progress Timeline: the scroll-timeline-name property
| Name: | scroll-timeline-name |
|---|---|
| Value: | [ none | <dashed-ident> ]# |
| Initial: | none |
| Applies to: | all elements |
| Inherited: | no |
| Percentages: | n/a |
| Computed value: | list, each item either a CSS identifier or the keyword none |
| Canonical order: | per grammar |
| Animation type: | not animatable |
Specifies names for the named scroll progress timelines associated with this element.
2.3.2. Axis of a Scroll Progress Timeline: the scroll-timeline-axis property
| Name: | scroll-timeline-axis |
|---|---|
| Value: | [ block | inline | x | y ]# |
| Initial: | block |
| Applies to: | all elements |
| Inherited: | no |
| Percentages: | n/a |
| Computed value: | a list of the keywords specified |
| Canonical order: | per grammar |
| Animation type: | not animatable |
Specifies the axis of any named scroll progress timelines sourced from this scroll container. If this box is not a scroll container in the specified axis, then the corresponding named scroll progress timeline is inactive.
Values are as defined for scroll().
2.3.3. Scroll Timeline Shorthand: the scroll-timeline shorthand
| Name: | scroll-timeline |
|---|---|
| Value: | [ <'scroll-timeline-name'> <'scroll-timeline-axis'>? ]# |
| Initial: | see individual properties |
| Applies to: | all elements |
| Inherited: | no |
| Percentages: | see individual properties |
| Computed value: | see individual properties |
| Animation type: | not animatable |
| Canonical order: | per grammar |
This property is a shorthand for setting scroll-timeline-name and scroll-timeline-axis in a single declaration.
.scroller{ scroll-timeline-name : --my-scroller; scroll-timeline-axis : inline; }
.scroller{ scroll-timeline : --my-scroller inline; }
3. View Progress Timelines
Often animations are desired to start and end during the portion of the scroll progress timeline that a particular box (the view progress subject) is in view within the scrollport. View progress timelines are segments of a scroll progress timeline that are scoped to the scroll positions in which any part of the subject element’s principal box intersects its nearest ancestor scrollport (or more precisely, the relevant view progress visibility range of that scrollport). The startmost such scroll position represents 0% progress, and the endmost such scroll position represents 100% progress; see § 3.2 Calculating Progress for a View Progress Timeline.
Note: The 0% and 100% scroll positions are not always reachable, e.g. if the box is positioned at the start edge of the scrollable overflow rectangle, it might not be possible to scroll to < 32% progress.
View progress timelines can be referenced
anonymously using the view() functional notation
or by name (see § 4.2 Named Timeline Scoping and Lookup)
after declaring them using the view-timeline properties
on the view progress subject.
In the Web Animations API,
they can be represented anonymously by a ViewTimeline object.
3.1. View Progress Timeline Ranges
View progress timelines define the following named timeline ranges:
- cover
-
Represents the full range of the view progress timeline:
-
0% progress represents the latest position at which the start border edge of the element’s principal box coincides with the end edge of its view progress visibility range.
-
100% progress represents the earliest position at which the end border edge of the element’s principal box coincides with the start edge of its view progress visibility range.
-
- contain
-
Represents the range during which the principal box
is either fully contained by, or fully covers,
its view progress visibility range within the scrollport.
-
0% progress represents the earliest position at which either:
-
the start border edge of the element’s principal box coincides with the start edge of its view progress visibility range.
-
the end border edge of the element’s principal box coincides with the end edge of its view progress visibility range.
-
-
100% progress represents the latest position at which either:
-
the start border edge of the element’s principal box coincides with the start edge of its view progress visibility range.
-
the end border edge of the element’s principal box coincides with the end edge of its view progress visibility range.
-
-
- entry
- Represents the range during which the principal box is entering the view progress visibility range.
- exit
- Represents the range during which the principal box is exiting the view progress visibility range.
- entry-crossing
-
Represents the range during which the principal box crosses the end
border edge
-
0% progress represents the latest position at which the start border edge of the element’s principal box coincides with the end edge of its view progress visibility range.
-
100% progress represents the earliest position at which the end border edge of the element’s principal box coincides with the end edge of its view progress visibility range.
-
- exit-crossing
-
Represents the range during which the principal box crosses the start
border edge
-
0% progress represents the latest position at which the start border edge of the element’s principal box coincides with the start edge of its view progress visibility range.
-
100% progress represents the earliest position at which the end border edge of the element’s principal box coincides with the start edge of its view progress visibility range.
-
- scroll
-
Represents the full range of the scroll container on which the view progress timeline
is defined.
-
0% progress represents the startmost scroll position of the scroll container underlying the view progress timeline.
-
100% progress represents the endmost scroll position of the scroll container underlying the view progress timeline.
-
In all cases, the writing mode used to resolve the start and end sides is the writing mode of the relevant scroll container. Transforms are ignored, but relative and absolute positioning are accounted for.
Note: For sticky-positioned boxes the 0% and 100% progress conditions can sometimes be satisfied by a range of scroll positions rather than just one. Each range therefore indicates whether to use the earliest or latest qualifying position.
[CSS-POSITION-3] [CSS-TRANSFORMS-1]
3.2. Calculating Progress for a View Progress Timeline
Progress (the current time) in a view progress timeline is calculated as: distance ÷ range where:
-
distance is the current scroll offset minus the scroll offset corresponding to the start of the cover range
-
range is the scroll offset corresponding to the start of the cover range minus the scroll offset corresponding to the end of the cover range
If the 0% position and 100% position coincide (i.e. the denominator in the current time formula is zero), the timeline is inactive.
In paged media, view progress timelines that would otherwise reference the document viewport are also inactive.
3.3. Anonymous View Progress Timelines
3.3.1. The view() notation
The view() functional notation can be used as a <single-animation-timeline> value in animation-timeline and specifies a view progress timeline in reference to the nearest ancestor scroll container that’s scrollable in the specified axis. Its syntax is
<view()> = view( [ <axis> || <'view-timeline-inset'> ]? )
By default, view() references the block axis; as for scroll(), this can be changed by providing an explicit <axis> value.
The optional <'view-timeline-inset'> value provides an adjustment of the view progress visibility range, as defined for view-timeline-inset.
.scroller element
will reveal itself as it crosses the scrollport.
@keyframes reveal{ from{ opacity : 0 ; } } .scroller > *{ animation : reveal linear both; animation-timeline : view (); }
For every element matched by the selector, a view progress timeline gets created to drive the animation. Because no arguments are passed into view() it uses the default values for <axis> and <'view-timeline-inset'>, thus tracking its scroll position in the block axis.
With the keyframes driven by the view progress timeline,
the element will be at opacity: 0 when it is about to enter the scrollport,
and at opacity: 1 when it has just left the scrollport.
Any scroll position in between results in an interpolated value.
Note: Because matched elements can be positioned at different offsets within the scroller or can differ in size, every matched element gets its own unique view progress timeline.
Each use of view() corresponds to its own instance of ViewTimeline
in the Web Animations API,
even if multiple elements use view() to reference
the same element with the same arguments.
3.3.2. The ViewTimeline Interface
dictionary {ViewTimelineOptions Element ;subject ScrollAxis = "block"; (axis DOMString or sequence <(CSSNumericValue or CSSKeywordValue )>)= "auto"; }; [inset Exposed =Window ]interface :ViewTimeline ScrollTimeline {constructor (optional ViewTimelineOptions = {});options readonly attribute Element subject ;readonly attribute CSSNumericValue startOffset ;readonly attribute CSSNumericValue endOffset ; };
A ViewTimeline is an AnimationTimeline
that specifies a view progress timeline.
It can be passed to
the Animation constructor or the animate() method
to link the animation to a view progress timeline.
subject, of type Element, readonly-
The element whose principal box’s visibility in the scrollport defines the progress of the timeline.
startOffset, of type CSSNumericValue, readonly-
Represents the starting (0% progress) scroll position of the view progress timeline as a length offset (in px) from the scroll origin. Null when the timeline is inactive.
endOffset, of type CSSNumericValue, readonly-
Represents the ending (100% progress) scroll position of the view progress timeline as a length offset (in px) from the scroll origin. Null when the timeline is inactive.
Note: The values of startOffset and endOffset
are relative to the scroll origin, not the physical top left corner.
Depending on the writing mode of the scroll container,
they therefore might not match scrollLeft or scrollTop values,
for example in the horizontal axis in a right-to-left (rtl) writing mode.
Inherited attributes:
source(inherited fromScrollTimeline)-
The nearest ancestor of the
subjectwhose principal box establishes a scroll container in the specified axis, whose scroll position drives the progress of the timeline. axis(inherited fromScrollTimeline)-
Specifies the axis of scrolling that drives the progress of the timeline. See <axis>, above.
currentTime(inherited fromAnimationTimeline)-
Represents the current progress of the view progress timeline as a percentage
CSSUnitValuerepresenting its scroll container’s scroll progress at that position. Null when the timeline is inactive.
ViewTimeline(options)-
Creates a new
ViewTimelineobject using the following procedure:-
Let timeline be the new
ViewTimelineobject. -
Set the
subjectandaxisproperties of timeline to the corresponding values from options.
-