Jump to content
Search Community

nested Timelines problem

pbohny test
Moderator Tag

Recommended Posts

Hi Jack

 

I am attaching a simple project:

 

import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;

TweenPlugin.activate([AutoAlphaPlugin]);

var timeline:TimelineMax = new TimelineMax({paused:true});

init();

function init():void{	
//drawLine();
drawBox();	
drawBox2();	
play_btn.addEventListener(MouseEvent.CLICK, playHandler)
stop_btn.addEventListener(MouseEvent.CLICK, stopHandler)
reverse_btn.addEventListener(MouseEvent.CLICK, reverseHandler)
}

function drawBox():void{
var test:TestBox = new TestBox();
test.visible = false;
addChild(test);
timeline.insert(test.animateIn(),0);
}

function drawBox2():void{
var test2:TestBox = new TestBox();
test2.visible = false;
addChild(test2);
timeline.insert(test2.animateIn(),2);
}

function drawLine():void{
var line:DrawLines = new DrawLines();
addChild(line);
timeline.insert(line.drawLine(),2);
}



function playHandler(event):void{
timeline.play()
}

function stopHandler(event):void{
timeline.stop()
}

function reverseHandler(event):void{
timeline.reverse()
}

 

The 2 TestBox instances work as expected, but when I uncomment drawline() function, drawline works fine but the other two timelines won't play anymore, they only show the last frame. With only the drawline() function active, it works as expected. How can I use the DrawLine Class correctly, so all three nested timeline play accordingly?

Link to comment
Share on other sites

I have this same problem.

 

If Timelines are instantiated with the paused option set to true, then nested inside another Timeline instance, calling play() on the outer timeline will not cause all nested Timelines to play.

 

I have this in a project in which I am generating several instances of an object and am using a Timeline to stagger their build.

 

public class QButton extends MovieClip {
 private var introAnim:TimelineMax;
 private var outroAnim:TimelineMax;

 public function QButton():void {
   introAnim = new TimelineMax();
   introAnim.insert(TweenMax.from(this, 0.6, {scaleX:2, scaleY:2, alpha:0, blurFilter:{blurX:16, blurY:16}, ease:Circ.easeOut}));
   outroAnim = new TimelineMax({paused:true, onComplete:destroy});
   outroAnim.insert(new TweenMax(this, 1, {scaleX:0.3, scaleY:0.3, autoAlpha:0, blurFilter:{blurX:16, blurY:16}, ease:Circ.easeIn}));
 }
 public function getIntroAnim():TimelineMax {
   return this.introAnim;
 }
 public function getOutroAnim():TimelineMax {
   return this.outroAnim;
 }
 // etc.
}

 

Then I have a Class for producing and nicely displaying multiple buttons with staggered intro and outro animations.

 

public class MultiQuestion {

	private var buttonArray:Array;
	private var inTimeline:TimelineMax;
	private var outTimeline:TimelineMax;
	private var stageRef:MovieClip;

	public function MultiQuestion(qArray:Array, stage:MovieClip) {
		var yPos:int = 70;
		var buildArray:Array = new Array();
		var breakdownArray:Array = new Array();
		this.buttonArray = new Array();
		this.inTimeline = new TimelineMax();
		this.outTimeline = new TimelineMax({paused:true});
		this.stageRef = stage;

		for each (var o:Object in qArray) {
			var newButton:QButton = new QButton();
			buildArray.push(newButton.getIntroAnim());
			breakdownArray.push(newButton.getOutroAnim());
			buttonArray.push(newButton);
			this.stageRef.addChild(newButton);
		}
		this.inTimeline.insertMultiple(buildArray, null, null, 0.1);
		this.outTimeline.insertMultiple(breakdownArray, null, null, 0.1);
	}
	private function cleanUp(event:MouseEvent):void {
		// Doesn't play
		this.outTimeline.play();
	}
}

 

I've removed a lot of button functionality for clarity, but the issue here is that the inTimeline plays fine, because the paused state is not set, while the outTimeline will never play, despite being called in the full code (where cleanUp() is called on click of any one of the buttons).

 

Apologies if it's not clear from this example, but I assume the issue is that a call to play() does not cascade to any child Timeline instances. I suppose a way around it in this case would be to generate outro animations only when needed, though I can only hope that I can separate concern in the same way while allowing the staggering for multiple instances.

Link to comment
Share on other sites

