diff --git a/scroll-animations-1/Overview.bs b/scroll-animations-1/Overview.bs index 2b00508130e..bfec8e257f9 100644 --- a/scroll-animations-1/Overview.bs +++ b/scroll-animations-1/Overview.bs @@ -238,6 +238,99 @@ spec:selectors-4; type:dfn; text:selector References to the [=root element=] propagate to the document viewport (which functions as its [=scroll container=]). +
+ Consider the following style sheet and markup: + + ```css + @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 + + … + + +
+ … +
Animation Subject
+ … +
+ + + ``` + + The `.subject`‘s animation is set up to be driven by + an anonymous [=Scroll progress timeline=] created with ''scroll()''. + Because no <> or <> 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` to be + `aliceblue` when at the start of the scroller + and `cornflowerblue` when scrolled to the very end. + Intermediary scroll positions results in an interpolated value. +
+ +
+ Building on the previous example, changing the 'animation-timeline' + to the following results in + the document viewport driving the animation progress. + + ```css + .subject { + animation: change-background linear both; + animation-timeline: scroll(root); + } + ``` +
+ +
+ The following declarations for 'animation-timeline' are all equivalent + but some are more explicit about what to target: + + ```css + /* 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 <> and <> + are ''nearest'' and ''block'' respectively. +
+ +
+ To animate the scroller itself, use the ''self'' keyword as the <> + + ```css + @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 @@ -335,6 +428,50 @@ spec:selectors-4; type:dfn; text:selector The values of {{ScrollTimeline/source}} and {{AnimationTimeline/currentTime}} are both computed when either is requested or updated. +
+ In the following example, a {{ScrollTimeline}} instance + that tracks the document viewport in the block direction + is created: + + ```js + const myTimeline = new ScrollTimeline({ + source: document.documentElement, + }); + ``` + + The created {{ScrollTimeline}} instance can be used + to animate an element as follows: + + ```js + const progressbar = document.querySelector('#progress'); + + progressbar.animate( + { + transform: ['scaleX(0)', 'scaleX(1)'], + }, + { + fill: 'forwards', + timeline: myTimeline, + } + ); + ``` +
+ +
+ In this example, the `.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. + + ```js + const scroller = document.querySelector('.scroller'); + + const myTimeline = new ScrollTimeline({ + source: scroller, + axis: 'inline', + }); + ``` +
+ ## Named Scroll Progress Timelines ## {#scroll-timelines-named} [=Scroll progress timelines=] can also be defined on the [=scroll container=] itself, @@ -349,6 +486,26 @@ spec:selectors-4; type:dfn; text:selector with 'scroll-timeline-name' as the [=coordinating list base property=]. See [[css-values-4#linked-properties]]. +
+ In the following example the `.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=]: + + ```css + .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; + } + ``` +
+ ### Naming a Scroll Progress Timeline: the 'scroll-timeline-name' property ### {#scroll-timeline-name}
@@ -398,6 +555,23 @@ spec:selectors-4; type:dfn; text:selector
 	'scroll-timeline-name' and 'scroll-timeline-axis'
 	in a single declaration.
 
+	
+ The following two rules are equivalent: + + ```css + .scroller { + scroll-timeline-name: my-scroller; + scroll-timeline-axis: inline; + } + ``` + + ```css + .scroller { + scroll-timeline: my-scroller inline; + } + ``` +
+ # View Progress Timelines # {#view-timelines} Often animations are desired to start and end @@ -560,6 +734,38 @@ spec:selectors-4; type:dfn; text:selector of the [=view progress visibility range=], as defined for 'view-timeline-inset'. +
+ In the following example, each direct child of the `.scroller` element + will reveal itself as it crosses the scrollport. + + ```css + @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 <> and <<'view-timeline-inset'>>, + resulting in the scroller being tracked in the ''block'' direction. + + 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 @@ -670,6 +876,27 @@ spec:selectors-4; type:dfn; text:selector The values of {{ViewTimeline/subject}}, {{ScrollTimeline/source}}, and {{AnimationTimeline/currentTime}} are all computed when any of them is requested or updated. +
+ In the following example, each child of the `.scroller` element + will reveal itself as it crosses the scrollport. + + ```js + document.querySelectorAll('.scroller > *').forEach(childElement => { + const timeline = new ViewTimeline({ + subject: childElement, + axis: 'block', + }); + + childElement.animate({ + opacity: [ 0, 1 ], + }, { + fill: 'both', + timeline, + }); + }); + ``` +
+ ## Named View Progress Timelines ## {#view-timelines-named} [=View progress timelines=] can also be defined declaratively @@ -684,6 +911,28 @@ spec:selectors-4; type:dfn; text:selector with 'view-timeline-name' as the [=coordinating list base property=]. See [[css-values-4#linked-properties]]. +
+ In the following example, each child of the `.scroller` element + will reveal itself as it crosses the scrollport. + This is done by explicitly referring to + the [=named view progress timeline=] created on the `.scroller`: + + ```css + @keyframes reveal { + from { opacity: 0; } + } + + .scroller { + view-timeline: my-scroller block; + } + + .scroller > * { + animation: reveal linear both; + animation-timeline: my-scroller; + } + ``` +
+ ### Naming a View Progress Timeline: the 'view-timeline-name' property ### {#view-timeline-name}
@@ -1021,6 +1270,55 @@ spec:selectors-4; type:dfn; text:selector
 	are only generated for properties that don't have keyframes
 	at or earlier than 0% or at or after 100% (respectively).
 
+	
+ In this example the range information is included directly in the ''@keyframes'' rule: + + ```css + @keyframes animate-in-and-out { + entry 0% { + opacity: 0; transform: translateY(100%); + } + entry 100% { + opacity: 1; transform: translateY(0); + } + + exit 0% { + opacity: 1; transform: translateY(0); + } + exit 100% { + opacity: 0; transform: translateY(-100%); + } + } + + .scroller > * { + animation: linear animate-in-and-out; + animation-timeline: view(); + } + ``` + + It has the same outcome as the following snippet which uses + two distinct sets of keyframes combined with 'animation-range' + + ```css + @keyframes animate-in { + 0% { opacity: 0; transform: translateY(100%); } + 100% { opacity: 1; transform: translateY(0); } + } + + @keyframes animate-out { + 0% { opacity: 1; transform: translateY(0); } + 100% { opacity: 0; transform: translateY(-100%); } + } + + .scroller > * { + animation: animate-in linear forwards, + animate-out linear forwards; + animation-timeline: view(); + animation-range: entry, exit; + } + ``` +
+ ## Attaching Animations to Timeline Ranges ## {#named-range-animation-declaration} A set of animation keyframes can be attached @@ -1054,6 +1352,87 @@ spec:selectors-4; type:dfn; text:selector ISSUE: Define application to time-driven animations. +### Examples + +
+ In the following example, each direct child of the `.scroller` element + reveals itself as it enters the scrollport instead of when entirely crossing it. + + This is achieved by setting 'animation-range' to limit the [=active interval=] + to the ''entry'' range instead of the default ''cover'' range: + + ```css + @keyframes reveal { + from { opacity: 0; } + } + + .subject { + animation: reveal linear both; + animation-timeline: view(); + animation-range: entry; + } + ``` +
+ +
+ A variation of the previous example is to add both entry and exit effects, + each linked to their own 'animation-range': + + ```css + @keyframes reveal { + from { opacity: 0; } + } + @keyframes hide { + to { opacity: 0; } + } + + .scroller > * { + animation: reveal linear both, + hide linear forwards; + animation-timeline: view(); + animation-range: entry, exit; + } + ``` + + The `reveal` effect is linked to the ''entry'' range + while the `hide` effect is linked to the ''exit'' range. +
+ +
+ The Web Animations API equivalent of the previous example is the following: + + ```js + document.querySelectorAll('.scroller > *').forEach(childElement => { + const timeline = new ViewTimeline({ + subject: childElement, + axis: 'block', + }); + + // Reveal effect on entry + childElement.animate({ + opacity: [ 0, 1 ], + }, { + fill: 'forwards', + timeline, + rangeStart: 'entry 0%', + rangeEnd: 'entry 100%', + }); + + // Hide effect on exit + childElement.animate({ + opacity: [ 1, 0 ], + }, { + fill: 'forwards', + timeline, + rangeStart: 'exit 100%', + rangeEnd: 'exit 100%', + }); + }); + ``` + + Each element gets its own timeline which is used by both effects. +
+ ### Specifying an Animation’s Timeline Range: the 'animation-range' shorthand ### {#animation-range}
@@ -1109,6 +1488,19 @@ spec:selectors-4; type:dfn; text:selector
 		
+
+ Because <<'animation-range-start'>> and <<'animation-range-end'>> accept <>s, + it’s perfectly fine to do calculations using ''calc()'' for the ranges: + + ```css + #subject { + animation: anim linear both; + animation-timeline: view(); + animation-range: entry calc(100% - 100px) exit calc(0% + 100px); + } + ``` +
+ ISSUE(8438): What's the best way to handle defaulting of omitted values here? ### Specifying an Animation’s Timeline Range Start: the 'animation-range-start' property ### {#animation-range-start}