-
Notifications
You must be signed in to change notification settings - Fork 657
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-easing-2] Complex easing/timing functions #229
Comments
There is some previous discussion of this topic, thread starting here: https://lists.w3.org/Archives/Public/public-fx/2015JulSep/0034.html |
Cubic beziers aren't a bad choice for timing functions. They're very easy to evaluate (just a few additions and multiplications). Also, most animation tools use some form of cubic spline to specify animation paths, which can be converted into cubic bezier easings with good fidelity. There are two problems with
The first issue can be addressed by, essentially, just adding more points to the cubic-bezier() syntax. I believe this has already been proposed in various forms. The second issue is pretty significant, though, and deserves some unpacking.
The extra dimension isn't useful and makes life harder for implementors and tool authors alike. Ideally, instead of having to specify a two-dimensional curve like With one dimension, instead of having to work backwards from (normalized) time to an implicit t, the normalized time could be used directly as the input without having to solve cube roots first. I guess if I were going to make a concrete proposal, I'd like to see syntax for specifying 1d bezier splines, in a format something like The first and last values are exactly like the control points in Between those end "control values", you could have zero or more (control value, time, control value) triples representing intermediate knots with their adjacent control "points". The knot times (the middle value of the triples) would have to be monotonically increasing, in the range (0, 1). As a concrete example, let's take the following complex easing:
Given the aforementioned
IMO this seems better for both export tooling and hand authoring. One thing this proposal doesn't address are use cases where you want easings/animations with sudden jumps in them. (i.e. not C0 continuous) Having multiple intermediate CSS keyframes may still be a good answer for animations with discontinuities (rather than trying to define timing functions with discontinuities), though in order to do that there would need to be a mechanism to specify different incoming and outgoing property values for a CSS keyframe. |
One of the things that bothers me about Using (1d) bezier splines for defining easing functions is far simpler than something like |
Thinking about it overnight, |
I'm also an idiot; those triples need to be four-tuples, there's an extra position parameter which I left out. So that example/proposal is wrong. |
Adding the missing position parameter to the two control values, the syntax would be something like
|
Having thought about this for a while and chatted with others, I think the minimum required features for representing complex easing functions boil down to:
Piecewise cubics are pretty much the common denominator for animation software, which either uses some form of piecewise cubic spline directly, or else curves which are reasonably easy to convert to piecewise cubics. The main exception are animation tools which are doing physical simulations. Even then, some simple physical systems can be directly represented. For example, piecewise cubics are more than enough to precisely represent physically-accurate "bouncing ball" easings, since ballistic trajectories are simple parabolic arcs. Oscillating spring (or pendulum) easings are sinusoidal and not exactly representable using simple polynomials, but you can still do a good job of approximating them by gluing multiple cubic segments together. If I don't know how often jump continuities are really needed in easing functions, but they're certainly required if you want to be able to define a single easing function to recreate existing complex |
It's probably worth observing that motion curves (as opposed to timing/easing curves) in animation software are a little bit of a different story. There you're more likely to find NURBS or other more sophisticated types of curves which require a bit more work to approximate with cubic segments. On the 1d versus 2d issue -- From what I can see so far, After Effects uses one-dimensional piecewise functions for timing curves, although I don't have a copy to play with directly. However, poking at the implementation of Blender, it appears Blender f-curve segments do work similarly to |
Additional observation: while you can muddle through with multiple keyframes and
|
Yes, this is actually quite common to see. The Web Animations API allows you to overlap keyframe offsets so that you can add discontinuities and I believe there has been discussion in the past of adding syntax to allow you to do this in CSS keyframes (e.g. |
One suggestion made in the Slack discussion, which I had promised to document here, was to use an SVG path to specify an easing function. In that case, any SVG path could be allowed, at least provided it ranged between 0 and 1 in the x dimension, and the curve's x component was continuous and monotonic over that range as well. In that case, the M operator could used to indicate jump discontinuities. So, for two cubic segments joined by a jump discontinuity 50% of the way through, you might have something like: I have mixed feelings about this, but it would work, aside from the issue of needing to indicate whether you wanted left- or right- continuity at any jumps. |
The idea I had floated prior to that was something like: (In spite of i.e. two one-dimensional cubic bezier segments which meet at x = 0.5, but the first ends at y = 0.4, and the second begins at y = 0.8. Still doesn't address the issue of directional continuity though. It does also require you to repeat the y/output value even when you want the two segments to join with C0 continuity. For example if the first segment ended at y = 0.4, and the second began there:
This also wouldn't allow for a direct translation of blender f-curves, while I think SVG paths would. |
That concerns me. Also, I can't imagine how to write a script to export to this format from a motion graph in, say, After Effects. |
There's a meta issue here in that there's a bit of a mismatch between the way AE deals with animation and CSS to begin with. More specifically, animation-timing-function doesn't directly correspond to motion (v.s. timing) graphs in AE. It's more akin to the time remapping feature. That being said, if you extract the x, y, etc. components from the motion separately (like the AE "separate dimensions" feature does), then you could turn them into separate animations with timing functions in the format I gave. But then CSS would need to provide a nice way to combine multiple |
We have that in CSS Animations 2: animation-composition |
So if we ignore the composition and what-i-call-triggers-but-might-be-also-called-chaining, and assume spring() is handled separately (#280), and see @rachelnabors's recent tweet, can we start by adding some hardcoded shortcuts? The majority of those on easings.net are variations of a cubic-bezier. If these are really useful, and if other implementors agree, we can add keywords for them. Unless I've missed some, the functions on easings.net that are not supported by CSS are:
How popular are these? The first two are very much like spring(). Looking at tools... After Effects only seems to have a couple of built-ins, which it calls "easy ease". However, it allows you to manually create some pretty complicated curves. Apple's Motion does things in two different ways. It has "Behaviours" which are animation effects that you don't really see as keyframes and easing (more like "move in this direction with this speed and friction"). For the traditional keyframe animations, it has a manual editing mode like After Effects, but some shortcuts for bezier, linear, exponential, logarithmic and continuous. Cinema 4D has basic ease in/out/both, linear and steps. It also has some tooling for smoothing keyframes (e.g. smooth tangents) which would likely produce curves that we couldn't exactly match in CSS at the moment. Can people provide other examples? |
@visiblecode. While I understand the hesitance to create an easing path function similar to SVG paths, I think it would make easings like GSAP's RoughEase.ease easier to do for tool makers. http://greensock.com/ease-visualizer and click on Rough |
Hi, I know there are two different types of animation being discussed in this, predetermined animation curves and programmatic animations, like Apple's spring. I've been thinking about the latter and I think I have an idea. What if we could facilitate scripted animation for transition values. Here is the proposition I have arrived at.. Anyway, your css would look something like this .my-element {
/* transition: transform 500ms url('../simple.js'); */
transition: transform 500ms url('../bounce.js')(1 100 10 0);
transform: translate(0px, 0px);
}
.my-element.active {
transform: translate(200px, 200px);
} Simple example of an ease function file File: simple.js /**
* The simplest possible easing function, linear
*/
export init function(){
//in this case init does nothing
}
/**
* Function that gets called every frame until a done() callback / promise.resolve()
* @param {float} t - Transition current Time, value from 0 to 1
* @param {Promise} - Promise that gets resolved when animation is complete
* @return {float} A value 0 is not transitioned at all and 1 is fully transitioned
*/
export frame function(t, animationComplete){
if(t === 1){
animationComplete.resolve('done');
}
return t; // linear, very boring..
} Example of how something more complex, e.g. Apple's bounce transition effect could be defined using this method File: bounce.js /**
* Simulate a spring using the solving algorithm defined by this JavaScript
function
* @param {float} The mass of the object attached to the end of the spring. Must be greater
than 0. Defaults to 1.
* @param {integer} The spring stiffness coefficient. Must be greater than 0.
Defaults to 100.
* @param {integer} The initial velocity of the object attached to the spring.
Defaults to 0, which represents an unmoving object..
Defaults to 10.
* @param {float} initialVelocity
*/
export init function((mass, stiffness, damping, initialVelocity)){
// code that need to be run once during initialization
// -real code
}
/**
* Fuction that gets called every frame until a done() callback / promise.resolve()
* @param {float} t - Time, value from 0 to 1
* @param {promise} - Promise that gets resolved when animation is complete
* @return {float} A value from 0 to 1+ where 0 is not transitioned at all and 1 is fully transitioned, in the case of spring the value overshoots 1 initially then eventually settles on 1
*/
export frame function(t, animationComplete){
// -real code
// -real code
return result;
} The browser would know up front about the function and could, I assume be prepared, Different variations of the spring, for example could be achieved by passing different values into the init function, which could be done from the CSS, no need to touch the js. Obviously some code savvy people could share their functions with the community, and feasibly come up with some very clever stuff. And it would not need to go through the standards process, in the spirit of the Houdini project The frame function would at maximum be executed every frame, The browser itself could work out how much rounding would happen. |
Interesting perspective, @vidhill. Thanks for sharing. I am personally a bit hesitant to wait to see how Houdini's adoption goes and what hiccups will come down the line when we could nail down a spec today. But if we can't, you very well may have described the future. @grorg Thanks for joining the conversation! I'd love to see browsers offer more defaults than just |
It sounds good in theory, but I wonder if making a separate network call for each easing is a good idea. Even if you banked on HTTP2 to deliver it with the CSS file as an additional resource, it still would make CSS dependent on JS or a JS subset. |
@rachelnabors just to clarify this wouldn't be a proposal for something that would be necessarily be a part of the Houdini spec. I mentioned Houdini because I imagine that it'd this would be preferable for it to align with the style/spirit. @notoriousb1t I understand the concern, the first thing that comes to mind is the question, what if the user has js disabled!? It shouldn't be capable of doing anything 'nasty' |
There's a long-term plan to support script-defined timing functions but we're waiting on certain houdini components to materialize first. I think the current name of the piece we're missing is a worklet -- basically we want a bit of script that runs with very limited context and no side effects that we can run on either the main thread or compositor. |
@birtles I had a small chat about this on twitter with 'surma' and a few others, If this is already on the long term plans is the a point to doing this? -I'm new to the standards process, so genuinely don't know |
@vidhill I think you should start a new issue for script-generated animations. This issue is pretty specifically about expanding the scope of timing functions that can be specified in a declarative manner. You could make that issue on this repository or on the web-animations one although I suspect it might be easier to start with Web Animations (since it is the lower level spec and already has a script API) and then we can layer CSS syntax on top later. In that issue, code examples using Houdini worklets would be useful. |
Thanks, |
I personally prefer Jake Archibalds @jakearchibald easing-worklet proposal, it seems a little more straight forward, what do you guys think? |
I wonder if it'd be even simpler to provide multiple points and just linear interpolate between them. You can produce almost any effect with enough points. The more complex curves can land later, when folks figure them out. |
Krill Vastletov discussed something similar in this article https://www.kirillvasiltsov.com/writing/how-to-create-a-spring-animation-with-web-animation-api/, if so we may not need to even finalize the api and can just start with custom made point easing today. |
Yeah, you can hack it with keyframes today, but it'd be nice if it could be could be used within keyframes. Something like |
Agreed, having to calculate interpolated keyframe values can be complicated depending on property values being interpolated and is destructive (i.e. your animation's original keyframes are now lost). |
@birtles @grorg @smfr how do you feel about going forward with something like I imagine the definitions of It still leaves the door open for a curve-based solution like |
I'd be in favour of that. Particularly for a < 1s animation I imagine it wouldn't require too many points to produce a convincing effect. |
Here's a little demo to test that https://static-misc-3.glitch.me/linear-easing/. Cases like bounce are tricky due to sudden changes of direction, but 50 points seems to do well. |
I guess people would need to lean on SASS and LESS even harder to generate these series of points. It seems like something that could be evaluated would be better. Something like |
@benlesh I disagree for the reasons I gave above. Eg this as a library: :root {
--ease-bounce: linear-easing(0, 0.004, 0.01, 0.03, 0.06, 0.09, 0.13, 0.18, 0.24, 0.3, 0.37, 0.45, 0.54, 0.63, 0.7, 0.8, 1, 0.963, 0.91, 0.86, 0.83, 0.8, 0.77, 0.76, 0.751, 0.7508, 0.758, 0.77, 0.79, 0.82, 0.86, 0.91, 0.96, 0.99, 0.97, 0.95, 0.94, 0.938, 0.943, 0.96, 0.98, 1, 0.99, 0.984, 0.988, 1);
} …could be used like this: .whatever {
transition: transform 1s var(--ease-bounce);
} Even with Trying to add unnecessary complexity will stall this again. The more complex curve-based solutions can come later. |
As long as this solution isn't in lieu of pursuing a more flexible – if more complex – solution, I guess I don't see any problem with it. (Not that you needed my permission, haha. I just mean my concern above is invalid) |
@jakearchibald I definitely like the proposal. Have you given thought to a different syntax, closer to I know Amelia mentioned how folks with math backgrounds might prefer |
The issues with this are pretty well documented in this thread. Starting #229 (comment) |
I tried to start a PR for this, but I'm not entirely sure what I'm doing when it comes to CSS specs #6533. |
What about taking inspiration from video editors https://youtu.be/xM9w0SSpgtc?t=289? If it worked for them it would work for CSS as well. |
The door remains open for other easing syntax in future, but I don't think we should avoid shipping a solution for another 5 years in search of some holy grail solution. |
@jakearchibald Inspired by your demo I created a IMO, we should call the easing function either |
I've opened #7508 to look into curve-based custom easings. |
@jakearchibald reviving this old thread (thanks @argyleink for pointing me here) -- there's a question I have around Suppose you want a smooth animation at 60fps, a non-approximation would mean having 60 points inside linear. That is only if you want a one second animation. Factor in higher FPS displays and/or longer animations and you end up bloating the CSS pretty fast. Would it be possible to re-open this issue, in favor of discussing the "longer term" (#229 (comment)) solution that solves for interpolation / proper bezier curves? |
@tomasdev I don't think your assessment is correct. Check out this example. According to your maths, this would need 480 points to be smooth, but 76 here seems more than enough. For a 1.5s animation, 35 points seems more than enough. That said, as per my previous comment, I opened #7508 for exploring curves. |
In light of the Webkit team's implementation of
spring()
, it's apparent we need to attend to the issue of complex timing functions sooner rather than later.The problem
Designers often need more advanced timing functions than can be described with cubic-beziers. They are not limited to spring functions, either. A common problem is there is no effective way to export a timing graph from Adobe After Effects to a timing function that could be used with CSS or with the Web Animations API. Currently designers have to hack together individual timing functions using CSS animation keyframes, which is impossible to do by hand in all but the most trifling instances.
spring()
is just a bandaid.The solution
We need a format to write functions like
spring()
in, one that we can export to from software like AfterEffects and prototyping tools that have yet to be built.I am not in a position to propose the technical specifications of this solution. But there are people who have that knowledge. I have invited them (@visiblecode) to share their proposals below.
The text was updated successfully, but these errors were encountered: