Jump to content
GreenSock

Mr Pablo

Timeline of Tweens not repeating

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

I set up a few Tweens, using Tween.set and a few .to() methods.

 

I then added these tweens to a Timeline, which was then added to a master Timeline, which is initially set to paused, and to repeat forever (setting it to -1).

 

The animation plays once then simply stops.

 

The files are attached. Problem is in the area of line 86 and line 109 onwards.

 

NB- If I change the TweenMax instance to a Timeline, it repeats properly.

Link to comment
Share on other sites

Hi,

 

You got me with almost no free time.

 

What I can see is that your master timeline is emtpy, if you do the following:

masterTimeline.add(timelineArray, 0, "sequence");

console.log(TweenMax.getTweensOf(masterTimeline));

It returns an empty array, therefore nothing is being added to your timeline.

 

Check the function you're using to add the particular timelines into the master one, also check what's in your timelines array, maybe the problem is there. Your tweens are being created there's no problem there, so you have to take it from there.

 

Hope this helps,

Cheers,

Rodrigo.

Link to comment
Share on other sites

I just put a console log output for "masterTimeline" and it seemed to be populated...

 

If what you said is true, and "masterTimeline" is technically empty, how come the tweens play once? My code only tells the "masterTimeline" to play, which in turn plays the tweens.

 

Confused.com!

Link to comment
Share on other sites

Sorry I didn't read your post very well.

 

I cannot understand how changing form timelines to tweens stopped them being added to the master timeline, the code to add them to the array is untouched!

 

Really no idea what's going on.

 

EDIT - I went back to the working version, using all timelines. Tried "TweenMax.getTweensOf(masterTimeline)" and it spat out an empty array, which is not the case!

 

I believe in the version I uploaded, the master timeline IS populated, but there is another reason it doesn't loop.

Link to comment
Share on other sites

in the future please try uploading a zip of all the assets necessary to run / test your file.

 

There seems to be a confusing amount of adding things to arrays and timelines going on.

Wasn't able to follow all of it. 

Perhaps some code comments would help.

 

My best guess is your animations are not really inside of masterTimeline and thus the repeats on masterTimeline aren't working.

 

Replace the bottom of your code with:

 

var masterTimeline = new TimelineMax({paused: true, repeat: -1});
            console.log("timelineArray.length " + timelineArray.length);
            masterTimeline.add(timelineArray, 0, "sequence");
console.log("masterTimeline duration " +  masterTimeline.duration());
           console.log("masterTimeline getChildren() ");
  console.log(masterTimeline.getChildren());    
            window.onload = function()
            {
                masterTimeline.pause(1);
            };

You will see that your masterTimeline has no duration as it is populated with 3 items that have no duration.

 

I would suggest you dig through a bit more.

 

Definitely read up on TimelineMax.getChildren as I think it will help you figure out exactly what is inside your masterTimeline and other timelines

Link to comment
Share on other sites

Hi

 

Sorry I couldn't follow up properly.

 

My point was precisely that, the timelines are being created but they aren't added to the master timeline and since the master timeline is the only one paused the other timelines play after being created and just once, because the master is the only one with a repeat var assigned. That's why my assumption was that the problem is in the part of your code when the timelines are added to the master one, the rest of the code is working properly; the nested timelines are being created and if you check the master timeline is playing and repeating endlessly, the only part missing is add the nested timelines into the master.

 

I'll take a better look at your code and see if I can come up with something.

 

Cheers

Rodrigo.

Link to comment
Share on other sites

Carl - Yea sorry, it's a tad confusing haha! It works like this:

 

The animation has 2 images. These have 2 transitions each (could be more or less in the end) these transitions are a start point and an end point.

 

I loop through the image assets in the json object, use the set() method to place them, then the transitions get made into a tween/timeline.

 

These then get added (well, hopefully) to the master timeline. This is done because there can be multiple animations outlined in the file.

 

Whilst the full timeline method worked, I couldn't get assets to start tweeting at the same time, if need be. I wanted to change the first bit to TweenMax rather then timeline so I could have assets tween at the same time.

 

I'll comment the code a bit better and maybe add another animation in to show how it could be.

 

Sadly won't be able to do so till next Wednesday though!

Link to comment
Share on other sites

Hi,
 
I just took another look to your code. First I apologize, the getTweensOf method gets the tweens of the timeline target and not the timeline, so Carl's suggestion for using the getChildren() method allow me see that in fact there are tweens in your timeline, but also all the nested tweens have a zero duration. So looking at your code you have this:

