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

[css-animations-2] Synchronised animation timelines #8534

Open
bramus opened this issue Mar 6, 2023 · 10 comments
Open

[css-animations-2] Synchronised animation timelines #8534

bramus opened this issue Mar 6, 2023 · 10 comments

Comments

@bramus
Copy link
Contributor

bramus commented Mar 6, 2023

When adding elements to a page, the applied animations (when not paused) start ticking forwards from the moment the elements are added. In some cases this is not wanted and authors want to keep (repeating) animations synchronized: independent of when a new elements was added, its animations need to be in sync with the other elements that use those animations.

The author of the web-based https://athenacrisis.com/ game for example mentioned this as one of the things they had to work around while building their game that uses CSS animations. As you can see in the demo video on their homepage, the game characters hover/bounce in sync with each other.

To do this today, an author currently has to resort to JavaScript:

  1. Add the elements to the DOM
  2. Loop the animations and alter them one by one by either
    • Altering the effect’s delay (demo)
    • Overriding the startTime (demo)

(Step 2 needs to be reapplied as more animation effects get added)

I think there should be an easier way to do this. I was thinking of introducing a new type of timeline to solve this: the synchronised timeline. When applied, the running animation still ticks forward on the document timeline, but its start is synced to multiples of its duration or – even easier – to simply 0.

For example, say a repeating animation has a duration of 6s and the element gets added at 21.15s into the page’s lifespan. Upon adding that element, the startTime for its animations is set to the preceding multiple of its duration (here 18s). That way, it’s animation is already at the 3.15s mark at first render, making it visually sync up with possible other elements that use the synchronised timeline for its animations. The animation delay would also need to be taken into account in this.

A proposed notation for this type of timeline would be a new function sync() or maybe document(sync) to indicate its relation to the default document timeline.

@birtles
Copy link
Contributor

birtles commented Mar 7, 2023

I think this would be better addressed using group effects. Adding coupling between timelines and effects seems awkward architecturally.

@bramus
Copy link
Contributor Author

bramus commented Mar 7, 2023

# I think this would be better addressed using group effects.

IUC group effects is more about grouping effects on one and the same element? How would that work across several elements?

# Adding coupling between timelines and effects seems awkward architecturally.

What I’m aiming at here is to visually sync animations across several elements. With the idea I had there’s no actual coupling between any of the timelines. Effects would still run independent from each other on the document timeline, yet appear to be synced because they all have their start time fixated to 0 (or a multiple of their duration). This approach could then also be used to align group effects across elements.

I’m very aware I could be oversimplifying things here, Brian; happy to learn what the group effects approach would (roughly) look like.

@birtles
Copy link
Contributor

birtles commented Mar 7, 2023

IUC group effects is more about grouping effects on one and the same element? How would that work across several elements?

Keyframe effects specify their target element so group effects (which combine keyframe effects) are able to target multiple elements.

For this particular use case, you'd specify the (infinite) iteration count on the group and then attach child effects to it. Any effects added to the group while it was in play would pick up from the group's current time and so would be synchronized with existing effects.

@fantasai
Copy link
Collaborator

fantasai commented Jun 2, 2023

This seems related to #6780 also...

@fantasai
Copy link
Collaborator

fantasai commented Jun 2, 2023

@birtles I think one downside to using WA2 is... how do you do this declaratively?

@birtles
Copy link
Contributor

birtles commented Jun 2, 2023

@birtles I think one downside to using WA2 is... how do you do this declaratively?

We need to devise the declarative form of timing groups. I wonder if a similar syntax to counters could work.

@fantasai
Copy link
Collaborator

fantasai commented Jun 6, 2023

For this issue specifically, what about if any name value for animation-timeline that wasn't bound to a declared timeline, instead of being an inactive timeline, was an automatically-generated time-based timeline? The first element to use it would create the timeline (and it would behave just like auto does today), but all other animations that request the same timeline get bound to that same timeline. That wouldn't give you control over when exactly it starts, but it would allow you to bind a set of animations across the document all to the same timeline.

@bramus
Copy link
Contributor Author

bramus commented Jun 12, 2023

One scenario where I recently needed this was when swapping out one animation for the other, but have the second animation continue where the first one was cut.

el {
  animation: one 5s linear infinite;
  animation-timeline: --sync;
}
el:hover {
  animation: two 5s linear infinite;
  animation-timeline: --sync;
}

@fantasai: I don’t think that with your suggestion I would be able to refer to the timeline of one to use for two, as the former no longer exists upon hovering? Or would it be kept alive in between?

@ydaniv
Copy link
Contributor

ydaniv commented Oct 8, 2023

I agree with @birtles, this can and should be solved by group effects.
Although the term "timeline" was used for this purpose in some libraries, e.g. GSAP, it's not the correct construct here. So this might be a source of confusion.

@bramus your example should be solved with group effects if these animations are added to same group, then they sync their progress with the group's.

For this issue specifically, what about if any name value for animation-timeline that wasn't bound to a declared timeline, instead of being an inactive timeline, was an automatically-generated time-based timeline?

We definitely need a declarative way to use group effects, though IMO using timelines here is not the correct way. We already have some mixup of animation matching actually to KeyframeEffects. So trying to continue this path for using *-timeline for groups will probably make things worse.

For example: we could specify group-timeline will be used for declaring a group effect with a name and timing properties, like:

group-timeline: --group-a 1s linear 2s;

and then elsewhere specify animation-timeline to use that name.

The main problem with this approach is that you can't play that group effect using a different timeline, like a ViewTimeline.


My proposed solution:

  1. Add a new property like animation-group that takes a list of idents to add an animation to a group, and possibly an integer for the order in a sequence.
  2. Add a new property, name to be bike-shedded, like group-effect, to declare a group effect that can be later used as above.

The group-effect can be just specified on a parent, which all animation-groups just seek upwards on the tree.
The group-effect property can be a shorthand of all properties: name, type, and rest of timing properties.
name can be a dashed/custom-ident.
type to specify group or sequence.

An example usage could be something along the lines of:

@keyframes slide { ... }
@keyframes blink { ... }

#container {
  group-effect-TBD: --gorup-1 sequence 2.5s 0.5s linear 2;
}

#target {
  animation:
    slide 1s ease-out both,
    blink 1.5s ease-in-out 1s infinite;
  animation-group: --group-1, --group-1;
}

Once the common parent with the group-effect is matched, that animation becomes active and starts its progress, and then the same goes for its children accordingly.

For further nesting we'll also need group-effect to be nest-able inside another group, so the syntax also needs to account for that, though I think we can't use animation-group again, since it's used with animation. I suppose adding another ident to its syntax should be the way to go.

@birtles
Copy link
Contributor

birtles commented Oct 9, 2023

@ydaniv Thanks for kicking off the discussion about CSS syntax for group effects. That certainly seems like a good start.

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

4 participants