Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web-animations-1][scroll-animations-1] Animations on inactive timelines should still become ready #9256

Open
flackr opened this issue Aug 28, 2023 · 6 comments

Comments

@flackr
Copy link
Contributor

flackr commented Aug 28, 2023

web-animations-1 section 4.5.6 says

An animation is ready at the first moment where both of the following conditions are true:

Per scroll-animations-1 section 2.1, there are conditions during which a scroll or view timeline can be inactive (e.g. the scroller is not currently scrollable) but may become active as a result of a user action, e.g. resizing the browser window such that the scroller is visible.

As a result of the second condition above, an animation using an inactive timeline is not considered to be "ready" until the scroll range is available. E.g. if you load the following demo the animation becomes ready once the viewport is smaller than the black box: https://jsbin.com/xezuluc/edit?js,output

This seems counter-intuitive to me. As a developer I would expect animations to become ready once the setup was complete, but not to be indefinitely delayed. E.g. a start delay on a time-based animation does not delay the ready promise. As it is currently implemented, it's not safe to wait on animation ready promises even though the effect is as ready as it can be on the next rendered frame. I think a developer would expect the animation ready promise to resolve once the animation has entered its current stable state.

@birtles, do you know why we have this condition? Would it be reasonable to remove it? I'm not sure which particular cases it is trying to cover.

@birtles
Copy link
Contributor

birtles commented Aug 29, 2023

As it is currently implemented, it's not safe to wait on animation ready promises even though the effect is as ready as it can be on the next rendered frame.

I didn't understand this part. What's not safe about waiting on the ready promise?

@birtles, do you know why we have this condition? Would it be reasonable to remove it? I'm not sure which particular cases it is trying to cover.

You mean the second condition?

I don't recall (it would take a bit of digging to find out since the spec has moved repositories so many times) but I think it comes down to the fact that calling play() is all about resolving the start time. Without an active timeline, you can't resolve the start time.

(Similarly, if we ever rebase SVG/SMIL animations on Web Animations, the SVG timeline does not become active until the load event fires for the nearest document fragment so the ready promise shouldn't resolve before that point because we don't know when the start time will be.)

4.5.8 Playing an animation has:

  1. Schedule a task to run as soon as animation is ready. The task shall perform the following steps:

    1. Assert that at least one of animation’s start time or hold time is resolved.

    2. Let ready time be the time value of the timeline associated with animation at the moment when animation became ready.

So if were to drop that condition, this procedure would no longer work since it assumes that when the animation is ready it has a timeline with a resolved time value (e.g. it attempts to calculate ready timehold time / playback rate).

If we update that procedure to allow an inactive timeline, we'd break the invariant that the start time is set when we're running and not pending.

@flackr
Copy link
Contributor Author

flackr commented Sep 8, 2023

As it is currently implemented, it's not safe to wait on animation ready promises even though the effect is as ready as it can be on the next rendered frame.

I didn't understand this part. What's not safe about waiting on the ready promise?

