Jump to content
GreenSock

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

Tweeing, Delays, Overwrites and insanity... [SOLVED]

Recommended Posts

Hi guys. Thanks GS for the great classes! I'm just trying them out for the first time.

 

Wouldn't you know it, I've already run into a snag. Let me try to summarize what I'm doing:

 

A button is set to "pulse" at regular intervals. The pulse is achieved by tweeing the scaleX and scaleY properties of the object. Pretty straightforward stuff really. The only complication is that the user can also "make" the button pulse by rolling over it and rolling off it.

 

To do the "pulse", I'm using a pair of TweenLite tweens with a delay between them that's exactly the length of time it takes the first tween to complete - one to grow it to its max size, the other to shrink it back to 100% original size. The reason I'm doing this in 2 steps rather than using Bounce is unclear, other than that I suspected that when the user needs to "take over" the pulse manually by rolling over the object, I wanted to be able to override the current Tween and just set up the new tween to match the rollover / rollout action.

 

so we have something like:

TweenLite.to(myButton, tweenLength, {scaleX:1.5, scaleY:1.5, overwrite:1});
TweenLite.to(myButton, tweenLength, {scaleX:1, scaleY:1, delay:tweenLength, overwrite:0});

 

This works like a charm. My button is happily pulsing. I set up a timer after this which waits for a few seconds, then starts the pulse over again, so there's a few second delay between "pulses". If I understand what's happening here, you're basically holding on to TWO tween objects that could theoretically conflict (because of overwrite:0) but because of the delay, the conflict never occurs. Dandy. Am I right so far?

 

Now, later on in the code, we define the rollover and rollout event handlers. This is where the weirdness creeps in. So my rollover looks like this:

TweenLite.to(myButton, tweenLength, {scaleX:1.5, scaleY:1.5, overwrite:1});

 

and my rollout looks like this:

TweenLite.to(myButton, tweenLength, {scaleX:1, scaleY:1, overwrite:1});

 

Now the way I see this is that either of these two tweens should COMPLETELY REPLACE any and all of the vars associated with the previous tweens that were set up in the pulse. Including the delay. At least that's how I read overwrite:1 (ALL) - completely kill the previous tween on that button (delete the TweenLite object) and then create a brand new one with its own brand-new set of instance variables.

 

Well here's the weird part: the rollover tweens have the delay in them now!

 

If the user rolls over the buttons BEFORE the first pulse ever occurs, there's no delay whatsoever, and everything works as God intended. BUT, if the user waits for a pulse, the next time he rolls over or rolls off the button, there's a delay that's exactly the same length as the delay I set up in the second pulse tween.

 

Now why should this be? First of all, I'm surprised that the delay variable even carries over from one TweenLite object to the next; second, I'm shocked that overwrite:1 doesn't, in fact, overwrite delay!

 

I was able to solve the problem by explicitly specifying delay:0 whenever I don't want a delay, but it seems to me that I should not have to do this. At least, according to how I interpret the documentation.

 

Any insights or thoughts on the matter would be helpful.

 

Tom

Link to comment
Share on other sites

Could you post an example of the broken version? I think there must be something else going on - the delay never carries over from one tween to another, and you're right - the overwrite:true will definitely kill all existing tweens of that object. However, you mentioned a timer that you set up that triggers a new pulse every so often - I wonder if that's causing the problems? TweenLite cannot know that your Timer is going to call a function that triggers new TweenLite instances (preemptively overwriting tweens that don't exist yet).

 

Oh, and for the pulse effect, you might want to try using combining the new "repeat", "repeatDelay", and "yoyo" properties of TweenMax v11 because you could get a similar effect with one line of code that'd replace all your timer stuff and the two other tweens. Like:

 

TweenMax.to(mc, 1, {scaleX:1.5, scaleY:1.5, repeat:-1, repeatDelay:0.5, yoyo:true});

 

http://blog.greensock.com/v11beta/

Link to comment
Share on other sites

Hey thanks for the quick reply. I don't *believe* the timer is causing the conflict, because it's a long delay and I'm testing the rollovers well within that delay tolerance. Regardless, here are the relevant pieces of code for you to peruse:

 

Setting up some constants:

private static const PULSE_TIMER_DELAY:Number = 5 * 1000;
private static const PULSE_REPEAT_DELAY:Number = 3 * 1000;
private static const PULSE_ITEM_DELAY_SECONDS:Number = .175;

// rollover and tween values - I'm putting them up-front here just to make it easier for my guys to tweak the values without digging deeply into the code
private static const TWEEN_FRAMES:Number = 8;
private var navTweenStates:Object = {
over: {
	alpha: .6,
	scaleX: 1.1,
	scaleY: 1.1,
	ease:Sine.easeOut
},
out: {
	alpha: 1,
	scaleX: 1,
	scaleY: 1,
	ease:Sine.easeIn
}
}

private var pulseTimer:Timer; // this will hold my Timer object to add a nice juicy delay to the timer

 

Then we initialize the view, and get a reference to the appropriate movieClip (navMC). navMC is a MovieClip that contains 3 buttons. We get references to those three buttons and they are passed to the TweenLite constructor (well, your .to static function cause I don't want to feel obliged to hold onto a reference to the TweenLite object! haha)

 

The "pulse" code

private function startPulseTimer() {
pulseTimer.start();
}

private function resetPulseTimer() {
pulseTimer.delay = PULSE_TIMER_DELAY; // this sets up the initial delay before the first "pulse"
pulseTimer.repeatCount = 1;
pulseTimer.reset();
}

private function stopPulseTimer() {
pulseTimer.stop();
}


private function doPulse(event:TimerEvent) {
for (var i = 1; i < 3; ++i) { // cause there are 3 buttons that have to pulse
	var navItem = getNavItem(i);

	var tweenObj = navTweenStates["over"];
	tweenObj.overwrite = 1; // just to be safe. I realize that 1 is the default since I'm not initing overwriteManager, but hey, things got weird back there...
	TweenLite.to(navItem, tweenTime, tweenObj);

	tweenObj = navTweenStates["out"];
	tweenObj.delay = tweenTime; // this is the delay to get the second pulse to follow the first, since I couldn't find a "back and forth" tween class - though now I'm aware there is one: thanks!
	tweenObj.overwrite = 0;
	TweenLite.to(navItem, tweenTime, tweenObj);
}

pulseTimer.delay = PULSE_REPEAT_DELAY; // this is a shorter delay between subsequent "pulses"
pulseTimer.repeatCount = 1;
pulseTimer.reset(); // probably redundant here but I'm not taking any chances!
pulseTimer.start();
}

 

Now the reason I set up this timer will hopefully be more clear - because there's this long initial delay, then smaller delays between subsequent pulses. But the pulsing is turned off as soon as the user interacts with the buttons. Once the user has stopped interacting, the long delay starts over, and then after the first pulse, goes back to the shorter delay.

 

Here's the mouseover stuff:

override protected function navButtonOver(event:MouseEvent) {
	stopPulseTimer();

	var tweenObj = navTweenStates.over;
	tweenObj.overwrite = 3;
	tweenObj.delay = 0;
	TweenLite.to(event.currentTarget, tweenTime, tweenObj);
}

override protected function navButtonOut(event:MouseEvent) {
	resetPulseTimer();
	startPulseTimer();

	var tweenObj = navTweenStates.out;
	tweenObj.overwrite = 3;
	tweenObj.delay = 0; // if I don't include this parameter at all, I see a delay when the user rolls the mouse out! Do note that at this point, the timer is stopped and there is no pulsing tweens going on anywhere else.
	TweenLite.to(event.currentTarget, tweenTime, tweenObj);
}

 

What do you think? Without resorting to TweenMax, where do you think this issue is coming from? I'd like to solve it from m y code first, before you show my some funky new-fangled way of doing things, just because I want to understand the error first.

 

Thanks so much,

 

T

Link to comment
Share on other sites

Yep, I see the problem. You set up 2 vars objects and then you reuse them. That's fine, but you keep changing the "delay" property of each one. So you changed it to a non-zero value, and then when you used that same vars object again and didn't alter the delay property, you probably thought it was zero, but it wasn't - you had set it to something else previously. That's why it worked fine when you made sure to specifically set it to zero.

 

Also, you said you're not initing OverwriteManager but you used an overwrite mode of 3 on one of the tweens - that won't work unless OverwriteManager is initted.

 

Make sense now? If something isn't working as expected, feel free ask. There's no voodoo magic going on in there that would cause strange behavior, requiring you to set overwrite values in strange places :). It should prove to be rock solid. No known bugs whatsoever. And I know a lot of people that kick the snot out of this little engine and rely on it heavily with zero problems. (not to imply there are never bugs - just saying you can generally trust it to do its job very well even under tons of stress)

 

Happy tweening!

Link to comment
Share on other sites

Hey GS, thanks for spending the time to look into this and explain it to me.

 

I did a little more digging and the conceptual mistake I was making was thinking I wouldn't get variable polution by using the object variables to pass parameters to TweenLite.

 

What had me particularly confused is the fact that these tweenObj objects are scoped completely local to the functions that create them. There is no reason that they ought to persist beyond the scope of the calling function. So when I create this object, and pass it to TweenLite, as soon as the function exits, that object is destroyed. The next time I use a similar construct and pass that to TweenLite, that has no relevance or "memory" of the previous time I used the same variable name to construct that object.

 

But the issue isn't these two locally scoped variables at all. The issue is that when I create them, I take a copy BY REFERENCE of the original definition object. Which means that when I alter my (temporary) local variable, I'M ALSO ALTERING THE ORIGINAL OBJECT. DOH! So when I create the next variable and take another copy by reference, the new vars object actually has the danged data from the previous time I (unwittingly) over-wrote the source object.

 

It's a pass-by-reference vs. deep copy issue, and now that I've figured that out with your help, it should be a simple matter to solve.

 

But, since this was a learning experience for me, I figure I should give back, so here's an example that illustrates the heart of the problem - it's not a local scope issue - you have to go farther back up the food chain to see where the problem is.

 

package {

import flash.display.MovieClip;
import flash.utils.ByteArray;

public class FunctionScope extends MovieClip {

	private static const sourceObj:Object = {
		a: "Aye",
		b: "Bee"
	}

	public function FunctionScope(){
		functionOne();
		functionTwo();
		traceObj(sourceObj);
		functionThree();
		traceObj(sourceObj);
	}

	private function functionOne():void {
		var myObj:Object = sourceObj;
		myObj.c = "Cee";
		traceObj(myObj);
	}

	private function functionTwo():void {
		var myObj:Object = sourceObj;
		myObj.d = "Dee";
		traceObj(myObj);
	}

	private function functionThree():void {
		var myObj:Object = clone(sourceObj);
		myObj.e = "Eew";
		traceObj(myObj);
	}

	private function traceObj(obj:Object):void {
		trace("Tracing: " + obj);
		for (var i in obj){
			trace(i + ":" + obj[i]);
		}
	}

	private function clone(obj:Object):Object {
		var temp:ByteArray = new ByteArray();
		temp.writeObject(obj);
		temp.position = 0;
		return temp.readObject();
	}
}
}

 

The above code traces:

Tracing: [object Object]
b:Bee
d:Dee
a:Aye
c:Cee
Tracing: [object Object]
b:Bee
d:Dee
a:Aye
c:Cee
Tracing: [object Object]
b:Bee
d:Dee
a:Aye
c:Cee
e:Eew
Tracing: [object Object]
b:Bee
d:Dee
a:Aye
c:Cee

 

Thanks again GS for your time and for getting me thinking about the variables themselves. I looked at your response and said - that's not possible! But obviously I needed to see for myself so I set up the above test and saw with my own eyes that the original values were changing. Bloody hell. Where's the damned "dereference" operator when you need it? my $newCopy = $$oldCopyReference; # arrggghhhh - old Perl-head.

Link to comment
Share on other sites

Implementing deep copy in this scenario seems like a bit of a kludge. Can you think of a more elegant way to add these parameters to the TweenLite.to() method?

 

The objective is to have, at the head of my script, an easy way to manipulate the basic parameters that will be tweened, but somehow allow me to add additional parameters to the .to() method as needed?

 

TweenLite.to(myClip, duration, myTweenParameters, tweenLiteReservedParameters).

 

Is there some basic way to concatenate two objects (anonymously)?

 

T

Link to comment
Share on other sites

Well, I'm not generally a fan of that style of creating tweens because it's a bit clunky and slow. Iterating through the properties of an object and copying them to a new object is...well...slow. If you want to create some general styles that can be overridden when necessary, you could just set up some functions that accept parameters for stuff that might change, kinda like:

 

function navOver(target:Object, duration:Number, delay:Number=0, overwrite:uint=1):TweenLite {
   return new TweenLite(target, duration, {alpha:0.6, scaleX:1.1, scaleY:1.1, ease:Sine.easeOut, delay:delay, overwrite:overwrite});
}
function navOut(target:Object, duration:Number, delay:Number=0, overwrite:uint=1):TweenLite {
   return new TweenLite(target, duration, {alpha:1, scaleX:1, scaleY:1, ease:Sine.easeIn, delay:delay, overwrite:overwrite});
}

 

Or if you want more flexibility and are willing to give up some speed, you could set up defaults like:

 

public static const NAV_OVER:Object = {alpha:0.6, scaleX:1.1, scaleY:1.1, ease:Sine.easeOut, delay:0, overwrite:1};

function tween(target:Object, duration:Number, style:Object, vars:Object=null):TweenLite {
   if (vars == null) {
       vars = {};
   }
   for (var p:String in style) {
       if (!(p in vars)) {
           vars[p] = style[p];
       }
   }
   return new TweenLite(target, duration, vars);
}

 

Then you could simply pass in the style, like:

tween(mc, 1, NAV_OVER); //uses all defaults
tween(mc2, 1, NAV_OVER, {delay:1, onComplete:myFunction}); //overrides the delay and adds an onComplete

 

Just a few ideas. Hope it helps.

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.
×