$.each(json['assets'], function(i, v){
    $('#master_container').append('<div id="'+ v.name +'"></div>');

    timeline[i] = TweenMax.set( $('#' + v.name), { css: { backgroundImage: "url('"+ v.src +"')", height: v.transitions[0].height + "px", position: "absolute", opacity: v.transitions[0].opacity , width: v.transitions[0].width+"px" } } );

Then this:

$.each(v.transitions, function(a, {
    /*some code*/
    TweenMax.to( $('#' + v.name), b.duration / 1000, { ease: easing, opacity: b.opacity, rotation: b.angle, scaleX: b.scaleX, scaleY: b.scaleY, x: b.left, y: b.top });
});

An finally:

objectTimeline.add(timeline[i]);
					
});
 
    timelineArray.push(objectTimeline);

masterTimeline.add(timelineArray, 0, "sequence");

Basically you're creating TweenMax.set and TweenMax.to instances based in json data, then you add them to an array and finally you put that array in the parent timeline, but the problem is that you're including just the set instances in your array so your timeline only has zero duration tweens. Maybe it'll work if you add the TweenMax.to() instances to your array.

 

Also see if you could work with fromTo instances it could save you some code lines and avoid the second loop, which could be good if you're planning on add more elements to your app.

 

Hope this helps,

Cheers,

Rodrigo.

Link to comment
Share on other sites

After some messing around, I am still having issues.

 

Right now, I am adding my image animations to a TweenMax instance, adding that to a TimelineMax instance, then adding each timeline to an array. This array is then added to the Master timeline.

 

The 2 animations play, but simultaneous, despite the master timeline being set to "sequence" mode.

 

Current code here:

 

https://dl.dropboxusercontent.com/u/3392109/index.html

Link to comment
Share on other sites

hmmm, the link is missing a few assets so nothing executes and there isn't any way to test any assumptions. 

 

From a quick glance it looks like you are pushing objectTimeline to that timelineArray multiple times

 

timelineArray.push(objectTimeline)

 

You probably should be creating NEW objectTimelines for each object. Seems you are re-using the same one over and over and trying to have it contain different tweens and populate multiple positions in an array and ultimately different positions in a parent timeline.

 

try creating a new timeline for each object in your json. 

Link to comment
Share on other sites

Hi,

 

As Carl said you are adding things too many times to your array and I believe that you should review the json data also.

 

I set up a codepen with the images and code you posted the first time:

 

See the Pen htxfI by rhernando (@rhernando) on CodePen

 

I don't know if this is somehow similar to what you want to achieve, but at least the timeline is repeating and the nested tweens are in sequence. Feel free to fork it and play around with it if further questions arise.

 

Now to the code, the  first thing that came out was the following:

/** add the tween to the timeline **/
objectTimeline.add(TweenMax);

Of course with that you're adding every tweenmax instance there is, but that could backfire in my opinion. Then this code was outside the loop that created the background and glass animation and for a simple scope matter those will never end up in the objectTimeilne, so once again the elements being added to the timeline were zero duration tweens. When the code changed to this:

$.each(json['assets'], function(i, v)
{
    $('#master_container').append('<div id="'+ v.name +'"></div>');
    //more code...
		
    $.each(v.transitions, function(a, 
    {
	//more code...
	
        //create a particular element to add to the timeline instead of every TweenMax instance
        var tn = TweenMax.to( $('#' + v.name), b.duration / 1000, { ease: easing, opacity: b.opacity, rotation: b.angle, scaleX: b.scaleX, scaleY: b.scaleY, x: b.left, y: b.top } );
	
        /** add the tween to the timeline **/THIS IS THE NEW CODE
	objectTimeline.add(tn);
    });
		
    /** add the tween to the timeline **/THIS WAS THE OLD CODE
    //objectTimeline.add(TweenMax);
});

You get the result of the codepen posted.

 

Finally why I suggest to look at the json data?, because when I was changing the images url  in objectArray[1] just one glass appeared in the result, unless two glasses are needed, and also there's the positions when the animation begins.

 

Hope this helps,

Cheers,

Rodrigo.

  • Like 1
Link to comment
Share on other sites

rhernando - I got it working not long after my post, with a solution similar to yours!

 

I created the objectTimeline and added the tweens to it directly, with "objectTimeline.add( TweenMax.set/to... )"

 

Until I add more animations and test out adding in the tween durations, it seems to be ok! Will let you know how it goes :)

Link to comment
Share on other sites

Typically, I have ran into some issues :D

 

Whilst I am now successfully adding Tweens to the object Timelines, I need to somehow hide the images after that timeline has finished.

 

I have tried "objectTimeline.set({}, { css: {opacity:0} }, 20)" but it just doesn't work, I think because it needs to target a specific asset, but I can't as this code has to sit outside the loops for the assets.

 

My other issue is that TweenMax.set and .to being static is a real set back. I really want to be able to use the following:

var tn = TweenMax.set();

tn.to()...
tn.to()...

Because I think the following is causing issues with durations and delays:

objectTimeline.add( TweenMax.set(...) );

objectTimeline.add( TweenMax.to(...) );
objectTimeline.add( TweenMax.to(...) );

Link to comment
Share on other sites

HI,

 

Try using immediateRender:false in the set instances, or a super short to instance to quickly tween the opacity.

 

Immediate Render:

objectTimeline.add( TweenMax.set(element), {opacity:0, immediateRender:false} )

Short tween:

objectTimeline.add( TweenMax.to(element, 0.0001), {opacity:0} )

Also you could create a generic onComplete callback and apply it  to any element every time you need it, like this:

objectTimeline.add( TweenMax.to(element, time{vars, onComplete:setOpacity, onCompleteParams:["{self}"]}) )

//Function
function setOpacity(tween)
{
    var element = tween.target;
    TweenMax.set(element, {opacity:0});
}

Like that every time you need to set an element's opacity to zero you can use the onComplete and onCompleteParams and the target of the objectTimeline tween's opacity will be set to zero. Finally if you need to tween the opacity instead of setting it immediately you can add a second parameter, time, like this:

objectTimeline.add( TweenMax.to(element, time{vars, onComplete:setOpacity, onCompleteParams:["{self}", time]}) )

//Function
function setOpacity(tween, time)
{
    var element = tween.target;
    TweenMax.to(element, time, {opacity:0});
}

//Equivalent to set
objectTimeline.add( TweenMax.to(element, time{vars, onComplete:setOpacity, onCompleteParams:["{self}", 0]}) )

//One second fade out
objectTimeline.add( TweenMax.to(element, time{vars, onComplete:setOpacity, onCompleteParams:["{self}", 1]}) )

Hope this helps,

Cheers,

Rodrigo.

Link to comment
Share on other sites

I know I need to use the onComplete option, but I am not sure how to add it in, as I have ended up with a different way of setting my Tweens.

 

I am creating tweens thusly:

var tn = TweenMax.set( $('#' + v.name), { css: { backgroundImage: "url('"+ v.src +"')", height: v.transitions[0].height + "px", position: "absolute", opacity: v.transitions[0].opacity , width: v.transitions[0].width+"px" } } );

tn = TweenMax.to( $('#' + v.name), b.duration / 1000, { delay: b.wait / 1000, ease: easing, opacity: b.opacity, rotation: b.angle, scaleX: b.scaleX, scaleY: b.scaleY, x: b.left, y: b.top } );

objectTimeline.add(tn);

The tween stuff is inside a loop (to cater for multiple tweens for a single image asset). After the loop, the tween is added to the "objectTimeline".

 

I am pretty sure I need onComplete functions for the objectTimelines, but I have no idea how you tell a timeline to set every tween inside it to change to zero opacity.

 

This doens't work:

function setOpacity(tween)
{
tween.to( {}, { opacity:0 } );
}

Called by:

objectTimeline = new TimelineMax( { onComplete: setOpacity, onCompleteParams: [ "{self}" ] } );

NB - I'm certain I cannot have the onComplete's on the Tweens, as they are added to the object timeline, so they cannot "disappear" after the tween is complete, it has to be after the timeline is complete.

Link to comment
Share on other sites

Hi,

 

Well is a little more complicated, but not hard by any means. I'm going backwards on this one so let's rock...

 

Adding callback parameters to a timeline is quite different than doing it in tween instances. In the case of the ["{self}"] param it refers to, is the tween instance and in the case of a timeline the timeline itself. So when the function calls to a tween param it's calling it to the tween or timeline, like this:

function doStuff(tween)
{
    TweenMax.set(tween, {opacity:0);
}

In this case you're telling the engine to animate the tween or timeline opacity to zero, but tweens/timelines are not DOM elements so nothing happens. That's why you define the tween's target inside the function, like this:

function doStuff(tween)
{
    var element = tween.target;
    TweenMax.set(element, {opacity:0);
}

In this case the if the tween param is a tween instance the tween.target is a DOM element, for example the beer glass, but if the param is a timeline the target becomes an issue because it'll return undefined.

 

Considering what's above you should add the onComplete call in the tweens being created in the loop and I assume that you want to apply this to the beer glass so it should be in that particular loop:

tn = TweenMax.to(
                 $('#' + v.name),
                 b.duration / 1000,
                 {
                     delay: b.wait / 1000,
                     ease: easing,
                     opacity: b.opacity,
                     rotation: b.angle,
                     scaleX: b.scaleX,
                     scaleY: b.scaleY,
                     x: b.left,
                     y: b.top,
                     //INSERT CALLBACK
                     onComplete:setOpacity,
                     onCompleteParams:["{self}"]
                 }
);

Also you have two loops, the second resides inside the first one, so for better use define the function inside the DOM ready event, like that it'll have a generic scope and you could use it in other GSAP instances:

$(function(){


$.each(//loop1

   $.each(//loop2
      
   );

);

function setOpacity(tween)
{
    var element = tween.target;
    TweenMax.set(element, {opacity:0});
}

});

Hope this helps,

Cheers,

Rodrigo.

Link to comment
Share on other sites

Hey Rodrigo, thanks for taking your time to help!

 

Unfortunately, I cannot use the onComplete calls on the separate tweens. this is because I need the tweened element, e.g. the beer glass, to stay in view until the timeline has reached 20 seconds.

 

If I added the code you provided to the image asset tweens, my beer glass would disappear as soon as it had finished animating? but it needs to stay on screen.

 

this is why I need to have the oncomplete calls on the Timeline and somehow set each timeline asset/tween to opacity 0.

 

Can this be done?

 

EDIT - out of interest, I added in onComplete calls to the tweens, as you suggested, but they don't seem to work. They fire (confirmed using console logging) but setting opacity to 0 doesn't work? "TweenMax.to( element, { opacity: 0 } );" should work?

Link to comment
Share on other sites

Fixed it, with a rough-around-the-edges approach!

 

I made an array of the image asset names (used for the ID's of the div element)

 

Then, I passed that array to the onCompleteParams.

 

In the onComplete function, I loop through the array and tween to opacity 0. Works like a charm, if a little long winded/hack-n-slash haha!

Link to comment
Share on other sites

Hi glad you got it working.

 

Considering your previous post:

Unfortunately, I cannot use the onComplete calls on the separate tweens. this is because I need the tweened element, e.g. the beer glass, to stay in view until the timeline has reached 20 seconds.

 

If I added the code you provided to the image asset tweens, my beer glass would disappear as soon as it had finished animating? but it needs to stay on screen.

 

this is why I need to have the oncomplete calls on the Timeline and somehow set each timeline asset/tween to opacity 0.

 

Can this be done?

 

EDIT - out of interest, I added in onComplete calls to the tweens, as you suggested, but they don't seem to work. They fire (confirmed using console logging) but setting opacity to 0 doesn't work? "TweenMax.to( element, { opacity: 0 } );" should work?

If you need the timeline to reach 20 seconds add a callback to it at the 20 second mark with the call() method, like this:

tl.call(setOpacity, [glass1, glass2], 20);//appends it at exactly 20 seconds into the timeline (absolute position)

Like that you're passing an array of the glasses to the function and you avoid the extra code.

 

Hope this helps,

Cheers,

Rodrigo.

Link to comment
Share on other sites

Rodrigo has clearly been offering some really solid advice. 

 

I've been loosely following the twists and turns of this conversation, and there may be another way to get the items to all fade out when the main timeline reaches 20 seconds.

 

When you are looping through each item that needs to have its own timeline of animation built you could also add the fade-out animation of that item to the main timeline at a time of 20 seconds

 

real rough fake code:

var mainTimeline = new TimelineLite();

for (i = 0, i<items, i++) {
  var itemTimeline = new TimelineLite();
  itemTimeline.to ( item, 1, {left:200})
  .to(item, 1, {rotation:180})
  
 //add itemTimeline to mainTimeline at an absolute time
 mainTimeline.add( itemTimeline, i * itemTimeline.duration() )

  //add fade out of item at 20 seconds into mainTimeline
 mainTimeline.to(item, 1, {opacity:0}, 20);

}
  • Like 1
Link to comment
Share on other sites

Yea, there has been some awesome insight on how to do things with GSAP. As you can tell, this project is a bit crazy!

 

I think I have stumbled onto another issue though.

 

So I have 2 animations, with 2 images each.

 

In my animation data, the pint glass has a wait/delay duration of 0, as does the background. However, the glass is only animating after the background has finished animating.

 

the timeline for each animation doesn't have an align override, so it should be using "normal" which honours delays, of which are all 0.

 

Any idea why my images are tweening at the same time?

 

EDIT - typicaly, I think I figured it out not long after posting :D I was adding the tweens one after the other to the timeline, with seperate .add() calls, so I assume that was making them play in order. I made a new array, added the tweens to the array, then created the objectTimeline after the loop, and used the "tweens" property.

 

Seems to work now with the delays in place. Although I get an odd behaviour when I have the animation paused initially, just the pint glass animates, nothing else (nothing should animate at all!) Weird!?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×