diff --git a/scroll-animations-1/Overview.bs b/scroll-animations-1/Overview.bs index f9cbf0a94df..676e451c39c 100644 --- a/scroll-animations-1/Overview.bs +++ b/scroll-animations-1/Overview.bs @@ -239,6 +239,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` 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. +
+ +
+ 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 @@ -336,6 +429,58 @@ 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, + } + ); + ``` + + 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. +
+ +
+ 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', + }); + ``` + + 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. +
+ ## Named Scroll Progress Timelines ## {#scroll-timelines-named} [=Scroll progress timelines=] can also be defined on the [=scroll container=] itself, @@ -350,6 +495,30 @@ 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; + } + ``` + + As you scroll horizontally through the `.scroller` element, + the `grow` animation on the contained `.subject` element + will move forwards or backwards in direct response. +
+ ### Naming a Scroll Progress Timeline: the 'scroll-timeline-name' property ### {#scroll-timeline-name}
@@ -399,6 +568,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 @@ -561,6 +747,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'>>, + 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 @@ -671,6 +889,33 @@ 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, + }); + }); + ``` + + In a vertical scroller, this results in an element going fully transparent (`opacity: 0`) + when its about to enter the scroller from the bottom edge of the scroller + to being fully opaque (`opacity: 1`) when it has completely entered the scroller. + + Scroll positions in between result in an opacity `0` and `1`. +
+ ## Named View Progress Timelines ## {#view-timelines-named} [=View progress timelines=] can also be defined declaratively @@ -685,6 +930,32 @@ spec:selectors-4; type:dfn; text:selector with 'view-timeline-name' as the [=coordinating list base property=]. See [[css-values-4#linked-properties]]. +
+ This example behaves exactly the same + as the previous one: + each child of the `.scroller` element will reveal itself + as it crosses the scrollport. + + The difference is that + instead of using an anonymous [=View progress timeline=] + to drive the animation, it now uses a [=named view progress timeline=] that tracks 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}
@@ -851,19 +1122,19 @@ spec:selectors-4; type:dfn; text:selector
 			}
 
 			.root {
-			  /* declares the scope of a 'scroller' timeline to reach all descendants */
-			  timeline-scope: scroller;
+			  /* declares the scope of a '--scroller' timeline to reach all descendants */
+			  timeline-scope: --scroller;
 			}
 
 			.root .animation {
 			  animation: anim;
-			  /* references the 'scroller' timeline for driving the progress of 'anim' */
-			  animation-timeline: scroller;
+			  /* references the '--scroller' timeline for driving the progress of 'anim' */
+			  animation-timeline: --scroller;
 			}
 
 			.root .animation + .scroller {
-			  /* attaches a scroll progress timeline to the timeline name 'scroller' */
-			  scroll-timeline: scroller;
+			  /* attaches a scroll progress timeline to the timeline named '--scroller' */
+			  scroll-timeline: --scroller;
 			}
 		
 		…
@@ -1022,6 +1293,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 @@ -1055,6 +1375,117 @@ 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; } + } + + .scroller > * { + animation: reveal linear both; + animation-timeline: view(); + animation-range: entry; + } + ``` + + In a vertical scroller, this results in an element going fully transparent (`opacity: 0`) + when its about to enter the scroller from the bottom edge of the scroller + to being fully opaque (`opacity: 1`) when it has completely entered the scroller. + + Scroll positions in between result in an opacity `0` and `1`. +
+ +
+ 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; } + to { opacity: 1; } + } + @keyframes hide { + from { opacity: 1; } + 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. + + In a vertical scroller, when scrolling down, + this results in the element going from `opacity: 0` to `opacity: 1` + as it enters the scrollport from the bottom edge of the scroller. + + When continuing to scroll down, + the element will eventually go from `opacity: 1` to `opacity: 0` + as the subject exits the scrollport at the top edge of the scroller. +
+ +
+ 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%', + }); + }); + ``` + + For every matched element, a single timeline tracking that element is created. + The timeline tracks the element inside its scroller in the block direction. + + The timeline is used to drive two animations added to the element + but the effects are only attached to a part of the range, + thanks to the `rangeStart` and `rangeEnd` options. + + The first animation is attached to the ''entry'' range, + animating the element from `opacity: 0` to `opacity: 1` + as it enters the scrollport. + + The second animation is attached to the ''exit'' range, + animating the element from `opacity: 1` to `opacity: 0` + as it leaves the scrollport. + +
+ ### Specifying an Animation’s Timeline Range: the 'animation-range' shorthand ### {#animation-range}
@@ -1110,6 +1541,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}