Closed bramus closed 1 year ago
Isn't it what we have ScrollTimeline
for? Isn't that simply a use-case for that?
Or, if the author prefers using ViewTimeline
, then it makes sense they have a revealing element that holds the timeline, and the header is simply the target of the animation. Which should be straightforward using the current API, or did I miss something and it's not?
Ok, just checked and we did lose that ability on SctollTimeline
. We definitely need to have offset and duration defined by <length>
s, same as ViewTimeline
can define those using phases.
So @bramus I guess this was the intention here?
I’ve given this some thought over the weekend, and could think of a few possible scenarios to tackle this:
Bring back the old scroll-offsets
as scroll-timeline-offsets
This seems like a straightforward thing, but is too limiting. For example:
body {
scroll-timeline: body-tl block;
scroll-timeline-offsets: 0 90vh; /* 👈 */
}
@keyframes shrink {
from { height: 100vh; }
to { height: 10vh; }
}
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: body-tl;
}
By defining the offsets on the ScrollTimeline
itself, the entire ScrollTimeline
is limited to the range 0
- 90vh
. What if the authors want to animate on scroll outside of that range (e.g. from 90vh
to 100%
)? This is not possible with this approach, unless authors are able to define multiple ScrollTimeline
s on the same element.
Allow scroll
as a (faux) phase with <length-percentage>
Following the way ViewTimeline
phases are defined, a scroll
phase could be introduced. That way there still is 1 set of keyframes and 1 ScrollTimeline
. In the keyframes, authors can target several parts of the timeline by prefixing that phase to the keyframe offsets.
body {
scroll-timeline: body-tl block;
}
@keyframes shrink {
scroll 0 { height: 100vh; } /* 👈 */
scroll 90vh { height: 10vh; } /* 👈 */
}
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: body-tl;
}
Authors would need to explicitly target the (faux) scroll
phase if they want to use it with a ScrollTimeline
. Instead of accepting only <percentage>
s as the keyframe offset, <length>
s would now also be valid here.
One of the cool things about the whole scroll-linked animations concept is that you can use regular keyframes without needing to target any phase. If wanted, maybe, it could be so that “no phase implies the scroll
phase” when used with a ScrollTimeline
?
@keyframes shrink {
0 { height: 100vh; } /* When used linked to a ScrollTimeline, this would imply the scroll phase */
90vh { height: 10vh; } /* When used with a DocumentTimeline animation, this would be invalid as it’s no <length> */
}
Introduce animation-timeline
offsets
A third option would be to introduce the scroll-offsets
on the animation-timeline
level. There would be two properties: animation-timeline-start
and animation-timeline-end
, with a <length-percentage>
as their allowed value.
body {
scroll-timeline: body-tl block;
}
@keyframes shrink {
from { height: 100vh; }
to { height: 10vh; }
}
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: body-tl;
animation-timeline-offsets: 0 90vh; /* 👈 */
}
This approach keeps the ability to define 1 ScrollTimeline
on a an element and also to target parts of it when linking an animation. However, authors would need to attach two animations/keyframes when targeting various offsets.
#coverheader {
animation-name: shrink, otheranimation; /* 👈 */
animation-timeline: body-tl, body-tl; /* 👈 */
animation-timeline-offsets: 0 90vh, 90vh 100%; /* 👈 */
}
One of the key things that ViewTimeline
introduced, was the ability for authors to define 1 set of keyframes which can target all different phases if needed. This approach with animation-timeline-offsets
does not offer this.
Something else?
This feels very similar to issue #7296 though I like some of the suggestions to handle this in a way similar to the way enter/exit phases are handled for ViewTimeline (See #7044). Following that thread, I think rather than offsets we could use whatever we decide the new shorthand for specifying start and end delay to set the range of the animation, e.g.
#coverheader {
animation-delay: 0 90vh, 90vh 100%; /* Same as setting startDelay to 0 and 90vh respectively and endDelay to 90vh and 0 respectively. */
}
Yes, since we already decided, at least for now, that these are properties of the animation, and not of the timeline. So these should go, as @flackr said above, on the animation
property as startDelay
and duration
/endDelay
.
Though this still makes @bramus's no. 2 suggestion valid, at least conceptually, with regard to the @keyframes
extension.
The extra added phase would leave the path for future additions open. Say a HoverTimeline
were to be added, then it’s only a matter of adding a hover
phase.
When no explicit phase is defined in the keyframes, the phase could be implied from the usage: adding non-phased keyframes to a ScrollTimeline
? Use those “regular” keyframes. Adding keyframes that have a scroll
phase to a ScrollTimeline
? Use those scroll
keyframes, ignoring the non-phased keyframes.
Some authors who have been playing around with the polyfill have raised similar issues, asking about offsets no longer being available.
The recent Motion One scroll library that enables JS-based Scroll-Linked Animations also offers this ability.
As @flackr suggested above, it could be achieved by having animation-delay-start
and animation-delay-end
accept lengths as their value.
animation-delay-start = [ <time> | <timeline-range-name> <percentage> | <length> ]
animation-delay-end = [ <time> | <timeline-range-name> <percentage> | <length> ]
FWIW I was thinking allowing lengths or percentages for ranges would allow greater flexibility:
animation-delay: [ <time> | <timeline-range-name>? [<percentage> | <length>]]+
Then you could use calculations within ranges, e.g.
/* Starts 10px after entering until 10px before done entering. */
animation-delay: enter 10px enter calc(100% - 10px);
Oh yes, great tweak! I like it!
@fantasai Could we get input from you on this before the spec goes to FPWD? Would love to get this back in there before the spec moves forward.
FWIW I was thinking allowing lengths or percentages for ranges would allow greater flexibility:
Then you could use calculations within ranges, e.g.
I like the idea of adding length-percentages to animation-duration
and animation-delay
, particularly since viewport units could then be used.
A few questions:
- When a length delay or duration is applied to a time-based animation, what does it resolve to?
- When a time delay is applied to a length-based animation, what happens? Do we create lag in the animation or ignore it or...?
I don't think this use-case is relevant to consider. As I suggested in #7944, time for delay and duration only make sense for time-based animations and length makes no sense there. And vice versa, if the animation has a timeline which isn't time-based, duration is irrelevant (since it's always 100%), and time for delay makes no sense. So we could ignore delay in this case and only parse the ranges (or use default values for ranges).
If an author wants to include both in @keyframes
to be used for either case they could write it as follows:
@keyframes fade-in {
0%, enter 20% {
opacity: 0;
}
100%, enter 100% {
opacity: 1;
}
}
I don't think this use-case is relevant to consider.
It doesn't matter if it's a use case or not, the behavior needs to be defined.
It doesn't matter if it's a use case or not, the behavior needs to be defined.
Yes, what I mean is that IMO these cases, where length is applied to time-based animations and time applied to length-based animations, should be treated as invalid and ignored.
animation-tlmeline
resolved to auto
, then it should only accept time values as it does now, and ignore ranges.This should also remove ambiguity from animation-delay
and resolve the confusion with animation-range
which currently only serves as an alias.
@ydaniv We can't make property parsing conditional on the value of another property. :) So we have to do something different, either zeroing out the mistyped values or treating the properties as having their initial value or something.
To summarize, I think we've got the following proposals up for debate:
<length-percentage>
to animation-delay
and animation-duration
@birtles Interested in your take on this discussion!
To summarize, I think we've got the following proposals up for debate:
Add to
animation-delay
andanimation-duration
Length delays resolve to 0s when applied to time-based animations and vice versa. (Percentage components are maintained intact.)
Length-based durations are ignored (treated as initial value) when applied to time-based animations, and vice versa.
Offset values are not clamped within their declared range.
@birtles Interested in your take on this discussion!
Thanks for including me and sorry for the delay (I was away last week and forgot to update my GitHub status).
I'm afraid I haven't been following the scroll animation discussion very closely so my input is probably not particularly valuable. Two very high-level notes, however:
An Animation
is intended to be the vehicle that maps a static effect onto a timeline so extending the animation as in option 3 from the original list of options would be the most natural fit from an architectural point of view.
Because animation-delay
maps onto an effect it feels a little misaligned to me (i.e. the effect was supposed to be a "dumb", static, stateless thing agnostic of how it is used), but see the next point.
When we started out with scroll-linked animations, we were under the assumption that it would be desirable to dynamically switch between a time-based timeline and a scroll-based one (e.g. when the scroll passed some threshold, the author would use JS to swap out the timeline to a time-based one, letting the animation run to completion).
Introducing different units to animation-delay
and animation-duration
such that some units are ignored depending on the type of timeline doesn't seem to be compatible with that assumption (or with the notion that effects are independent of how they are used), but maybe we've determined it's no longer an important use case?
@birtles:
i.e. the effect was supposed to be a "dumb", static, stateless thing agnostic of how it is used
I strongly agree with this, but, I suppose, since we've already agreed we'll allow keyframe's offsets to be ranges etc. So this is no longer the case.
Though I like the ergonomics of using animation-delay
/animation-duration
for offsets, it may be a bit off using it for offsets since these offsets relate to the ScrollTimeline.source
and not to the animation's target, so I wonder whether this will be a source of confusion?
Thanks @ydaniv.
i.e. the effect was supposed to be a "dumb", static, stateless thing agnostic of how it is used
I strongly agree with this, but, I suppose, since we've already agreed we'll allow keyframe's offsets to be ranges etc. So this is no longer the case.
Good point. I guess that ship has sailed.
It would be nice to do some wholistic thinking about how all these units could combine harmoniously in the context of group effects where we may wish to use percentage values / ranges even when using a regular timeline.
I've outlined the situation a little bit here and here but unfortunately no-one, myself included, has ever had time to look into it properly.
I think it would be really fun to analyze these pieces in terms of layout terms. If you can picture the different intervals being laid out horizontally as boxes:
position: relative
, hence why percentage keyframe offsets resolve against the effect's iteration duration.overflow
property somewhat.scaleX()
transform.flex-direction: column
.I think once you look at it in those terms, we can think of natural ways to have percentages resolve (e.g. if a group effect specifies a duration, it behaves like position: relative
and percentages delays on children resolve against its iteration duration, otherwise they resolve against the next ancestor all the way up to the abspos animation and its iteration duration, if any).
I'm not really sure what the equivalent for a timeline would be though?
My take is that some units, like percentages, can be used across multiple domains (times and lengths) whereas others (e.g. times and lengths) only work in their domains. We've seen lots of developer demand both for time based (see #4907) and scroll / length based animations (this issue and others) wanting to not have to use the abstract progress types (percentages) especially in cases where the percentage is not static. My take is that we should allow developers to use the unit type that makes the most sense to them in the context, even if it means that animation effect can't be used on a different type of timeline.
I don't think any of this precludes us making effects that work harmoniously within an animation group. E.g. percentages can still resolve against the ancestor's duration as you've suggested even if that ancestor has a time or length based duration. If you make the same analogy to boxes the fact that we have other ways to specify lengths would be similar to using px
or cqh
to refer to absolute durations outside of whatever the parent effect is.
Brian's comments in https://github.com/w3c/csswg-drafts/issues/7575#issuecomment-1320748822 remind me that there was some discussion about building in some drag into when a scroll animation triggers, I think this was raised by @smfr? In which case, do we want a time-based animation-delay
to control that delay (rather than zeroing out)?
Brian's comments in #7575 (comment) remind me that there was some discussion about building in some drag into when a scroll animation triggers, I think this was raised by @smfr? In which case, do we want a time-based
animation-delay
to control that delay (rather than zeroing out)?
This is tracked in #7059. I proposed there the full set of transition timing functions to allow for developers to fully customize the easing. Just using animation-delay
as a time delay suggests to me (following the way animation-delay works for web-animations) no easing but simply delaying the update of the effect.
The CSS Working Group just discussed [scroll-animations-1] Bring back Scroll Offsets
.
The CSS Working Group just discussed [scroll-animations-1] Bring back Scroll Offsets
, and agreed to the following:
RESOLVED: Add length % to animation-range to be used as offsets
A question regarding the resolution above, does it mean that the value of animation-range
can be :
<animation-range-name> <length-percentage>
?<animation-range-name> <percentage>
OR <length-percentage>
? If it's 1 then we have an ambiguity issue here.
FYI @fantasai @flackr
I was under the impression we decided that <length-percentage>
can be added to a <animation-range-name>
(for use with ViewTimeline), but that it could also be use without a <animation-range-name>
(for use with ScrollTimeline). So yeah, needs some clarification to see what exactly we resolved on.
@bramus unless I'm missing something, but from what I can see range names have no use with ScrollTimeline. ScrollTimeline should be using simple <length-percentage>
for duration and/or offsets, which we resolved on using ranges instead of delays - since delays have time values.
My original request for this issue was to be able to run a scroll-driven animation over a certain distance (in case of the header example: as you scroll the page from 0 to 90vh). The way I interpret the resolution, is that it would now be possible to do this as follows:
@keyframes shrink {
from { height: 100vh; }
to { height: 10vh; }
}
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: scroll(); /* 👈 Will find the root scroller */
animation-range: 0 90vh; /* 👈 Animation will run when scrolling from 0vh to 90vh */
}
If that’s incorrect, then the resolution from https://github.com/w3c/csswg-drafts/issues/8298#issuecomment-1412484155 might offer a solution, as it calculates the timeline ranges differently now. IUC correctly, the combined outcome would allow this:
@keyframes shrink {
from { height: 100vh; }
to { height: 10vh; }
}
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: view(self); /* 👈 Track myself */
animation-range: exit 0 exit 90vh; /* 👈 This works because of #8298 */
}
Maybe @flackr or @fantasai can clarify?
If the header is stuck at top: 0
then the way I see it is:
In the first example:
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: scroll();
animation-range: 0 90vh;
}
Will start the animation at scrollY=0
and continue until scrollY=90vh
.
In the second example:
#coverheader {
position: sticky;
top: 0;
animation-name: shrink;
animation-timeline: view(self);
animation-range: exit 0 exit 90vh;
}
Regarding the range start, animation won't start running while the header is stuck, because it's still in contain
range.
Only when it starts exiting, it starts shrinking.
As for the range end, I'm not sure we have a clear definition of what exit 90vh
means? Is that the same as exit 0% + 90vh
? - 90vh
?
I think we need to resolve on #8054 to answer that.
@bramus you are correct, the intent is to allow
As for the range end, I'm not sure we have a clear definition of what
exit 90vh
means? Is that the same asexit 0% + 90vh
?- 90vh
? I think we need to resolve on #8054 to answer that.
exit 90vh
is equivalent to exit calc(0% + 90vh)
.
@flackr great! But then I think we have an ambiguity here, see https://github.com/w3c/csswg-drafts/issues/7575#issuecomment-1416493493. Should I open a new issue for this?
Sorry perhaps I'm just a bit slow today but can you explain the ambiguity? What's an example? Happy to see a new issue for that if you don't mind filing it.
Fixed in https://github.com/w3c/csswg-drafts/commit/43b12e47663cff4cead0d187086dcf9ea8f33626 ; @bramus @flackr, let me know if I missed anything.
@fantasai I've added comments in the commit on specific lines.
@ydaniv Replied. OK to close the issue?
@fantasai yes, thanks!
One of the things that did not make it into the scroll-animations rewrite is the ability to animate as a scroller scrolls for a fixed distance. This used to be covered by the scroll-offsets.
There are some use-cases, such as this cover page that transforms to a header bar as you scroll over a distance of
90vh
.One could use use a
ViewTimeline
here, but not on the cover/header element itself because it is sticky and therefore neverexit
s. Instead, one would have to look at the succeeding element.This, however, is only possible if all of these apply:
enter
The first 2 conditions might not always be the case, but the author could work around that if they rearrange some things in their markup. The 3rd condition is not possible, since we decided in https://github.com/w3c/csswg-drafts/issues/7047 to have “timeline search look at preceding siblings and ancestors”.
Therefore I think we should reintroduce the Scroll Offsets.