w3c / csswg-drafts

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

[css-animations-2] Proposal: Time-based Keyframe Animations #4907

Open shshaw opened 4 years ago

shshaw commented 4 years ago

@keyframes are a great way to define animations, but the percentage-based declaration can be difficult to adapt to. Users familiar with GUI based animation programs or recreating existing animations in CSS can struggle with the conversion from absolute time to a percent.

Since we already have a <time> data type in CSS, could @keyframes be extended to support time based waypoints? This fits well within a standard animation timeline mental model which should make it easier for people to compose animations.

.box {
  animation: time-based-keyframes;
}
@keyframes time-based-keyframes {
  /* 0s 'frame' would be optional, same as `from` */
  0s { transform: rotate(0deg); }
  1s { transform: rotate(45deg); }
  2s { transform: rotate(45deg) translateY(100px); }
  /* Longest time duration = last 'frame', add pauses/delays with ranges */
  3s, 4s { transform: rotate(45deg) translateY(100px); }
}

This may not fit within the existing @keyframes model, but that would be preferable versus have a different model that could cause conflicts in the namespace.

Duration

animation-duration: auto would use the longest time declaration in the @keyframes. This could become the default value of animation-duration and would evaluate to 0s for standard percentage based @keyframes.

Scaling

The keyframe duration could be overridden by setting an exact duration, animation-duration: 2s, which would speed up or slow down the time-based @keyframes to match that duration. Much like in a video editor where you have a clip that's 10 seconds long, but you can speed it up so the duration of that video is only 6s or stretched out to 20 seconds. Doesn't change the original clip's 'length', but does change how the clip plays in the timeline.

Additionally, percent values in animation-duration could allow relative scaling of the keyframe defined duration.

/* Animation `@keyframes fade-out { 1s { opacity: 0 } }`*/
animation-duration: 200%; /* = 2s */
animation-duration: 50%; /* = 0.5s */

Having a standalone property for this like animation-timescale or animation-playback-rate would be useful, though expanding animation-duration for this "scaling" seems preferable.

Self-Contained Animations

Having time-based keyframes would make self-contained (“composed”) animations much easier. If the default duration becomes auto, then adding a composed animation to an element could be as simple as animation: my-keyframes where the duration, timing-functions and animated properties could all be declared within the keyframes.

.box { animation: scale-up; }

@keyframes scale-up {
  0s { animation-timing-function: cubic-bezier(0.5, 0, 0.5, 1); }
  1s { transform: scale(2); }
}

This containment would make animation design systems, exporting from GUIs, and CSS animation libraries easier to implement across a codebase.

WAAPI

Though outside of the CSSWG, it's worth considering how this could also improve WAAPI animations:

document.querySelector(".box").animate([
    { offset: "0s", opacity: 1 },
    { offset: "1s", opacity: 0 }
  ]); /* No duration needed */

"Polyfills"

Processors like Sass and PostCSS could implement this functionality by converting times to percentages given a duration ( @mirisuzanne was kind enough to create a Sass based mockup!
https://codepen.io/mirisuzanne/pen/jOPQdWw ) but native support would make CSS animation a lot more accessible to folks.

Conclusion

There are certainly opportunities to expand on this (relative times, delays, etc.), however the current scope seems like a realistic and reasonable addition which would make CSS animations easier to work with for users and tools.

CSS Animation 2 Spec for reference: https://drafts.csswg.org/css-animations-2

birtles commented 4 years ago

This sounds good. Note that #4862 is also suggesting adding an auto duration for scroll timelines and we have wanted that for groups too. In fact, the (very very draft) Web Animations level 2 spec introduces the intrinsic iteration duration concept and auto keyword for this.

Note that Web Animations (WAAPI) is a product of the CSSWG so it's fine to include that in scope. In fact, I think the best path forward here would be to work on the intrinsic iteration duration concept and scaling behavior such that it covers this case, the group case, and scroll timeline case.

For groups, in particular, I think the concept would also allow specifying child keyframe effects without any duration (only keyframe offsets), and then using the duration specified on an ancestor to calculate their final duration.

That is, similar to layout calculations, child nodes would pass up their resolved intrinsic durations, and bubble them up to the root, and then in a subsequent top-down traversal, we'd fill in the unresolved times based on the available time as well as scaling resolved times.

(In the very early days of planning Web Animations we had a number of proposals like this that we later realized were simply "flexbox, but for time".)

@flackr @majido Does #4890 harmonize with this in a way that covers groups too?

nickLeidman commented 4 years ago

I think it is a great idea, animations based on percentages always felt a little unwieldy. In the past I was destining complex CSS animations and i remember siting with notebook and calculator to figure out the percentages. Still have these long scripts somewhere.

But, not only this approach clarifies the timescale a little bit, for me adding something into a finished animation was a real pain, and this looks like a great solution for this as well! Basically, it looks like it comes down to just adding another keyframe at he end, no tedious recalculation of all percentages. Awesome, love that!

Another use case i see is for larger projects, with many animations. may be animation libraries, even. With custom properties controlling speed of animations becomes really just a "one line" solution. I imagine it to be something like that:

:root {
  --animationDuration: 100%;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --animationDuration: 50%;
  }
}

