tweenjs / tween.js

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

Additive animations using tween.js! #329

Closed martikaljuve closed 6 years ago

martikaljuve commented 7 years ago

DEMO: https://bl.ocks.org/martikaljuve/863cb5edf774d3a154b8b423bc4381f6

I've been captivated by additive animations ever since I read about them - now I've finally taken the time to try implementing them using tween.js. I urge you to read and understand the following post, it was a real eyeopener for me: https://greensock.com/forums/topic/12573-additive-animation/?p=52587.

In short, additive animation makes it possible to have multiple tweens running on the same object. It can be implemented by storing the final value of a property and on every update, adding the sum of reversed Tweens to it, where reverse Tween means tweening from start value - end value to zero. For example, to animate var a = { x: 200 } to { x: 300 }, the animation will store { x: 300 } as the final state and tween from { x: -100 } to { x: 0 }, adding up the final state and tween value on every update.

Here's an example of having two tweens running on the same object with a 500ms delay:

var obj = { x: 0 };

var composite = new TWEEN.CompositeTween(obj)
  .on('update', function(obj) { console.log(obj); });

var t1 = composite.add({ x: 100 }, 1000)
  .start(0);

var t2 = composite.add({ x: 0 }, 1000)
  .start(500);

TWEEN.update(0); // { x: 0 }
TWEEN.update(100); // { x: 10 }
TWEEN.update(200); // { x: 20 }
TWEEN.update(300); // { x: 30 }
TWEEN.update(400); // { x: 40 }
TWEEN.update(500); // { x: 50 }
TWEEN.update(600); // { x: 50 }
TWEEN.update(700); // { x: 50 }
TWEEN.update(800); // { x: 50 }
TWEEN.update(900); // { x: 50 }
TWEEN.update(1000); // { x: 50 }
TWEEN.update(1100); // { x: 40 }
TWEEN.update(1200); // { x: 30 }
TWEEN.update(1300); // { x: 20 }
TWEEN.update(1400); // { x: 10 }
TWEEN.update(1500); // { x: 0 }

The beauty of this approach will only become apparent with non-linear easing:

var obj = { x: 0 };

var composite = new TWEEN.CompositeTween(obj)
  .on('update', function(obj) { console.log(obj); });

var t1 = composite.add({ x: 100 }, 1000)
  .easing(TWEEN.Easing.Cubic.InOut)
  .start(0);

var t2 = composite.add({ x: 0 }, 1000)
  .easing(TWEEN.Easing.Cubic.InOut)
  .start(500);

TWEEN.update(0); // { x: 0 }
TWEEN.update(100); // { x: 0.40 }
TWEEN.update(200); // { x: 3.20 }
TWEEN.update(300); // { x: 10.80 }
TWEEN.update(400); // { x: 25.60 }
TWEEN.update(500); // { x: 50 }
TWEEN.update(600); // { x: 74 }
TWEEN.update(700); // { x: 86 }
TWEEN.update(800); // { x: 86 }
TWEEN.update(900); // { x: 74 }
TWEEN.update(1000); // { x: 50 }
TWEEN.update(1100); // { x: 25.60 }
TWEEN.update(1200); // { x: 10.80 }
TWEEN.update(1300); // { x: 3.20 }
TWEEN.update(1400); // { x: 0.40 }
TWEEN.update(1500); // { x: 0 }

The only change I had to make to the original Tween.js code is to expose var _valuesStart as this._valuesStart. I also wrapped the TWEEN.update function to emit a TWEEN.on('update', listener) event and implemented a TWEEN.EventedTween object that replaces onStart/onStop/onUpdate/onComplete callbacks with .on('start|stop|update|complete', listener) events.

Hopefully someone else finds this as useful (and cool) as I do. Feedback is appreciated. I will also mention that I didn't consider array values and chain/repeat/yoyo, since I've never used this functionality.

EDIT: Added images.

edap commented 7 years ago

This would be really helpful

mikebolt commented 7 years ago

This would completely change the way the library works. It would be better to start from scratch, or try to add this into the es6 version.

martikaljuve commented 7 years ago

This is definitely different from the regular TWEEN.Tween behavior. It is implemented as a separate TWEEN.CompositeTween class that I could publish as a plugin, but it requires exposing the _valuesStart object in TWEEN.Tween first.

dalisoft commented 6 years ago

@martikaljuve Try es6-tween v4.0.2, something like you want, it has been on my plan few month ago, but haven't been time

n8tz commented 4 years ago

I've wrote an engine some years ago doing this. https://github.com/react-voodoo/tween-axis

This lib allow working with additive tween of numeric values, It need some cleaning / js updates & can have minimal imprecision depending the use case, But it deal with delta based additive / merged animations Fell free to make contribs / use it

Also as it was implying some code constraints & there was advantages using it with SSR / scroll, i've started a tweening engine for react to allow SSR rendering & draggable combined anims https://github.com/react-voodoo/react-voodoo

trusktr commented 3 years ago

This is some good stuff (two years later :D) ! If a concept builds on top of Tween.js, that is great because it doesn't impact the foundational implementation.

Are there any features of Tween.js that can be improved to make creation of CompositeTween easier? Looks like adopting an event emitter pattern would help. Issue to track that: https://github.com/tweenjs/tween.js/issues/571