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

Tweens should be composable with no side-effects #269

Closed
ejmurra opened this issue Jun 19, 2016 · 6 comments
Closed

Tweens should be composable with no side-effects #269

ejmurra opened this issue Jun 19, 2016 · 6 comments
Assignees
Labels

Comments

@ejmurra
Copy link

ejmurra commented Jun 19, 2016

Hi, I want to preface this by saying this library is excellent. I just finished a medium-sized project using tweenjs and I have some feedback.

I found that it was pretty cumbersome to create complex tweens using this library because the chain method is not side-effect free. Here's a pseudocode example:

/* I want to use tweenjs to make a light blink three times and then pause for a beat */
let light = new Light();
/* Here's the code I would expect to work */
let lightOn = new TWEEN.Tween().to(0, 100).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = x * 1);
let lightOff = new TWEEN.Tween().to(0, 100).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = 1 - (x * 1));

let lightBlink = lightOn.chain(lightOff) // A chain should return a new tween and NOT change lightOn itself

/* This makes our final product. lightBlink returns a tween, repeat 3 returns a new tween, delay 200 returns a tween after that, repeat again inifitely and then start that final tween. */
lightBlink.repeat(3).delay(300).repeat(Infinity).start() 

/* Here's the code I actually had to write to get this to work */
let lightOnTime = 75;
let lightOffTime = 225;

let lightOn = new TWEEN.Tween().to(0, lightOnTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = x * 1);
let lightOff = new TWEEN.Tween().to(0, lightOffTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = 1 - (x * 1));
let lightOn2 = new TWEEN.Tween().to(0, lightOnTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = x * 1);
let lightOff2 = new TWEEN.Tween().to(0, lightOffTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = 1 - (x * 1));
let lightOn3 = new TWEEN.Tween().to(0, lightOnTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = x * 1);
let lightOff3 = new TWEEN.Tween().to(0, lightOffTime).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = 1 - (x * 1));

lightOn.chain(lightOff.chain(lightOn2.chain(lightOff2.chain(lightOn3.chain(lightOff3.chain(new TWEEN.Tween().delay(150).chain(lightOn))))))).start()

As you can see, the second approach is more verbose and messy and less straight forward. This isn't a particularly complex tween but you can see how it would grow unwieldy very quickly.

One other note, when you have tween chains producing side-effects it can lead to subtle bugs. Here's an example:

let lightOn = new TWEEN.Tween().to(0, 100).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = x * 1);
let lightOff = new TWEEN.Tween().to(0, 100).easing(TWEEN.Easing.Quartic.Out).onUpdate(x => light.intensity = 1 - (x * 1));

let lightBlink = lightOn.chain(lightOff) // If you create a new tween and don't use it, it shouldn't affect the tweens it is composing

/* One would expect this to turn on the light quickly. Instead it turns the light on and off because of the chain above */
lightOn.start()
@sole
Copy link
Member

sole commented Jun 20, 2016

Thank you! Glad you liked the library and it helped you!

Agreeing that the chain method is unwieldly and not clear at all. We've discussed it before, in #176 and #153 (linking for reference).

I have no immediate fix for the issue - I'm just acknowledging it. It requires better thought and probably a breaking change (or more).

@mikebolt
Copy link
Contributor

mikebolt commented Aug 1, 2016

Because the chain method was advertised in the user guide as having an effect even when the return value is ignored, this probably cannot be changed safely in the current version. The best thing we could do right now would be to make the user guide more clear about the behavior. It may also help if we fixed the chain/repeat bug, or if we made a clone method, so that you would not have to completely redefine the tweens an extra two times.

Another option would be to make a compose method that takes two tweens and creates a new tween that is the same as the two tweens chained together, but does not modify either of the two tweens. Although it wouldn't break the existing API, this approach is probably more difficult to implement.

@dalisoft
Copy link
Collaborator

Write bit more code, you made complex tweens.
Tween.js is simple and FREE (to make it as you like).
Or use already complex tween maker based on tween.js

@sole
Copy link
Member

sole commented Oct 3, 2016

@mikebolt I think since the syntax is so terribad it would be just better to either make a 'breaking change' (bumping major version) or to use a better syntax for V2.

@mikebolt
Copy link
Contributor

I think we can deprecate 'chain' and add an equivalent 'compose' method with no side-effects. This should be documented better.

@trusktr trusktr removed the Bug label Jun 3, 2020
@trusktr
Copy link
Member

trusktr commented Apr 23, 2023

Tween is already getting complicated. Maybe it is better to pursue something like a Timeline feature where functionalities are abstracted and not all meshed into a single Tween class.

@trusktr trusktr closed this as completed Apr 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants