Search code examples
three.jsgsap

Tween two properties at once in Three.js with TimelineMax


TweenMax doesn't allow you to tween 2 or more properties at once in Three.js on the same tween. For example you can't tween rotation and position at once. You can only tween the rotation in one tween or the position but not both.

I've managed to do it by pushing 2 tweens in an array and calling tl.insertMultiple(array).

Unfortunately, for some unknown to me reason, it only plays fine the first time. After restart(), the tween/timeline is broken/stutters/skips.

Notice when the color changes to red it's no longer smooth animation.

jsFiddle Demo

/* TWEENMAX ANIMATION STARTS HERE
 p = position
 r = rotation
 t = time
*/

var miroKeyframes = JSON.parse(`[
{"t":"0"},
{"p":{"x":"0.050","y":"0.220"},"r":{"x":"0.246","y":"-0.444","z":"0.014"},"t":"0.29"},
{"p":{"x":"0.010","y":"0.060"},"r":{"x":"0.109","y":"-0.150","z":"0.150"},"t":"1.01"},
{"p":{"x":"0.746","y":"0.738"},"r":{"x":"0.109","y":"-0.050","z":"0.013"},"t":"1.67"},
{"p":{"x":"-0.495","y":"0.804"},"r":{"x":"0.097","y":"-0.040","z":"0.105"},"t":"2.63"},
...
]`);

// Setup a timeline object. Restart on complete.
var tl = new TimelineMax({ onComplete:restart }),

tweens = [];


for (var i = 1; i < miroKeyframes.length ; i++) {

  var keyframe = miroKeyframes[i]; //current keyframe
  var dur = keyframe.t - miroKeyframes[i-1].t ; //auto-duration


  tweens.push( TweenMax.to( obj.rotation, dur, { x:keyframe.r.x, y:keyframe.r.y, z:keyframe.r.z, delay:keyframe.t, ease:Sine.easeIn} ));
  tweens.push( TweenMax.to( obj.position, dur, { x:keyframe.p.x*20, y:keyframe.p.y*20, delay:keyframe.t, ease:Sine.easeIn} ));

  //Works with either one of these but not both. It will execute each consequently. I need both at the same time.
  //tl.add( TweenMax.to( obj.position, dur, { x:keyframe.p.x, y:keyframe.p.y, ease:Sine.easeIn } ));
  //tl.add( TweenMax.to( obj.rotation, dur, { x:keyframe.p.x, y:keyframe.p.y, ease:Sine.easeIn } ));

}

tl.insertMultiple(tweens);

Please let me know how to fix this and what's going on. I don't want to use 2 objects - 1 for rotation and 1 for position.


Solution

  • The solution came from the GSAP forums by PointC and Dipscom. Special thanks!

    The first and easier solution is to use labels or position parameters. Check the video tutorial.

     tl.to( obj.position, dur, { x:keyframe.p.x*10, y:keyframe.p.y*10, ease:Sine.easeIn }, "label" + i );
     tl.to( obj.rotation, dur, { x:keyframe.r.x, y:keyframe.r.y, ease:Sine.easeIn }, "label" + i );
    

    Working demo

    The second solution is to use 2 timelines - 1 main time line and 1 mini-timeline to add both properties at the same time:

      tl2.to( obj.rotation, dur, { x:keyframe.r.x, y:keyframe.r.y, z:keyframe.r.z, ease:Sine.easeIn}, 0 );
      tl2.to( obj.position, dur, { x:keyframe.p.x*20, y:keyframe.p.y*20,  ease:Sine.easeIn}, 0);
    
      tl.add(tl2);
    

    Working Demo

    Hope this helps.