w3c / csswg-drafts

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

[css-animations-2] there should be a way to set the effect-wide easing for a CSS Animation #6982

Open graouts opened 2 years ago

graouts commented 2 years ago

Currently, the animation-timing-function property set on an element will set the default value for animation-timing-function for each keyframe of a @keyframes applied to that element. This means that there is no way to set the easing for an animation effect, only on its keyframes, thus not allowing authors to specify a CSS Animation with multiple keyframes with a single easing being applied throughout.

This always bugged me and resurfaced while discussing https://github.com/w3c/csswg-drafts/pull/6974 with @birtles.

One idea @birtles suggested in this conversation would be to add a new animation-effect-timing-function property which would apply animation-wide. This is a good suggestion.

An alternative I could think of would be to use the term easing since this is the name of the property used in the Web Animations specification and also the term used in the CSS Easings specification. Thus, we could introduce a new animation-easing property.

That property could apply to keyframes as well, where the animation-easing property would override the value set on the element for this particular keyframe. And if the animation-easing is set on either the element or a keyframe, then the animation-timing-function values would be completely disregarded, therefore deprecating that property in favor of the new animation-easing which I think would be more user-friendly.

ydaniv commented 1 year ago

Perhaps we could accept 2 values of <easing-function> in the animation shorthand, and then the first one will always be the animation-timing-function, and the second one, if exists, will be for effect-wide easing function?

Another option, if we go with animation-easing, is to make it a shorthand itself for -keyframe and -effect and then a single value will map to -keyframe and animation-timing-function, and 2 values would map to both, like

animation-easing: linear ease;

Which would make it backwards compat with animation-timing-function, and then we could also completely replace it in the shorthand and deprecate -timing-function.

But this option might still be confusing when mapped to Web Animations.

ydaniv commented 10 months ago

ping @graouts @birtles @flackr @dbaron @bramus could we make this fit into agenda for TPAC? Anything we can agree on to progress this for agenda?

birtles commented 10 months ago

I'm not attending TPAC I'm afraid but I don't have particularly strong opinions on this.

I tend to prefer @graouts original proposal simply because having a shorthand that puts the keyframe effect first seems a little awkward. Most often authors will only want to set the effect-wide easing (more accurately, iteration-wide, but hey) so having to write linear ease is a bit cumbersome and error-prone. The backwards compatibility is great though.

birtles commented 10 months ago

In terms of things we agree on, I think everyone agrees we want a way to set effect-wide easing from CSS.

I think most of us would like, if possible, to transition towards using the "easing" terminology over the more verbose "timing function" terminology because that's the name of the member in the Web Animations API and the name of the spec that defines these easing functions.

ydaniv commented 9 months ago

Another way tackle this could be introducing a new separated property that controls the "mode" of the specified easing to control whether timing function in shorthands are used per keyframe or per iteration. Something along the line of (TBB):

* {
    animation-easing-mode: iteration; /* or `keyframe` */
}

And then, like in the above example, you can set it once as default for all and only override where/if needed otherwise. Also, this probably shouldn't replace the animation-easing proposal, just allow its introduction into shorthands. @birtles @graouts WDYT?

birtles commented 9 months ago

I think that would work but I still prefer @graouts' original proposal for its simplicity. Is there any particular concern with that proposal?

ydaniv commented 9 months ago

Is there any particular concern with that proposal?

There's no problem, I'm only trying to find a simple way forward to have that easing property work in animation shorthand seamlessly.

flackr commented 8 months ago

if the animation-easing is set on either the element or a keyframe, then the animation-timing-function values would be completely disregarded, therefore deprecating that property in favor of the new animation-easing which I think would be more user-friendly.

I think more detail is needed about what exactly is meant by "disregarded". I believe what you intend is that the default animation-timing-function becomes linear unless an alternative is specified in an animation-easing rule for a particular keyframe. However, we would only be able to do this if animation-easing was set to a non-initial value, and given that the default effect-wide easing is linear, linear would be a good choice, except that then the following would not actually result in a linear animation:

.target {
  animation: my-animation 1s;
  animation-easing: linear;
}

So I think to do this we would have to have an auto value which would compute equivalent to linear except that it wouldn't trigger ignoring animation-timing-function.

For a slightly different proposal, we could treat animation-easing as a set-to-linear shorthand for animation-timing-function. This would mean that having animation-easing listed would set animation-timing-function to linear, but it could be subsequently overridden if the developer wanted to give a different keyframe easing. This is similar to other reset-only shorthand properties.

E.g. an author could explicitly reset the animation-timing-function after animation-easing:

.target {
  animation: my-animation 1s; /* animation-timing-function is initially ease as always */
  animation-easing: steps(10); /* implicitly sets animation-timing-function to linear. */
  animation-timing-function: ease; /* explicitly sets animation-timing-function back to ease. */
}

One challenge about this entire space is that it will still be difficult to add the effect-wide easing to the animation shorthand since it would have to come second there anyways without some other syntax to specify that you want to set the effect-wide easing.

E.g.

.target {
  animation: my-animation 1s ease steps(10);
}
ydaniv commented 7 months ago

I think more detail is needed about what exactly is meant by "disregarded". I believe what you intend is that the default animation-timing-function becomes linear unless an alternative is specified in an animation-easing rule for a particular keyframe.

How would setting the iteration-wide easing inside a keyframe work? Would it reset the progress from that point? If not, what use-cases would that serve?

So I think to do this we would have to have an auto value which would compute equivalent to linear except that it wouldn't trigger ignoring animation-timing-function.

If we add another auto wouldn't that be a problem in the animation shorthand?

For a slightly different proposal, we could treat animation-easing as a set-to-linear shorthand for animation-timing-function.

I thought that was the initial proposal. You mean it would treat the linear value as "auto" until animation-easing is explicitly specified?

Another option we can consider for solving the shorthand problem is using a new "mode" property like I suggested above.

bramus commented 7 months ago

The proposed animation-easing-mode seems like the cleanest as it doesn’t have the problem of animation-easing and animation-timing-function fighting against each other.

One downside though, is that you’re then limited to either effect-timings or either keyframe-timings – you can’t have both. Not sure if that’s a limitation that would bother most people or not.

Another approach is to not auto-set animation-timing-function to linear when animation-easing is used, requiring authors to be explicit about both. Maybe a shorthand to set both in one go could also be created?

As for the shorthand: that’s a recurring problem which I don’t think we’ll be able to solve here.

flackr commented 7 months ago

How would setting the iteration-wide easing inside a keyframe work? Would it reset the progress from that point? If not, what use-cases would that serve?

It would not be a valid property inside a keyframe, like other animation affecting properties.

So I think to do this we would have to have an auto value which would compute equivalent to linear except that it wouldn't trigger ignoring animation-timing-function.

If we add another auto wouldn't that be a problem in the animation shorthand?

If we used auto, I'd propose it is only specified through omission.

For a slightly different proposal, we could treat animation-easing as a set-to-linear shorthand for animation-timing-function.

I thought that was the initial proposal. You mean it would treat the linear value as "auto" until animation-easing is explicitly specified?

I'm a bit confused here. If animation-easing is unspecified, then the animation easing will be linear (as it is today), and the animation-timing-function will have its default ease value unless another is specified by the developer. If the developer sets animation-easing: linear my thinking was that we would want to then replace the initial animation-timing-function: ease value with animation-timing-function: linear as well to respect the specified linear. This requires that the initial "linear" value and the specified "linear" value are handled differently.

Another option we can consider for solving the shorthand problem is using a new "mode" property like I suggested above.

I'm not a fan of changing the interpretation of the timing function, but accept this may be the direction we have to go here.

ydaniv commented 7 months ago

@bramus:

One downside though, is that you’re then limited to either effect-timings or either keyframe-timings – you can’t have both. Not sure if that’s a limitation that would bother most people or not.

That's a good point. I'm trying to think about use-cases but can't think of any.

I guess authors could as well do something like:

* {
  animation-timing-function: linear;
}

And then only work on animation-easing. That could leave the option to have both maybe?

@flackr:

How would setting the iteration-wide easing inside a keyframe work? Would it reset the progress from that point? If not, what use-cases would that serve?

It would not be a valid property inside a keyframe, like other animation affecting properties.

Yes, that was my thought.

If we used auto, I'd propose it is only specified through omission.

Right, that sounds good, still leaving us with the shorthand problem.

I'm a bit confused here. If animation-easing is unspecified, then the animation easing will be linear (as it is today), and the animation-timing-function will have its default ease value unless another is specified by the developer. If the developer sets animation-easing: linear my thinking was that we would want to then replace the initial animation-timing-function: ease value with animation-timing-function: linear as well to respect the specified linear. This requires that the initial "linear" value and the specified "linear" value are handled differently.

OK, I wasn't sure this is possible via an unspecifiable/ommitted auto, but I guess we're on the same page now.


To sum it up, I think we have an agreed way forward for reseting keyframe-wide to linear behavior. It's just the shorthand that's still an issue.

ydaniv commented 7 months ago

We could also go with a solution that allows setting only the iteration-wide function and then authors could still override in the @keyframe itself per keyframe

ydaniv commented 2 months ago

Reading through this again, I think I didn't understand @flackr's suggestion before, but I think I got it now 😅

So, what if we made animation-easing have a non-specifiable initial value of something like none, which computes to linear, but doesn't reset animation-timing-function. Then, when specifying animation-easing it resets animation-timing-function to linear, like suggested above. In most cases (almost all) authors wouldn't want to override animation-timing-function again, but they can, like above.

Also, like suggested above, allowing easing to also take 2 values, if needed, can also be used to set the keyframe-wide easing and then iteration-wide one, which can also later be used inside the animation shorthand.

So in short, I propose to accept @flackr's suggestion above, with the suggested none non-specifiable initial value.