Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
mrkos67

Nested timelines don't follow the parent timeline's playback

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've created a modular media player, where add-ons and modules are created and instantiated at run-time. One of these modules is a BannerBar which creates and loads animated banners into their own separate iframes. Each banner has a public access to a timeline variable, which is paused by default. Everything is good here, banners get loaded, are displayed at the correct times (by controlling the display [block/none] of the iframe itself). 

Now, in the BannerBar file, I'm adding the timelines of each banner onto the BannerBar's timeline; however, the banners' timelines don't follow the playback of the BannerBar's timeline playback when I unpause them on add. This is the code for looping through my data file, retrieving banner info, loading them into a templatized iframe code, initing once loaded, and adding timelines to the BannerBar's timeline:

// load/init banners into DOM
var changeBannerTime = _data.times.start;
for( var i = 0; i < _data.banners.length; i++ )
{
	var b = _data.banners[i];

	// validate id, src, and duplicate banner object
	if( b.id && b.src && _dctBanners[b.id] == null )
	{
		// data validation
		b.time = parseFloat( b.time ) || 4;
		b.href = b.href || "#";
		b.data = b.data || {};

		// create iframe, attach to DOM
		var htmlIFrame = _tplIFrame;
		var $iframe = $( htmlIFrame );
		$iframe.attr( "id", "bannerbar-id-" + b.id );
		_$slider.append( $iframe );

		// banner management
		_dctBanners[b.id] = b;
		_dctIFrames[b.id] = $iframe;
		hideBanner( b.id );

		// load iframe, wait for load, call init
		$iframe.attr( "src", b.src ).on( "load", ( function( b, t ) { 
			console.log( "bannerBar :: onLoad() - id:" + b.id );
			return( function() {
				try { 
					var banner = this.contentWindow.banner;

					// init the banner
					banner.init( b.data ); 

					console.log( "  id:" + b.id + " timeline added at:" + t );
					// add banner timeline to main timeline for playback control
					_timeline.add( banner.timeline.play(), t );
				}
				catch( e ) { }
			})
		})( b, changeBannerTime ) );


		// set up hide/show times
		_timeline.addLabel( b.id, changeBannerTime-.01 );
		_timeline.addCallback( showBanner, changeBannerTime, [ b.id ] );
		_timeline.addCallback( hideBanner, changeBannerTime + b.time, [ b.id ] );

		changeBannerTime += b.time;
	}
}

All of the banners' timelines start playing immediately even though the BannerBar's timeline is paused. Also, when I pause/play or seek the BannerBar's timeline, the banners timelines don't play pause/play/seek as they should.
 
I've tried adding the paused banner timelines to the BannerBar's timeline, but they remain paused, as expected.
 
You can see it in action here: http://dev.option5.net/CNCT/CNCT15001/video.html
 
The BannerBar slides in at :02s, but the first banner is already playing (it should start at :02s too, presumably). The second banner has some animation which should start at :14s when it appears, but it's already done playing by the time it appears.
 

 

Link to post
Share on other sites

Sorry, I tried looking at your files and its just way too much.  There is no practical way for us to debug JavaScript spread across 3 files. It seems that the core issue is very basic: a child timeline not honoring the paused state of the parent. If you can create a reduced test case that replicates this behavior we will be happy to help. We don't need to see any code that is dynamic or inherits properties from other objects... just a very basic simulation of the problem. Thanks.

 

http://greensock.com/forums/topic/9002-read-this-first-how-to-create-a-codepen-demo/

  • Like 1
Link to post
Share on other sites

the try/catch block was throwing an error on the line: _timeline.add( banner.timeline.play(), t );

 

the error is: "Cannot add [object Object] into the timeline; it is not a tween, timeline, function, or string."

 

So it seems the return value of banner.timeline isn't coming back as a readable TimelineMax instance (which might be because of the iframe?). How do I make sure it's a valid timeline instance? Or is there a way to clone based on a timeline/object?

Link to post
Share on other sites

it is indeed related to using the timeline across the iframe.

 

i created a quick sample where a simple timeline is created for each banner item on the same page, and it works. but the timelines created on the other pages getting loaded into the iframe don't work, throwing the error above. 

 

hmm. anybody know a way around this?

Link to post
Share on other sites

Are you saying that you're trying to take animations that are created in completely different iframes/contexts and drop them into one timeline? Got a simplified reduced test case that you could zip and upload here (click the "more reply options" to get to a screen where you can attach a zip)?

Link to post
Share on other sites

I figured out some more... (for reference, page A contains the iframe which loads in page B. )

 

It's an issue with page B loading up a new instance of the GS libraries, and those aren't the same as page A's GS libraries. When page B creates a timeline based on parent.TimelineMax (page A's), it works as it should. 

 

I'm assuming each import of the GS libraries are sandboxed in some way? Is there a way to reconcile multiple instances of GS library imports?

Link to post
Share on other sites

The browser itself does some sandboxing of its own. So, for example, if you create an Array in one window/iframe, and then test "if (myArray instanceof Array)" in another, it will fail. Weird, I know. I'm not sure if there's a way around it, but I have a vague idea of something but it'd really help if you could provide a reduced test case that shows the issue very clearly so that I can quickly pop into the file(s) and test. I don't want to risk recreating a whole different scenario that's not really the context in which it's failing for you. 

Link to post
Share on other sites

For good reason, the browser doesn't allow the iframe's content to reach up into the parent and mess with it via JS. But the parent can reach down into the iframe's content. So maybe you can do something like this:

 

In the iframe:

var initted;
function init(TweenMax, TimelineMax) {
    //only init once
    if (!initted) {
        window.TweenMax = TweenMax; //point TweenMax and TimelineMax at whatever is passed in.
        window.TimelineMax = TimelineMax;
        initted = true; 
        return start();
    }
}
function start() {
    //put your tweening code here
    var tl = new TimelineMax( { 
        onStart:    function() { console.log( "testiframe :: timeline started..." ); },
        onComplete: function() { console.log( "testiframe :: timeline complete." ); }
     }).addCallback( function(){}, 4 );
    return tl;
}

window.onload = function() {
    console.log( "testiframe :: loaded" );
    //just a fallback so that this file can run independently (if the parent never calls init())
    TweenLite.delayedCall(0.5, function() {
        init(TweenMax, TimelineMax); 
    });
};

In the parent/container, something like: 

$("#iframe1").attr("src", "testiframe.html").on("load", function() {
  try {
    _timeline.add( this.contentWindow.init(TweenMax, TimelineMax) ); //we're passing this window's TweenMax and TimelineMax instances so that the iframe can use those
  } catch(e){ console.log( "error:" + e ); }
  start(); //or whatever your function is that kicks things off.
});
Link to post
Share on other sites

 

For good reason, the browser doesn't allow the iframe's content to reach up into the parent and mess with it via JS. 

 

It actually does, if Page A's iframe has the appropriate sandbox values (http://www.w3schools.com/tags/att_iframe_sandbox.asp), or in my case, I've left off the sandbox attribute, allowing Page B to do anything it wants inside of Page A.

 

I've figured out that if page B has included the GSAP libs, parent.TweenMax works on its own, cuz parent is equal to the current window. When Page A has Page B inclded in an iframe, Page B's calls to parent.TweenMax correctly calls Page A's instances of the GSAP libs, so object types are retained between the pages. The only problem is if Page A doesn't include the GSAP's libs, which I've got a check for that. 

 

It's not elegant, but it works. So all is good, at least as how I need things to be. 

  • Like 1
Link to post
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.

×