If Timelines are instantiated with the paused option set to true, then nested inside another Timeline instance, calling play() on the outer timeline will not cause all nested Timelines to play.

 

This is by design - it is not a bug. When you pause a tween or timeline, it remains paused until you unpause it. Otherwise, it would cause problems. For example, what if you put a tween into a timeline and want to pause it based on some user interactivity but the rest of the timeline should play? If playing a parent always forced all its children to play as well, it would lead to some frustrating behavior. Don't pause a tween/timeline if you don't want it to be paused or just make sure you unpause it when you want it to be able to play. TweenLite/Max/TimelineLite/Max respects your instructions in terms of the playability (is that a word?) of your tweens/timelines, as it should.

 

See what I mean?

Link to comment
Share on other sites

The 2 TestBox instances work as expected, but when I uncomment drawline() function, drawline works fine but the other two timelines won't play anymore, they only show the last frame. With only the drawline() function active, it works as expected. How can I use the DrawLine Class correctly, so all three nested timeline play accordingly?

 

That was rather tricky to debug, but I found the problem in your code. It had to do with the fact that you were setting the visible property to false on test and test2 initially, and then in your animateIn() tween, you were trying to tween the "visible" property to true. However, you never activated the VisiblePlugin! In this situation, the WRONG functionality is what you were wanting. See, when you tween "visible", it's supposed to just change it at the END of the tween. In your code, though, since you didn't activate the plugin, TweenLite didn't recognize "visible" as a special property so it treated it like any other numeric property (as it should). Flash interpreted the Boolean false as 0 and true as 1, so as soon as it started tweening, it made visible true because 0.001 (or any number other than 0) is converted to true by Flash as far as Boolean values are concerned.

 

Then inside DrawLines, you used TweenMax to do your tweening. The nice thing about TweenMax is that it activates a bunch of plugins for you - VisiblePlugin is one of them. So as soon as you used DrawLines, it TweenMax got compiled in your swf and it was kind enough to activate the VisiblePlugin which made your "visible" tween function properly (not what you were looking for though). Your tweens were maintaining the original visibility of test and test2 throughout the tween and then toggled it to true at the end of the tweens. So you thought it was breaking your FLA when it was actually making it work properly :)

 

You're welcome to add a super-quick tween of the visible property at the beginning of your TimelineLite in animateIn() so that your object becomes visible quickly at the start. The duration of that tween could be 0.001.

 

Phew!

 

Okay, so do you understand the problem now?

Link to comment
Share on other sites

...a Phew back from me!

 

This could be my day, Switzerland has just beaten Spain at the World Cup (Soccer for all US friends) and there might be a solution to my problem. I have not yet had the time to really understand your response and to correct my code, but I am once more impressed by the brains and the effort you put into your support.

 

As I am more of a designer than a coder, I must blame you for tearing me into your excellent set of Classes. I am doing things I never was dreaming of. I am worried I have gone to far. If you say it was "tricky", how could I ever understand it? I will try tommorow. You would not be able to post my project back, worrking with you corrections?

  • Like 1
Link to comment
Share on other sites

This is by design - it is not a bug. When you pause a tween or timeline, it remains paused until you unpause it. Otherwise, it would cause problems. For example, what if you put a tween into a timeline and want to pause it based on some user interactivity but the rest of the timeline should play? If playing a parent always forced all its children to play as well, it would lead to some frustrating behavior. Don't pause a tween/timeline if you don't want it to be paused or just make sure you unpause it when you want it to be able to play. TweenLite/Max/TimelineLite/Max respects your instructions in terms of the playability (is that a word?) of your tweens/timelines, as it should.

 

See what I mean?

 

I can understand your reasoning, so perhaps I am missing the control methods that allow pausing and playing of nested TimelineMax/Lite instances. Or perhaps there is a getter or publicly accessible array of instances?

 

The way around the issue in my case was to use the "getter" as an instantiator for Timeline objects and immediately insert the generated array into a paused Timeline. Where this project needs a good separation of concern, the code is probably more macaroni than is typical, and I am therefore wondering about the Flash handling of events with regards to frame progression. My hope is that before the frames of animation for a TweenMax/Lite/Nano instance can begin, all traversal of code is completed, allowing a controller to receive all relevant instances and prevent them from proceeding. I admit I am not intimately familiar with the way Flash handles frame sensitive execution of code, so I cannot speak to whether this would be a problem.

 

Edit: Sure enough, there's a getter - getChildren() :)

 

