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-easing-1] Some ideas for linear() easing #6533

Closed
wants to merge 5 commits into from

Conversation

jakearchibald
Copy link
Contributor

@jakearchibald jakearchibald commented Aug 20, 2021

This is an attempt to make some progress on custom easing functions, specifically #229 (comment)

I've never edited a CSS spec before, so I don't really know what I'm doing. Sorry if this isn't helpful in any way. Here's what I'm trying to do:

Edit: I've updated this following discussion below.

There's already a linear keyword. I figured we could overload that, so linear is a shorthand for linear().

The arguments are a comma-separated groups of:

  • Output (number)
  • Start input (optional, percentage)
  • End input (optional, percentage)

The values work very similar to linear-gradient.

linear(0, 0.7, 0.87, 0.98, 1) - this creates 5 points:

Screenshot 2021-08-23 at 13 11 21

Which is equivalent to linear(0 0%, 0.7 25%, 0.87 50%, 0.98 75%, 1 100%), as points without a 'start input' are given one automatically (using the same process as linear gradients).

The third value, again similar to gradients, can be used to create an extra point using the same output value. So linear(0, 0.5 25% 75%, 1) creates 4 points:

Screenshot 2021-08-23 at 08 52 21

Output values will typically be 0-1, but can be outside of that, as they can with cubic-bezier. A likely usage here will be elastic/spring easings.

Similarly, input values will typically be 0%-100%, but can extend beyond that to provide points for edge-cases where easings are chained.

The ends of the graph automatically extend beyond their specified point using their final angle, so the previous easing (linear(0, 0.5 25% 75%, 1)) would extend like this:

Screenshot 2021-08-23 at 09 00 32

However, if the final two points are in the same position, the line extends horizontally, so linear(0, 0.5 25% 75%, 1 100% 100%) (the difference being the 100% 100% at the end), would be:

Screenshot 2021-08-23 at 09 02 27

Some edge cases: linear() is equivalent to linear (as in, an identity function). If only one value is provided, then the output value is always that value, so linear(0.5) always outputs 0.5.

The overall goal is to allow something like:

.whatever {
  animation-timing-function: linear(0, 0.003, 0.013, 0.03, 0.05, 0.08, 0.11, 0.15, 0.2, 0.26, 0.31, 0.38, 0.45, 0.53, 0.62, 0.71, 0.81, 0.9, 0.99, 0.94, 0.89, 0.85, 0.82, 0.79, 0.77, 0.76, 0.752, 0.75, 0.755, 0.77, 0.78, 0.81, 0.84, 0.87, 0.92, 0.97, 0.99, 0.97, 0.95, 0.94, 0.938, 0.94, 0.949, 0.96, 0.99, 0.994, 0.986, 0.985, 0.989, 1 100% 100%);
}

…which would graph like this:

Screenshot 2021-08-23 at 11 45 53

And here's a demo of how that would animate: https://static-misc-3.glitch.me/linear-easing/

@tabatkins
Copy link
Member

Syntax nit: I'd probably use a % for the progress value. That accords better with things like gradients, and makes it much clearer which value is progress and which is the value:

linear(0 0%, 0.1 20%, 0.3 40%, 0.7 60%, 0.8 80%, 1 100%)

It also means we wouldn't necessarily have to impose an ordering, since the types are distinguishable.


I really don't like the different behavior between linear() and linear(0,1). If we just always made <0 and >1 extend using the first/last segment, you could achieve the clamping behavior with linear(0 calc(infinity * -1%), 0 0%, 1 100%, 1 calc(infinity * 1%)). We could probably add some syntax to make that easy and explicit instead, like linear(0 0% fill, 1 100% fill), where the fill keyword is only valid on the first/last value, and has the same approximate meaning as animation-fill-mode.


Otherwise, big 👍 on the proposal overall. I don't think the spec needs to be quite as algorithmic as written here, but the behavior is fine.

@okikio
Copy link

okikio commented Aug 21, 2021

I personally prefer the linear-easing(...) style, it seems easier to understand.

@birtles
Copy link
Contributor

birtles commented Aug 21, 2021

As I've said before, I really like this proposal. Thanks for pushing this forward.

I'm a bit dense so I had a bit of trouble following the description in the first comment however because there are many different terms:

  • "value" (A)
  • "progress position" (B)
  • "input progress value" (=A? =B?)
  • "position value" (=A? =B?)

I think I might be able to better understand the clamping issue if the terms were more consistent.

@okikio
Copy link

okikio commented Aug 21, 2021

Yeah, I agree with @birtles.

If the goal is to enable the creation of complex easings then I feel we should keep the key word linear-easing(....) or custom-easing(....), with linear-easing(0, 0.4, 0.6, ...) being the basic form and linear-easing(0 20%, 3 40%, 0.5 80%, 0.6, 0.7, 0.8, 1) being the advanced mode. I don't think we should complicate the linear-easing function with fillMode, as it would make the linear-easing function the only CSS Easing Function that currently does this and create potentially counter intuitive behaviors, I feel a separate proposal for fillMode would be better, so, for example, we could have something like this,

.div {
    animation-fill-mode: both 0%, forwards 50%, none 80%, backwards 90%;
}

As an experiment, I followed the technique @jakearchibald suggested and I was able to create spring easing using WAAPI, so, I think the proposal just in the basic form is plenty powerful and versatile, plus together with progress values as percentages, it would be even easier to understand what is happening. One thing to note is that linear-easing would be pretty difficult to follow if the points of said easing are all over the place, I think some amount of imposed order is beneficial.

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Aug 21, 2021

@tabatkins

Syntax nit: I'd probably use a % for the progress value. That accords better with things like gradients, and makes it much clearer which value is progress and which is the value:

linear(0 0%, 0.1 20%, 0.3 40%, 0.7 60%, 0.8 80%, 1 100%)

I had the same thought, but here's why I ended up with two numbers: Both values are the same type, that's how the simple identity version of linear works. So, they could both be %, or both be numbers. I went with numbers because that's what the spec already uses for cubic-bezier and all the diagrams.

I agree it looks better with different types, but I'm not sure it makes sense. If we go with one value being a number, and the other being a %, I guess we should update the diagrams in the spec too to reflect that.

It also means we wouldn't necessarily have to impose an ordering, since the types are distinguishable.

This gets really weird when trying to generate auto values for input position. I think it's why gradient syntax doesn't allow it (well, if one percent is less than the previous one, it makes it equal to the previous one). Being consistent with gradient syntax here seems better.

I really don't like the different behavior between linear() and linear(0,1). If we just always made <0 and >1 extend using the first/last segment, you could achieve the clamping behavior with linear(0 calc(infinity * -1%), 0 0%, 1 100%, 1 calc(infinity * 1%)). We could probably add some syntax to make that easy and explicit instead, like linear(0 0% fill, 1 100% fill), where the fill keyword is only valid on the first/last value, and has the same approximate meaning as animation-fill-mode.

We could borrow more from gradient syntax here:

.whatever {
  background: linear-gradient(to right, red 20%, orange 20% 40%, yellow 40% 60%, green 60% 80%, blue 80%);
}

Here, orange 20% 40% effectively creates two 'orange' points, one at 20% and one at 40%. We could do the same.

.whatever {
  /* No clamping */
  animation-timing-function: linear(0, 1);
  /* Clamping */
  animation-timing-function: linear(0 0 0, 1 1 1);
}

The latter creates 4 points, two where the input and output are 0, and two where the input and output are 1. Having two points means the values would 'extend' flatly. This makes it relatively easy to clamp just one side of the easing.

The syntax also allows for creating a 'rest' period during the easing:

.whatever {
  animation-timing-function: linear(0, 1 .25 .75, 0);
  /* equivalent to: */
  animation-timing-function: linear(0 0, 1 .25, 1 .75, 0 1);
}

I don't think the spec needs to be quite as algorithmic as written here

That's my lack of experience with CSS specs showing 😄

@jakearchibald
Copy link
Contributor Author

