w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.52k stars 674 forks source link

[scroll-animations-1] Allow named ranges to be used with math functions #8852

Open ydaniv opened 1 year ago

ydaniv commented 1 year ago

Currently the spec treats <timeline-range-name> as an <ident> and a named range is specified as <timeline-range-name> <length-percentage>.

A range can also be simply a <length-percentage> without a name for use with ScrollTimelines, and there's a suggestion for allowing the same for ViewTimelines, for attaching to the entire scroll range.

However, there are some use-cases, which are currently not addressed, more specifically: clamping a scroll range at the edges of the scroll container, so the effect, for example, will always start at 0% progress or end at 100% progress, regardless of layout.

Example could be an element that has the following animation:

@keyframes fadeIn {
  from {
    opacity: 0;
  }
}

.target {
  animation: fadeIn auto both view();
  animation-range: entry;
}

But when the page loads the target is already partially visible in the viewport. So it will present on load as semi-transparent.

One way to address these could be but can be done if we could use named ranges inside math functions, so authors could write it as:

@keyframes fadeIn {
  from {
    opacity: 0;
  }
}

.target {
  animation: fadeIn auto both view();
  animation-range: max(0%, entry 0%) entry 100%;
}

So questions are:

cc @fantasai @flackr @bramus

tabatkins commented 1 year ago

Unfortunately that's not currently possible. It would be a pretty decent syntax and processing change to allow arbitrary values like that. It's something we'd certainly like to do eventually, especially for things like this where the keyword converts directly into a numeric value and doesn't otherwise change the behavior of the property (unlike, say, width, where a bunch of things care whether the value is definite, content-based, or indefinite).

The best way to hack this in would be via a special function, valid in animation-range, that took a keyword+percentage pair and resolved it to a %. It would be a no-op when used on its own, but it would allow math functions to just treat it like a percentage, which is well-defined.

fantasai commented 1 year ago

@ydaniv I'm not sure I understand your use case. You want the element to be fully opaque as soon as it's visible, right? So why would it have a fade-in animation during entry?

ydaniv commented 1 year ago

Since we resolved on not clamping timeline ranges by default, I still want to allow it as opt-in. So my use case is an element with an entry animation that should play from 0% progress. Now, either that element is out of the first fold and a timeline from entry 0% can play from it's beginning, or on a specific screen it may load inside first fold and will appear as semi transparent. It's also important for authoring tools where effects are generic but a consistent experience is desired by the user.

I guess we can achieve this opt-in in other methods, but this one seemed the most simple one.

fantasai commented 1 year ago

@ydaniv so, for a fade-in animation, if the element is 10% on screen, you want the animation to be equivalent to entry 10% entry 100%, i.e. fully transparent until the user starts scrolling?

ydaniv commented 1 year ago

@fantasai correct. The idea is to allow author to opt-in to the UA respecting the full effect progress from 0% to 100% (same in end of scroll container), what we called before "clamping".

fantasai commented 1 year ago

@ydaniv Do you want the same effect if that image is 90% above the fold? Should it still be fully transparent at that point?

ydaniv commented 1 year ago

@fantasai of course at some point the range may become ridiculously short. In some cases it would make sense to disable that effect completely. Assuming this is a case that happens mostly in an authoring tool, then disabling the effect would be even preferred. But the main use-cases are on the edges, where an element can't start from 0% or end at 100%.

bramus commented 1 year ago

Another use-case would be this one right here:

image

The bottom right image reveals itself from cover 25% to cover 50% but is never fully revealed as we’re at the bottom of the page.

Setting the animation-range-end to the fictitious min(100%, cover 50%) would fix that, by shortening the animation scroll distance. Side-effect is that it runs “faster”.

The best way to hack this in would be via a special function, valid in animation-range, that took a keyword+percentage pair and resolved it to a %.

Would that be something like min(resolve(100%), resolve(cover 50%)), or more like min-range(100%, cover 50%)?

ydaniv commented 1 year ago

Thanks @bramus! Yep, that's the use-case for animations at end of scroller.

The best way to hack this in would be via a special function, valid in animation-range, that took a keyword+percentage pair and resolved it to a %.

Would that be something like min(resolve(100%), resolve(cover 50%)), or more like min-range(100%, cover 50%)?

If we use min() it's resolved as a <length-percentage> and then used as offset from cover's 0%. So perhaps the latter, that can be resolved to a "range attachment", or something that's neither a name+length or a length.

bramus commented 1 year ago

Just had another related request where someone wanted to run an animation from exit 100% + 30px to exit 100% - 30px. That way the animation would run during “the last 60px of exiting”.

Relying on percentages to run the animation – e.g. from exit 95% to exit 100% – wasn’t an option, as some of the elements are much more taller then others, resulting in values from as low as 40px distance up to 180px.

Right now they are working around it by injecting a sentinel div of 60px height at the bottom of the element, which they are then tracking.

ydaniv commented 1 year ago

@bramus they should be able to specify exit calc(100% - 30px) exit calc(100% + 30px), though not calc(exit 100% + 30px) as this issue suggests

bramus commented 1 year ago

@bramus they should be able to specify exit calc(100% - 30px) exit calc(100% + 30px), though not calc(exit 100% + 30px) as this issue suggests

Oh wow! Didn’t know that worked. Works like charm (demo), thanks!

ydaniv commented 1 year ago

GSAP just added this via new clamp() function: https://twitter.com/greensock/status/1674434131021926400

flackr commented 1 year ago

This use case also requires that we have a way to specify the non-subject range for a view timeline as currently we have defined that a bare value refers to the cover range. e.g. maybe we add it a new scroll range name.

flackr commented 3 weeks ago

Adding a scroll range name was resolved in #9367.

I propose that we move this to scroll-animations-2, lacking a particular way this could work right now.