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

Additive animations using tween.js! #329

Closed
martikaljuve opened this issue Mar 21, 2017 · 6 comments
Closed

Additive animations using tween.js! #329

martikaljuve opened this issue Mar 21, 2017 · 6 comments

Comments

@martikaljuve
Copy link
Contributor

martikaljuve commented Mar 21, 2017

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
Copy link

edap commented Apr 6, 2017

This would be really helpful

@mikebolt
Copy link
Contributor

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
Copy link
Contributor Author

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
Copy link
Collaborator

@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
Copy link

n8tz commented Aug 17, 2020

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
Copy link
Member

trusktr commented Oct 20, 2020

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: #571

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

No branches or pull requests

6 participants