I'll probably write a cascading play() method anyway.

Link to comment
Share on other sites

There is one last problem I have with this example and I just can't figure it out.

 

Playing, stopping and reversing the timeline works fine for the boxes, but the Drawlines instances won't reverse. Why is that?

Link to comment
Share on other sites

Playing, stopping and reversing the timeline works fine for the boxes, but the Drawlines instances won't reverse. Why is that?

 

The problem has to do with the fact that your code simply uses lineTo() to progressively draw the line, but you never erase any of the line that is already drawn. See what I mean? So the timeline is reversing fine - you just don't SEE that because your code doesn't accommodate backing up (clearing the previously drawn line segments).

 

The same problem with stoping the timeline, while the boxes pause, the drawline continues. Where do I oversee the cause?

 

If drawline is continuing, you must not have stopped that tween/timeline. When a parent TimelineLite/Max is stopped/paused, so are all of its children/descendants.

Link to comment
Share on other sites

  • 1 month later...

Hi Guys and the ever amazing Captain GreenSock,

 

I have read through this thread and completely understand why you've set timelines to be paused independantly when they are nested. But therein lies my problem! Im trying to make a system where the user is presented with a series of checkboxes at the start that allow them to switch on/off the parts of the timeline, and it will then dynamically create a timeline, playing only what they selected.

 

So for example if the user wants to see Items 1, 3 and 7; a mainTimeline is created that nests and plays timeline1-timeline3-timeline7, and so on and so forth for any other custom combinations they might need.

 

The reason im wanting to do this is because i need to include the ability to scrub through the entire presentation using tweenTo, but need it to scrub through only what they chose to see, and the others to remain hidden. So for example if they are watching 1, 3 and 7 and choose to scrub back to the start, it only goes back through 7, 3 and then to 1. (1,2,4,5,6 are hidden)

 

At the moment thats not working the way im doing it, and i understand why. I dont want the individual timelines to play until its their turn, so i have to set them to {paused:true}. And when i play the mainTimeline they remain paused. Makes perfect sense but i cant think of a workaround that would allow me to play them in order. In my sample file, im passing which timelines need to be loaded into an array, then checking the array in a gateway once they press the play button, nesting the chosen ones inside the main timeline. Im sure there is an easier way to do this, but cant seem to get it.

 

Im hoping that makes sense! Im a little new to AS3, so my coding probably completely sucks, but if you could please take a look and tell me where im going wrong i would be forever grateful! Feel free to completely tear apart any of my coding, id love some constructive criticism.

 

DynamicTimeline.zip

 

package {

import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.*;
import com.greensock.*;
import com.greensock.easing.*;

public class Main extends MovieClip {
	var timelineArray:Array = new Array(3);
	var mainTimeline:TimelineMax = new TimelineMax({});
	var timeline1:TimelineMax = new TimelineMax({paused:true});
	var timeline2:TimelineMax = new TimelineMax({paused:true});
	var timeline3:TimelineMax = new TimelineMax({paused:true});

	public function Main() {
		this.init();
	}

	private function init(){
		setupTimelines();
		trace("Lets do arrays")
		addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
	}

	function setupTimelines(){
		timeline1.append(TweenLite.to(button1, 1, {rotation:"20"}) );
		timeline2.append(TweenLite.to(button2, 1, {rotation:"-20"}) );
		timeline3.append(TweenLite.to(button3, 1, {rotation:"52"}) );
	}

	function onMouseDownHandler(event:MouseEvent){
		if (event.target == button1){
			timelineArray[0] = "1enabled";
		}
		if (event.target == button2){
			timelineArray[1] = "2enabled";
		}
		if (event.target == button3){
			timelineArray[2] = "3enabled";
		}
		if (event.target == playtimelineButton){
			timelineGateway();
		}

		trace(timelineArray)
	}

	function timelineGateway(){
		if(timelineArray[0] == "1enabled"){
				mainTimeline.append(timeline1);
			}
		if(timelineArray[1] == "2enabled"){
				mainTimeline.append(timeline2);
			}
		if(timelineArray[2] == "3enabled"){
				mainTimeline.append(timeline3);
			}
			mainTimeline.play()
	}
}
}

Link to comment
Share on other sites

Hi Guys and the ever amazing Captain GreenSock,

 