@birtles

  • "value" (A)
  • "progress position" (B)
  • "input progress value" (=A? =B?)
  • "position value" (=A? =B?)

It terms of these diagrams, a point's 'position' is the x-axis, and a point's 'value' is the y-axis.

Happy to rename these to whatever.

@jakearchibald
Copy link
Contributor Author

@okikio

If the goal is to enable the creation of complex easings then I feel we should keep the key word linear-easing(....) or custom-easing(....), with linear-easing(0, 0.4, 0.6, ...) being the basic form and linear-easing(0 20%, 3 40%, 0.5 80%, 0.6, 0.7, 0.8, 1) being the advanced mode

That isn't consistent with gradients. I'm trying to borrow as much as possible from existing concepts.

I don't think we should complicate the linear-easing function with fillMode, as it would make the linear-easing function the only CSS Easing Function that currently does this and create potentially counter intuitive behaviors, I feel a separate proposal for fillMode would be better,

Don't think of this being like fillmode as it exists in other parts of the animation spec, it's pretty different. Does the idea in #6533 (comment) make more sense?

@birtles
Copy link
Contributor

birtles commented Aug 21, 2021

@birtles

  • "value" (A)
  • "progress position" (B)
  • "input progress value" (=A? =B?)
  • "position value" (=A? =B?)

It terms of these diagrams, a point's 'position' is the x-axis, and a point's 'value' is the y-axis.

Happy to rename these to whatever.

Thanks! I'm just wondering which "input progress value" and "position value" are.

@jakearchibald
Copy link
Contributor Author

Input progress value is an existing term. "Position value" isn't in this proposal or the existing spec. Might be easier to make a comment against a particular line in the PR?

@birtles
Copy link
Contributor

birtles commented Aug 21, 2021

Input progress value is an existing term. "Position value" isn't in this proposal or the existing spec. Might be easier to make a comment against a particular line in the PR?

Sorry I didn't realise this was a PR! I was just responding to the first comment here which I thought was the proposal and which uses terms like "position value". I'll have to look at the PR next week.

@tabatkins
Copy link
Member

I feel we should keep the key word linear-easing(....)

The reason I don't like having -easing() in the name is because the existing easing keywords and functions don't use that suffix either. A custom easing (as in, backed by JS or something as a Houdini API) would probably need a name like custom-easing(), but for the built-ins, linear-easing() looks a little weird alongside linear, ease-in, and steps().

I don't think we should complicate the linear-easing function with fillModeI don't think we should complicate the linear-easing function with fillMode

I'm not saying it interacts with fill mode. I'm saying that we can use the analogy of fill mode for the keyword that extends the linear easing backwards/forwards infinitely. There might be a better keyword, it was just my first suggestion.

So, they could both be %, or both be numbers. I went with numbers because that's what the spec already uses for cubic-bezier and all the diagrams.

cubic-bezier isn't quite doing percentages, tho. (well, all numbers are percentages, just with the decimal point moved, but you know what I mean) Placing points between "start" and "end" is a percentage, tho, and we use that type to indicate the exact same semantic for animations and gradients. We should stick with it, both for consistency and readability. (I literally couldn't tell what value was which in your example, and had to read your prose to figure out that you meant for the progress values to be evenly spaced, meaning the .2/.4/.6/.8 must be the progresses.)

This gets really weird when trying to generate auto values for input position. I think it's why gradient syntax doesn't allow it (well, if one percent is less than the previous one, it makes it equal to the previous one). Being consistent with gradient syntax here seems better.

Sorry, you misunderstood me. Using different types means that ordering in each pair doesn't need to be specified - you could write 20% .3 or .3 20%. Between pairs we obviously still need ordering, for all the reasons you give.

[stuff about doubling up progress value]

Yeah that works nicely, and I agree that in general having doubled progresses is useful just as it is for gradients. We'd need to call this technique out explicitly in the spec as a way to get "flat" values at the start/end, but I like when things come for free. ^_^

Note that this makes the values even less readable when they're all the same type; percentages are an absolute necessity here.

@jakearchibald
Copy link
Contributor Author

Placing points between "start" and "end" is a percentage

Isn't the other value also a percentage by that logic? Where 0% is the start value and 100% is the end value.

We could make it like opacity where 0-1 and 0%-100% mean the same thing, and folks could use either. That means you need ordering within the pair though.

We'd need to call this technique out explicitly in the spec as a way to get "flat" values at the start/end

I don't have a gut feeling for what "expected" behaviour is here, but if folks expect flat at the start/end maybe we should make that the default.

@tabatkins
Copy link
Member

Yeah like I said, all numbers can be percentages, especially when they're typically in the [0,1] range.

But we have precedence that progress is written as a % (from both animations and gradients), and forcing it to be a % while the other is a number gives us useful readability benefits, whereas making them both the same type (or allowing both to be either type) makes it harder to read.

I don't have a gut feeling for what "expected" behaviour is here, but if folks expect flat at the start/end maybe we should make that the default.

Problem is that if we do that, then we still need to add a separate mechanism to make it extendable so you can get the same behavior as linear. Out-of-bounds progress is mostly a corner-case anyway; unless there's a great reason to do one or other specifically, I'd prefer we go with the behavior that gives us the simplest syntax. (Which means, currently, I prefer "extend the first/last segment indefinitely, and you can get flat manually without much effort by just making the first/last segment flat".)

@jakearchibald
Copy link
Contributor Author

Fair points all around, I'm convinced 😀

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Aug 22, 2021

The nice thing about this is you can now explain step() using linear(). You can also create stepped easings that are non-linear.

@birtles
Copy link
Contributor

birtles commented Aug 23, 2021

I finally had a look at the PR (sorry, I completely missed that this was a PR the first time around).

First, a minor process point: CSS Easing Level 1 is in CR so this will need to go in Level 2. Tab can tell us how best to proceed with that.

Secondly, an even more pedantic point just to get this out of the way, @fantasai once taught me that it's helpful to break up lines quite a bit, roughly one line per clause so that it makes it easier to follow the blame log later.

e.g. instead of

A [=linear easing function=] has <dfn for="linear easing function">points</dfn>, a [=/list=] of [=linear easing points=]. Initially a new empty [=/list=].
A [=linear easing function=] has <dfn for="linear easing function">points</dfn>,
a [=/list=] of [=linear easing points=].
Initially a new empty [=/list=].

The long lines also make it a bit hard to review this in GitHub.

On to the content itself:

  • I agree with Tab about keeping linear and linear(0,1) consistent. I think we've generally found when chaining these functions together that extrapolating tends to produce the most natural results. That's why the algorithm for cubic bézier easing functions tries to find the tangent at the ends and use that for extrapolation. I'm pretty sure it was the Chrome team discovered that works best.

    Doing that would mean, I think, that linear(0.4 40%, 0.6 60%) should be the same as linear(0, 1). If you want the straight line version you'd have linear(0.4, 0.4 40%, 0.6 60%, 0.6) (assuming the missing position values are filled in using the same approach we use for missing keyframe offsets).

  • Speaking of which, does the spec need to define how the second <<number>> is calculated when not specified? And the serialization of the function?

  • I love that you made the algorithm follow the standard end-point exclusive timing. That is, when two or more points overlap, you made it choose the last point to start for. Thank you!

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Aug 23, 2021

I've updated the initial comment to reflect feedback. I've changed the spec to follow, and made it less algorithmic as @tabatkins suggested. I hope it still makes sense! I guess it would benefit from diagrams like I've included in the OP.

  • Speaking of which, does the spec need to define how the second <<number>> is calculated when not specified? And the serialization of the function?

Yes, I'm not really sure how to write that down, but we need something that converts the notation into a set of points. Also, calculate linear output progress needs connected up so it's actually used.

I tried to copy the syntax from linear-gradient, but I couldn't see where things like yellow 40% 60% are allowed. I'm looking at the syntax, but I can't see how it allows three values.

Secondly, an even more pedantic point just to get this out of the way, @fantasai once taught me that it's helpful to break up lines quite a bit, roughly one line per clause so that it makes it easier to follow the blame log later.

I've given this a go (although I generally find it more of an authoring burden vs any later benefit). Also, it isn't the style used throughout the rest of the document.

@Afif13
Copy link

Afif13 commented Aug 23, 2021

I recently wrote an article about how to approximate such curves using CSS variables and CSS houdini (wihout any JS): https://css-tricks.com/build-complex-css-transitions-using-custom-properties-and-cubic-bezier/ .. It might be helpful until we have such feature.

@dholbert dholbert marked this pull request as ready for review August 23, 2021 17:13
@dholbert
Copy link
Member

dholbert commented Aug 23, 2021

Oops, sorry -- I clicked a "review" button, in an attempt to read the latest version of the PR, but that seems to have actually been a 1-click button to change the state of things here. (I see "dholbert marked this pull request as ready for review" in the comment history now.)

I don't see an "oops, undo" button, or else I would click it. Sorry for muddling with things; feel free to revert my marking or whatever's appropriate. [EDIT: I found and clicked the appropriate button to undo my unintentional state-change, and I converted this back to a draft.]

(I'm an infrequent github user and trip over things now and then.)

@dholbert dholbert marked this pull request as draft August 23, 2021 17:19
@tabatkins
Copy link
Member

I tried to copy the syntax from linear-gradient, but I couldn't see where things like yellow 40% 60% are allowed. I'm looking at the syntax, but I can't see how it allows three values.

You want Images 4, not 3: https://drafts.csswg.org/css-images-4/#color-stop-syntax. The text is pretty simple:

A color stop with two positions is equivalent to specifying two color stops with the same color, one for each position.

@birtles
Copy link
Contributor

birtles commented Aug 23, 2021

For the unspecified input progress case, I think it would be good to follow the keyframe spacing behavior here, i.e. linear(0.2, 0.3, 0.5 50%, 0.7 70%, 0.2) is computed to linear(0.2 0%, 0.3 25%, 0.5 50%, 0.7 70%, 0.2 100%).

Unfortunately the text for that in Web Animations is very algorithmic and we could probably get by with a much more simple prose description for the same here.

Here's an initial (fairly unsuccessful) attempt:

The positions for points without a specified position is computed such that they are spaced equidistantly between points with specified positions whilst ensuring that the computed position of the first point in the series of points, if not specified, is zero (0), and the computed position of the last point in the series of points, if not specified, is one (1).

Update: Never mind the above. I completely misunderstood the initial description. I thought "Similarly, input values will typically be 0%-100%, but can extend beyond that to provide points for edge-cases where easings are chained" was describing the inputs to the function meaning that this proposal allows positions less than 0% / greater than 100% but it's just restating the definition of an input progress value.

@tabatkins
Copy link
Member

We can copy the gradient text: https://drafts.csswg.org/css-images-4/#color-stop-fixup

(Notably, we'll still need step 2, because calc() means we can't always detect whether stops are in the correct order at parse-time.)

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Aug 24, 2021

You want Images 4, not 3: https://drafts.csswg.org/css-images-4/#color-stop-syntax

Oh ffs, I totally missed that. I got to https://drafts.csswg.org/css-images-4/#linear-gradients which suggests there's no change in the notation.

@birtles
Copy link
Contributor

birtles commented Aug 24, 2021

However, I'm not sure how to hook it all up from here.

I think we need to create an easing level 2 spec first. Given that level 1 is in CR, I think we could just copy all the text from there into level 1 without bothering with a delta spec. Tab might have a better suggestion though.

@jakearchibald
Copy link
Contributor Author

TODO: Need to make this fail on infinite values, since they don't interpolate.

@tabatkins
Copy link
Member

No need; outside of the special-case handling of infinities in the math functions themselves, infinities showing up anywhere else just get clamped to the maximum value the impl supports in the context.

(Plus they interpolate just as well as any sufficiently-large finite value.)

@torickjdavis
Copy link

I'm coming to this thread via the HTTP 203 Video "Bringing bounce and elastic easing to CSS". One comment I made there, I thought might have more benefit here:

As an alternative to overloading linear, would it make sense to call it lerp (linear interpolation)?

@SebastianZ
Copy link
Contributor

SebastianZ commented Dec 5, 2021

As an alternative to overloading linear, would it make sense to call it lerp (linear interpolation)?

There are two reasons why I wouldn't prefer this. Firstly, abbreviations are very uncommon in CSS. And secondly, the term lerp is probaby unknown to most web authors. I'm not sure whether linear is the best name for it either, though.

Sebastian

@nohns
Copy link

nohns commented Dec 18, 2021

Saw a comment from Viktor Luft on the Youtube video Bringing bounce and elastic easing to CSS | HTTP 203, which made good sense to me:

I'd call it "linear-spline" instead of "linear" to match the mathematical notation. I really like the idea

Also another comment, from victornpb, highlighted the possibility of adding smoothing to the function, reducing the amount of points need to form beautiful easings:

I think this could be easily expanded to have smoothed functions, the same way graphing libraries do point interpolation (quadratic, log, etc). This would probably solve long animations without having to have like 500 points.

I think these points worth taking into consideration when moving forward with this proposal. The syntax could be something like this. Not much more complex than cubic-bezier curves :

animation-timing-function: linear-spline(0, 0.2, 0.5, 0.9, 1.1, .0.95, 1);
animation-timing-function: smooth-spline(0, 0.2, 0.5, 0.9, 1.1, .0.95, 1);

Using cardinal interpolation as in this example, can cause problems with getting multiple y values for a given x value, but maybe this can be circumvented by fallbacking to linear interpolation if it happens

@okikio
Copy link

okikio commented Dec 18, 2021

animation-timing-function: linear-spline(0, 0.2, 0.5, 0.9, 1.1, .0.95, 1);
animation-timing-function: smooth-spline(0, 0.2, 0.5, 0.9, 1.1, .0.95, 1);

I like @nohns idea, I think it makes lots of sense, how does everyone else feel?

@mauriciabad
Copy link

mauriciabad commented Jan 7, 2022

There's a thing I don't see explained in the spec (I'm not used to .bs files, so maybe it's there, but I don't see it):

What happens when 2 points have the same x coordinate?
e.g. linear(0.1 50%, 0.9 50%) (or even linear(0.1 50%, 0.1 50%))

The algorithm only considers the first appearing point in the list, so the example is equal to linear(0.1 50%)?

If yes, keep in mind that then, linear(0.1 50%, 0.9 50%) becomes that special case where there's only 1 point, but the original list had more than 1 point.

BTW (off-topic), I love this proposal because it is simple to understand and very aligned with the "typical" pure css approach to do complex easings. I wanted custom easings so bad that I created my own tool to make them using keyframes (https://easyeasings.mauri.app/). I'm so happy to see progress on this, so I can stop using pesky keyframe animations!

@jakearchibald
Copy link
Contributor Author

The algorithm takes care of this. Values cannot go backwards. If it's less than the previous value, it's clamped to the previous value. It's the same behaviour as linear-gradient.

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Jan 27, 2022

Context for an upcoming working group meeting:

This is a proposal for a way to create custom easings, such as bounce, and elastic.

Easings are created using a syntax very similar to linear-gradient():

.whatever {
  animation-timing-function: linear(0, 0.7, 0.87, 0.98, 1);
}

This would create the following easing:

Screenshot 2021-08-23 at 13 11 21

The input values are distributed automatically, but can also be manually specified, as with linear-gradient. For example, the above is equivalent to linear(0 0%, 0.7 25%, 0.87 50%, 0.98 75%, 1 100%).

Like linear-gradient, output values are linearly interpolated from one value to the next.

By providing enough points, developers can specify easings that are currently impossible, such as bounce.

:root {
  --ease-bounce: linear(
    0, 0.003, 0.013, 0.03, 0.05, 0.08, 0.11, 0.15, 0.2, 0.26,
    0.31, 0.38, 0.45, 0.53, 0.62, 0.71, 0.81, 0.9, 0.99, 0.94,
    0.89, 0.85, 0.82, 0.79, 0.77, 0.76, 0.752, 0.75, 0.755,
    0.77, 0.78, 0.81, 0.84, 0.87, 0.92, 0.97, 0.99, 0.97, 0.95,
    0.94, 0.938, 0.94, 0.949, 0.96, 0.99, 0.994, 0.986,
    0.985, 0.989, 1 100% 100%
  );
}

.whatever {
  animation-timing-function: var(--ease-bounce);
}

…which would graph like this:

Screenshot 2021-08-23 at 11 45 53

The door would remain open for a more advanced syntax that allowed for curves to be specified, hopefully in a way that also works for linear-gradient. However, attempts to introduce that kind of complexity initially have stalled previous efforts, eg #229, so this proposal limits itself to linear interpolation between points.

@chrishtr
Copy link
Contributor

Agenda+ to discuss and hopefully resolve to add in 2 weeks (can't be an APAC meeting since Jake is in Europe).

@birtles
Copy link
Contributor

birtles commented Jan 27, 2022

Agenda+ to discuss and hopefully resolve to add in 2 weeks (can't be an APAC meeting since Jake is in Europe).

I won't be able to make the telcon (I'm in APAC) but as the only(?) active editor of that spec I'm strongly in support of this proposal. It's something I've been suggesting for years and I'm really grateful Jake has done the hard work of actually turning this into a PR.

@flackr
Copy link
Contributor

flackr commented Jan 27, 2022

Not one of the editors, but I just want to say I completely agree with @birtles. This is a valuable addition to the platform and I like the alignment with gradient easing. Thanks Jake!

@svgeesus
Copy link
Contributor

Context for an upcoming working group meeting:

I would hope those examples and diagrams would also make it into the spec?

@fantasai
Copy link
Collaborator

I think this is a good idea, but it needs to be PR against css-easing-2, since css-easing-1 is already in CR and widely deployed. (Which also means css-easing-2 needs to exist.)

@jakearchibald
Copy link
Contributor Author

Which also means css-easing-2 needs to exist.

I don't have experience with level'd specs so I'm not sure what the process is. I guess someone else will set that up, then I'll create a new PR against that spec?

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-easing] Some ideas for linear() easing, and agreed to the following:

  • RESOLVED: Accept this as L2 Editor’s Draft.
  • RESOLVED: Add Jake Archibald as editor/co-editor of that Editor’s Draft.
The full IRC log of that discussion <fantasai> Topic: [css-easing] Some ideas for linear() easing
<fantasai> github: https://github.com//pull/6533#issuecomment-1023455165
<emeyer> Github: https://github.com//pull/6533#issuecomment-1023455165
<faceless> present-
<emeyer> JakeA: (presents slides)
<emeyer> …Idea is why not let people define a bunch of points in the easing space and we’ll interpolate between them?
<TabAtkins> Big +1
<emeyer> …`linear-spline()` would permit more complex easing patterns
<argyle> +1
<Rossen_> q
<TabAtkins> Probably still want to extend cubic-bezier() later, yeah, but this is perfectly acceptable and reasonable on its own
<flackr> +1
<emeyer> …This would leave the door open to a nicer system later on, and in the meantime we can allow elastic easing (including bounce easing)
<chris> q+
<Rossen_> ack chris
<emeyer> chris: Overall I think this is a good thing. It’s a nice balance between complexity and usability. I’d like to see this move forward. Is there a volunteer to edit this level 2 easing spec? JAKE?
<emeyer> JakeA: What would that mean?
<emeyer> chris: It means handling feedback and pushing things forward to get to implementation.
<emeyer> JakeA: Yeah, I’ll give it a go.
<emeyer> Rossen: Great, thanks for being a good sport.
<smfr> q+
<Rossen_> ack fantasai
<TabAtkins> More than happy to help Jake out fwiw
<emeyer> fantasai: I think we need two resolutions. One to create easing-2 and the other to incorporate this proposal. I’d also like to suggest FPWD as soon as it’s in.
<smfr> https://github.com//issues/280
<Rossen_> ack smfr
<argyle> been in safari for ages too right?!
<emeyer> smfr: There is a proposal for spring timing functions. I do think we should continue with the current suggestion.
<emeyer> Rossen: Sounds like it will be great to start this as ED, put the work in, move it to L2. We can go to FPWD as soon as there’s enough support. We’ll see if Dino’s spring function effort can go in as well.
<Rossen_> q?
<emeyer> RESOLVED: Accept this as L2 Editor’s Draft.
<JakeA> Reporting for duty!
<emeyer> RESOLVED: Add Jake Archibald as editor/co-editor of that Editor’s Draft.

@okikio
Copy link

okikio commented Feb 16, 2022

Awesome 🥳

@ydaniv
Copy link
Contributor

ydaniv commented Feb 17, 2022

I think it would be great to also have bounce and elastic keywords along with this addition, as an OOTB sensible defaults for the "80%". Kind of like cubic-bezier() has its own with ease and ease-*.
Also since the 2 most probable use cases are bounce and elastic, and as @jakearchibald noted above, we'll probably start seeing the same value copied all over as a custom property value, so why not have 2 nice looking defaults already spec'd and shipped inside the browsers?

@jakearchibald
Copy link
Contributor Author

I think it would be great to also have bounce and elastic keywords along with this addition

I think that would get bogged down on what the default bounce and elastic would look like, so I don't think the addition of linear()/linear-spline() should be blocked on this.

For instance, the bounce in #6533 (comment) starts with an ease-in, but I can imagine a lot of cases where that wouldn't be desirable.


1. Set |largestInput| to |extraPoint|'s [=linear easing point/input=].

1. Otherwise, if |stop| is the first [=list/item=] in |stopList|,
Copy link

@dshin-moz dshin-moz May 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, admittedly this is an edge case, but if any of the entries not at either edge contain negative or higher-than-100% percentage values (linear(0, 0.2 -10%, 0.8 110%, 1.0)), this would no longer be a proper function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the ordering enforced? So -10% would be clamped to 0, since there's a 0 beforehand?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true on the start side - how about the end, linear([...], 0.8 110%, 1.0)? Could use |largestInput|, though each side behaving differently seems a bit less intuitive (Debatable, since we're already in an edge case).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the last value doesn't have a position, it should be assumed to be 100%. Does that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, let me test gradients…

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://jsbin.com/mopojul/4/edit?html,css,output

It seems like the first value is assigned 0% if it has no position. However, it seems like the last position will be Math.max(previousPosition, 100%).

My intent will be to spec the same position behaviour as gradients unless there's a good reason to do something different.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable. Thanks!

1. For runs of [=list/items=] in |function|'s [=linear easing function/points=] that have a null [=linear easing point/input=],
assign a number to the [=linear easing point/input=] by linearly interpolating between the previous and next [=linear easing function/points=] that have a non-null [=linear easing point/input=].

1. Return |function|.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serializing back from this returned function may result in a different round trip result: linear(0 0% 50%, 1 50% 100%) -> linear(0 0%, 0 50%, 1 50%, 1 100%). Do we want to specify how the serialization should be carried out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with linear-gradient? If there's precedent there, same should happen here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linear-gradient(0deg, red 0% 10%, green, blue 90%); seems to become llinear-gradient(0deg, rgb(255, 0, 0) 0%, rgb(255, 0, 0) 10%, rgb(0, 128, 0), rgb(0, 0, 255) 90%);.
That said, not sure if I 100% agree on keeping it the same as linear-gradient:

  • There may be a lot more stops being supplied for the easing function in general
  • linear-gradient's function equivalent is really called once for paint vs. every frame of animation for this easing function.

So the two options of keeping both presentable & function forms, or calculating the function form every time seem... unattractive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it's a little weird that syntax unfolds like that, but I think it still makes sense to do the same as gradient.

@jakearchibald
Copy link
Contributor Author

Closing this in favour of #7414, which makes the change in the correct spec.

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

Successfully merging this pull request may close these issues.

None yet