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=]). +
@@ -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 <+ 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. +> 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=]. + + 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 <+ 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}>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); + } + ``` +