.animatedBlock {
  ...
  animation-name: myFancyAnimation;
  animation-duration: var(--animationDuration);
}

As I know in mobile operating systems (Android, for example) there is a control for animation speed. This proposal makes it easier to implement something like that in web aps.

birtles commented 1 year ago

@fantasai I notice you added Agenda + to this. Was there anything in particular you wanted to discuss?

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed [css-animations-2] Proposal: Time-based Keyframe Animations, and agreed to the following:

The full IRC log of that discussion <TabAtkins> astearns: Anyone want to propose something for this, or just continue discussing in an issue?
<TabAtkins> fantasai: If we want to do, we can draft. If we'r enot sure, we can figure out the scope
<TabAtkins> miriam: I've heard lots of requests for this
<fantasai> TabAtkins: prsumably would work as we just discussed
<emeyer> TabAtkins: I presume it work in a way similar to what we just discussed
<TabAtkins> fantasai: Right, I think we have to think about these two together.
<TabAtkins> fantasai: So if we want to do this we can resolve it and figure out the issues together
<TabAtkins> miriam: is 100% the final time?
<TabAtkins> fantasai: That question is also relevant for the previous issue.
<TabAtkins> TabAtkins: Like is it duration, or duration+delay?
<emeyer> TabAtkins: Is it the duration, or is it the duration plus the dleay? Is that the question?
<flackr> q+
<TabAtkins> miriam: Or final keyframe
<astearns> ack astearns
<astearns> ack fantasai
<astearns> ack flackr
<TabAtkins> flackr: I think this is similar to range-based keyframes
<TabAtkins> flackr: Where they're converted to %s of the animation
<TabAtkins> flackr: The precedent we set is they don't set the range of the animation itself, they can go before beginning and after end
<TabAtkins> flackr: I can see if you use duration:auto if picks up the greatest duration specified in keyframes
<emeyer> TabAtkins: That would make sense if we ever do things like spring-timing functions
<TabAtkins> flackr: Yeah
<TabAtkins> flackr: But otherwise the default model should be the span is the animation duration, time values before/after that are clipped
<TabAtkins> +1
<TabAtkins> fantasai: That's different from... well what happens when you iterate?
<TabAtkins> fantasai: [missed]
<TabAtkins> flackr: Think it's consistent with range-based keyframes
<TabAtkins> flackr: They convert as if you have one iteration, and you shorten
<TabAtkins> fantasai: We'd do that here?
<TabAtkins> flackr: Yeah, don't think you want subsequent iterations to be different form earlier ones
<TabAtkins> fantasai: Feel like something's not clicking but not sure.
<TabAtkins> fantasai: But if we want to see this we should resolve on it and draft it, and see how all these keyframe types work together to make sure they're consistent
<TabAtkins> astearns: Anyone think we shouldn't work on it?
<TabAtkins> astearns: So options are (1) continue to work on details in the issue, or (2) put a draft in Animations 2
<TabAtkins> astearns: Anyone prefer leaving it in issue?
<TabAtkins> astearns: Anyone object to starting work on this in Anim 2?
<TabAtkins> RESOLVED: Draft up proposal for time-based keyframe selectors in Anim 2
scottkellum commented 1 month ago

+1 thank you for this issue and so carefully thinking it out. I created a Sass mixin to help me alleviate some of the issues in the thread but it would be very nice if this were adopted in some form.