I have read through this thread and completely understand why you've set timelines to be paused independantly when they are nested. But therein lies my problem! Im trying to make a system where the user is presented with a series of checkboxes at the start that allow them to switch on/off the parts of the timeline, and it will then dynamically create a timeline, playing only what they selected.

 

So for example if the user wants to see Items 1, 3 and 7; a mainTimeline is created that nests and plays timeline1-timeline3-timeline7, and so on and so forth for any other custom combinations they might need.

 

The reason im wanting to do this is because i need to include the ability to scrub through the entire presentation using tweenTo, but need it to scrub through only what they chose to see, and the others to remain hidden. So for example if they are watching 1, 3 and 7 and choose to scrub back to the start, it only goes back through 7, 3 and then to 1. (1,2,4,5,6 are hidden)

 

At the moment thats not working the way im doing it, and i understand why. I dont want the individual timelines to play until its their turn, so i have to set them to {paused:true}. And when i play the mainTimeline they remain paused. Makes perfect sense but i cant think of a workaround that would allow me to play them in order. In my sample file, im passing which timelines need to be loaded into an array, then checking the array in a gateway once they press the play button, nesting the chosen ones inside the main timeline. Im sure there is an easier way to do this, but cant seem to get it.

 

Im hoping that makes sense! Im a little new to AS3, so my coding probably completely sucks, but if you could please take a look and tell me where im going wrong i would be forever grateful! Feel free to completely tear apart any of my coding, id love some constructive criticism.

 

[attachment=0]DynamicTimeline.zip[/attachment]

 

package {

import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.*;
import com.greensock.*;
import com.greensock.easing.*;

public class Main extends MovieClip {
	var timelineArray:Array = new Array(3);
	var mainTimeline:TimelineMax = new TimelineMax({});
	var timeline1:TimelineMax = new TimelineMax({paused:true});
	var timeline2:TimelineMax = new TimelineMax({paused:true});
	var timeline3:TimelineMax = new TimelineMax({paused:true});

	public function Main() {
		this.init();
	}

	private function init(){
		setupTimelines();
		trace("Lets do arrays")
		addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
	}

	function setupTimelines(){
		timeline1.append(TweenLite.to(button1, 1, {rotation:"20"}) );
		timeline2.append(TweenLite.to(button2, 1, {rotation:"-20"}) );
		timeline3.append(TweenLite.to(button3, 1, {rotation:"52"}) );
	}

	function onMouseDownHandler(event:MouseEvent){
		if (event.target == button1){
			timelineArray[0] = "1enabled";
		}
		if (event.target == button2){
			timelineArray[1] = "2enabled";
		}
		if (event.target == button3){
			timelineArray[2] = "3enabled";
		}
		if (event.target == playtimelineButton){
			timelineGateway();
		}

		trace(timelineArray)
	}

	function timelineGateway(){
		if(timelineArray[0] == "1enabled"){
				mainTimeline.append(timeline1);
			}
		if(timelineArray[1] == "2enabled"){
				mainTimeline.append(timeline2);
			}
		if(timelineArray[2] == "3enabled"){
				mainTimeline.append(timeline3);
			}
			mainTimeline.play()
	}
}
}

Link to comment
Share on other sites

Yeah, I think you're overcomplicating things a bit. You could use a Dictionary to store the selections based on the actual button instances and then create a new TimelineLite each time the playtimelineButton is clicked based on those selections, like this:

 

public class Main extends MovieClip {
 var timeline:TimelineLite;
 var selections:Dictionary = new Dictionary();

 public function Main() {
 this.init();
 }

 private function init():void {
 addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
 }

 function onMouseDownHandler(event:MouseEvent):void {
 if (event.target == playtimelineButton){
	timelineGateway();
 } else {
	selections[event.target] = true; 
 }
 }

 function timelineGateway():void {
  timeline = new TimelineLite();
  if (selections[button1] == true) {
	  timeline.append( TweenLite.to(button1, 1, {rotation:"20"}) );
  } 
  if (selections[button2] == true) {
	  timeline.append( TweenLite.to(button2, 1, {rotation:"-20"}) );
  }
  if (selections[button3] == true) {
	  timeline.append( TweenLite.to(button3, 1, {rotation:"52"}) );
  }
 }
}

Link to comment
Share on other sites

You guys are absolute heroes!

 

Id never even heard of Dictionary before, thanks for introducing me it seems very handy! Absolutely changes the way i think about things.

 

Now i have clean, efficient and working code! Thanks so much Greensock. You are amazing.

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.
×
×
  • Create New...