What I mean by "not safe" is that the code which is waiting for the animation to be "ready" may not run at all (if the observed scroller isn't scrollable) or not for quite some time, even though everything needed to run the animation has happened.

@birtles, do you know why we have this condition? Would it be reasonable to remove it? I'm not sure which particular cases it is trying to cover.

You mean the second condition?

I don't recall (it would take a bit of digging to find out since the spec has moved repositories so many times) but I think it comes down to the fact that calling play() is all about resolving the start time. Without an active timeline, you can't resolve the start time.

I guess I would expect the animation to become ready once any necessary setup was complete but not to require waiting for some user triggered state change. E.g. it doesn't seem mandatory to me that we know what the start time will be as long as it has been set up and can be expected to take effect in the frame at which it should be active.

(Similarly, if we ever rebase SVG/SMIL animations on Web Animations, the SVG timeline does not become active until the load event fires for the nearest document fragment so the ready promise shouldn't resolve before that point because we don't know when the start time will be.)

So at a high level this seems fine and safe in the meaning I meant above - as the document fragment will eventually load it's a reasonable thing to put some code behind that you expect to run once everything finishes loading, unlike how a scroll timeline may never become active until the user resizes the window.

I think this can be covered by the first condition for the ready promise:

  • the user agent has completed any setup required to begin the playback of the animation’s associated effect, including rendering the first frame of any keyframe effect.

As it couldn't possibly render the first frame until it loads, right? If it did, it would be an incorrect rendering to not have the animation effect.

4.5.8 Playing an animation has:

  1. Schedule a task to run as soon as animation is ready. The task shall perform the following steps:

    1. Assert that at least one of animation’s start time or hold time is resolved.
    2. Let ready time be the time value of the timeline associated with animation at the moment when animation became ready.

So if were to drop that condition, this procedure would no longer work since it assumes that when the animation is ready it has a timeline with a resolved time value (e.g. it attempts to calculate ready timehold time / playback rate).

If we update that procedure to allow an inactive timeline, we'd break the invariant that the start time is set when we're running and not pending.

I worry that this invariant may no longer be as strong as it should be. Scroll driven animations don't have a correct start time when the timeline becomes inactive. E.g. try out https://jsbin.com/fuzomoq/edit?css,js,console,output . The start time can't compute correctly until the view is scrollable but the animation state is running and not pending.

@kevers-google

@birtles
Copy link
Contributor

birtles commented Sep 9, 2023

It sounds like scroll-driven animations have some different invariants regarding the start time.

For time-based animations, I think we possibly need both conditions to explain the behavior in the following tests:

That is, simply waiting for setup to complete is not enough to describe why the second test doesn't animate but the third one does.

Is there a need for scroll-animations to be considered inactive if the scroller isn't scrollable? Could they be active but with a range of zero?

@kizu
Copy link
Member

kizu commented Sep 9, 2023

Disclaimer: I'm out of my league when talking about the JS Web Animations API (I've never really used it, only working with animations in CSS), so I'm not sure if my comment would bring anything to the conversation, but I'm a bit confused when looking at scroll-driven animations and thinking about terms like “play an animation”, "ready”, etc.

In my mental model for scroll-driven animations, they're just a mapping of the current scroll and position to the style derived for the keyframes, which can be updated arbitrarily, either on scroll or even when there is no scroll happening, as a lot of animations can be based on elements' positions in their scrollports, regardless of them being scrollable (either no overflow when overflow: auto, or when overflow: hidden) (for view timelines — I would be mostly talking about them; for scroll timelines things seem to be more in line, but maybe there are nuances).

And, because the styles are applied regardless of the scroll position, I tend to think that the animation is active due to us being able to “resolve” the “time” for such an animation (and it could change if the element's position or the container size changed).

(I can provide my Position-Driven Styles article as a potential source for use-cases of that aspect — static mapping of the scroll-driven animations for non-scrollable containers.)

So, when I look at the following excerpt from the spec:

If the 0% position and 100% position coincide (i.e. the denominator in the current time formula is zero), the timeline is inactive.

in my mind the “inactive” would happen only when the range is such that its length is zero, thus 0% and 100% positions coinciding, but this is unrelated to if the container is scrollable, as the element's position, its size and the container's size can impact this for a view timeline.

@flackr
Copy link
Contributor Author

flackr commented Sep 9, 2023

Is there a need for scroll-animations to be considered inactive if the scroller isn't scrollable? Could they be active but with a range of zero?

I like this train of thought. We went with making them inactive because it had the visual side effect we wanted - the animation does not produce an effect - but it doesn't match the implied meaning of an inactive timeline not being "ready".

I think it would be better for scroll timelines to be considered active but by some means produce no effect when the range is zero. This could be by having a null currentTime, which normally is only produced by inactive timelines.

@flackr
Copy link
Contributor Author

flackr commented Sep 11, 2023

in my mind the “inactive” would happen only when the range is such that its length is zero, thus 0% and 100% positions coinciding

This is roughly the current behavior. The issue is that it's unexpected that the animation would not be "ready" until the scroller becomes scrollable. E.g. when @kevers-google and @andruud were landing web tests they had to be very careful not to await the ready promise of a timeline on a zero range even though it feels like animations should eventually become ready even if the current state of the animation is inactive.

From a developer point of view you could imagine a page sets up a parallax animation (and a bunch of other async tasks) and waits for all of the promises to resolve but on a large screen that animation never becomes ready preventing some other intialization logic from running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants