tweenjs / tween.js

JavaScript/TypeScript animation engine
https://tweenjs.github.io/tween.js/
Other
9.8k stars 1.41k forks source link

custom interpolation function #352

Closed andreasplesch closed 1 year ago

andreasplesch commented 7 years ago

This issue is for thinking through an idea rather than for an actual issue with the library or even a feature request.

Arrays as target (to) values are allowed and the defined intervals are currently spread evenly such that each interval takes the same fraction of the total duration of the tween. For example, if there are four intervals (array length is five) and the total duration is 1000ms each interval takes 250ms. This behaviour is defined by the (linear, probably all) array interpolation function.

What I would like to have is an additional array of interpolation progress keys that make it possible to define uneven duration intervals, eg. different speeds for each interval. There would the same number of progress keys as target values. For example if there are five value array items [0, 2, 3, 3.5, 1] there would be five progress key array items [0, 0.1, 0.5, 0.9, 1]. The value 2 would be then returned as the result of the interpolation function when progress is 10% of the total duration, for example.

This is similar to chaining of tweens but has several advantages. It can be more concise, the behaviour can be exactly defined, eg. there is no question when exactly the next tween starts and is applied, it can be perhaps more efficient, and is more familiar as a key frames concept. Easing could be applied to the complete function, not just per tween.

Since this amount to modified or additional interpolation functions which take the progress key array as an additional argument, a generalization would be allowing custom interpolation functions.

andreasplesch commented 7 years ago

Ok, it looks like it is possible to provide a custom interpolation function to .interpolation . Is there an example somewhere ? One could probably just follow https://github.com/tweenjs/tween.js/blob/master/src/Tween.js#L771. Indeed, here is an example:

https://clear-apparel.glitch.me/

But it could only accept the to array (end) and the progress as parameters, and not an additional parameter which actually shapes the custom function. I guess one could make a specific custom function for each tween, or the custom function could access a (global) parameter, perhaps just added to the TWEEN namespace.

mikebolt commented 7 years ago

You can accomplish what you initially described by using an appropriate piecewise-linear easing function. If you still want custom easing on top of that, then you can compose the easing functions. A utility method that accomplished this would be helpful.

Interpolation functions that are tween-specific should work, but that is probably not efficient. I think the best way to support your use case would be to add an optional "interpolationInstants" parameter to Tween that the interpolation function can access. This accomplishes the same thing without per-tween closures.

Did that make any sense?

On an off-note, the interpolation code could benefit from some attention.

On Sun, Jul 2, 2017 at 2:09 PM, Andreas Plesch notifications@github.com wrote:

Ok, it looks like it is possible to provide a custom interpolation function to .interpolation . Is there an example somewhere ? One could probably just follow https://github.com/tweenjs/tween.js/blob/master/src/ Tween.js#L771. But it could only accept the to array (end) and the progress as parameters, and not an additional parameter which actually shapes the custom function. I guess one could make a specific custom function for each tween, or the custom function could access a (global) parameter, perhaps just added to the TWEEN namespace.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/tweenjs/tween.js/issues/352#issuecomment-312517129, or mute the thread https://github.com/notifications/unsubscribe-auth/ACCav71S7Knq-NVFZREGgeTwVJm1P-KYks5sKAcngaJpZM4OLqsR .

andreasplesch commented 7 years ago

https://glitch.com/edit/#!/rightful-chopper?path=views/index.html:59:12

is more or less what I had in mind.

I do not think a custom easing function is easy to derive when you want to have exact values at given times during the interpolation ?

You would need to know to what time a certain value corresponds to for the uneased function. It becomes an inverse problem.

Let me think about the other parts of your response.

Since it seems not too cumbersome to come up with custom interpolation functions, I do not think there is an argument for expanding the library. But examples for custom functions would be helpful.

mikebolt commented 7 years ago

Yeah, it should be documented. Regardless, if I can find a way to incorporate your interpolation function, I think it would be useful for others.

On Sun, Jul 2, 2017 at 7:54 PM, Andreas Plesch notifications@github.com wrote:

https://glitch.com/edit/#!/rightful-chopper?path=views/index.html:59:12

is more or less what I had in mind.

I do not think a custom easing function is easy to derive when you want to have exact values at given times during the interpolation ?

You would need to know to what time a certain value corresponds to for the uneased function. It becomes an inverse problem.

Let me think about the other parts of your response.

Since it seems not too cumbersome to come up with custom interpolation functions, I do not think there is an argument for expanding the library. But examples for custom functions would be helpful.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/tweenjs/tween.js/issues/352#issuecomment-312538546, or mute the thread https://github.com/notifications/unsubscribe-auth/ACCav3vaNSO0-glefqJCocgWhAh11ht2ks5sKFfygaJpZM4OLqsR .

andreasplesch commented 7 years ago

I could think more clearly about what the corresponding easing function would need to be. It turns out that we do know exactly when the uneased function reaches the array values, since the duration is evenly divided into the intervals. I updated the glitch here to show the equivalent custom easing function: https://rightful-chopper.glitch.me/ To superimpose one of the standard easing function, presumably it is possible to just apply it to the result of the custom easing function. It could be an additional parameter to the custom function, defaulting to linear (no-op). The easing approach is mentally backwards and harder to figure out. But it seems cleaner and can be universally applied to any interpolation function. So if this could be more useful for others, I am not sure now which solution (custom interpolation or custom easing) could be offered as part of the library. Both require generating per tween custom functions.

andreasplesch commented 7 years ago

The figure below attempts to explain how a custom easing for keyed frames is constructed. The easing needs to map the current progress to ? progress in the figure. It turns out to be simply a matter of finding the fractional progress in the current segment, and then applying this fraction to the corresponding segment in the evenly spaced, regular tween segments. In the example below this works out as: fractional progress in current segment: f=( 0.8-0.2 ) / ( 1-0.2 ) = 0.6/0.8 = 0.75 eased final progress: 0.5 + f (1-0.5) = 0.5 + 0.75 0.5 = 0.875

image

trusktr commented 4 years ago

It can be more concise, the behaviour can be exactly defined

Sidenote, there's no question when the next tween starts with chained tweens, they are perfectly aligned with the previous tween's time. See here:

https://github.com/tweenjs/tween.js/blob/1f85d6e74491037653ddad2ee4193958f09928e2/src/Tween.ts#L402-L405

But true, it can be more concise.

Easing could be applied to the complete function, not just per tween.

And that would be nice!

This is similar to features CSS @keyframes has. :+1:

looks like it is possible to provide a custom interpolation function to .interpolation

That's true, but this feature would make it easier and more concise.

If you do make a custom interpolation function generator, it could be used like this:

const tween = new Tween({scaleX: 0})
tween
  .to({scaleX: [0.25, 0.5, 0.75, 1]})
  .interpolation(percentInterpolation([10, 25, 75, 90]))

The equivalent in CSS would be:

@keyframes my-keyframes {
    0% { transform: scaleX(0); }
    10% { transform: scaleX(0.25); }
    25% { transform: scaleX(0.5); }
    75% { transform: scaleX(0.75); }
    100% { transform: scaleX(1); }
}

Also note, that in CSS, these key frames are saved to a my-keyframes variables, so the key frames can be re-used in any animation that can have any number of key frames with separate easing functions (i.e. they are composable onto an object's tween). We can probably borrow some concepts from CSS here.

I do not think a custom easing function is easy to derive when you want to have exact values at given times during the interpolation ?

Definitely! A declarative API (perhaps inspired by CSS, but a new chained API in Tween.js) would for sure make it easier, especially for someone who wants to focus on the experience they are making rather than the math involved with making interpolation functions.

I am not sure now which solution (custom interpolation or custom easing) could be offered as part of the library. Both require generating per tween custom functions.

I would argue let's focus on declarative APIs (taking some inspiration from CSS for example, though in the Tween.js chained syntax). However if there is anything we can do to make things easier for those people that want to make custom functions, let's think about API for them too.

The figure below attempts to explain how a custom easing for keyed frames is constructed.

Great diagram! I think that's a good idea. I haven't used interpolations in any projects yet (the need hasn't been there). If I understand correctly, an interpolation function basically sets up a "scrubber" (for all intents and purposes), and an easing function effectively scrubs across the timeline at different speeds. Is that how you imagine it @andreasplesch ?

trusktr commented 1 year ago

I think this issue would complicate the Tween too much. Instead, I think it would be more valuable to be able to achieve these sorts of things with a higher level Timeline API: https://github.com/tweenjs/tween.js/issues/647