Getting Started With BetweenAS3
by 21 July, 2009 1:00 pm15
There are many popular open source tweening engines in the Flash community today. Numbered among these are Tweener, TweenLite/Tweenmax, Tweensy, and GTween. This is a short look at the capabilities of newcomer to the scene: BetweenAS3. BetweenAS3 is the tweening engine brainchild of Yoshihiro (yossy) Shindo and is available as part of the Spark project (i.e. it is brought to us by one of the same group that brought the world the FLARToolkit for Augmented Reality in Flash and countless other goodies).
Requirements
To go through the code examples presented here, you will, of course, need the BetweenAS3 library added to your class path. The library can be downloaded from the .svn depository here. You’ll notice that there are two branches to the library – one for Flash Player 9 and one for Flash Player 10. Although the syntax for the two branches is the same, I’ve used the Flash Player 10 branch for the examples here. I’ve also written the examples here as a pure Actionscript project, so something that can compile actionscript projects, such as FlashBuilder/Flexbuilder or FlashDevelop plus the Flex SDK is also required. Flash CS4 can also be used if each example class is used as a .fla document class. The included download contains all actionscript files plus the FlashDevelopment project file.
Pre-Requisites
As the name BetweenAS3 implies, all code presented will be in Actionscript 3. While it would be helpful (and is assumed) that the reader has at least a basic working knowledge of Actionscript 3, this isn’t 100% necessary.
Basic Syntax and Functionality
Primarily, BetweenAS3 relies on several static method calls. Each of these static methods will return an instance of an ITween interface. Because of this, BetweenAS3 offers a great deal of flexibility as each of these static methods may nest any number of additional methods thereby building extremely complex tweens from relatively simple syntax.
In its most simple form, a basic BetweenAS3 tween may look like this:
var tween:ITween = BetweenAS3.tween(targetObject, toObject, fromObject, time, easingMethod);
Once created, the tween can be started using the play() method, stopped using the stop() method or controlled with the Movieclip like methods gotoAndPlay(time) or gotoAndStop(time). You can determine the length of the tween in seconds with the duration property, the current time of the tween with the position property and can determine whether or not the tween is playing at any given moment with the aptly named isPlaying property.
Like all good tweening engines, with BetweenAS3 you are able to trigger actionscript methods when the tween starts, stops, updates or completes. Unlike most tweening engines, though, BetweenAS3 offers two ways of doing this. You can either use the standard AS3 event model and listen for a BetweenEvent, or you can use a more AS2 style approach and add callback methods to the tween instance. For example, say you wanted to call the method named tweenDone when a tween completes. You could say:
tween.addEventListener(BetweenEvent.COMPLETE, tweenDone); private function tweenDone(event:BetweenEvent):void { // the tween is complete }
Or, instead, you could say:
tween.onComplete = tweenDone; private function tweenDone():void { // the tween is complete }
Using this latter approach, it is also possible to pass parameters to the callback method by passing an array of parameters to the tween instance’s onCompleteParams property like so:
tween.onComplete = tweenDone; tween.onCompleteParams = ["hello", 3]; private function tweenDone(param1:String, param2:int):void { trace(param1, param2); }
Now that we are armed with some basic knowledge of how BetweenAS3 operates, let’s take a look at some specific examples of this powerful tweening engine in action.
The Basic Tween
Before diving straight into examples, let’s first create a couple simple class files that we can reuse throughout this tutorial. The first thing we’ll want is a target to tween. For this we’ll simply extend the Sprite object and use its graphics property to draw a small grey square. This then is our TweenTarget class:
package { import flash.display.Sprite; /** * Simple DisplayObject to demonstrate BetweenAS3 */ public class TweenTarget extends Sprite { public function TweenTarget() { graphics.beginFill(0x999999); graphics.drawRect(0, 0, 25, 25); graphics.endFill(); } } }
The second bit of code we’ll want to get out of the way is a template that we can use with each example. This class will just create an of a textfield (that simply says to click to start or stop the tween), and a tween instance class property. It will also contain a toggleTween method that, using the methods mentioned above, will allow our ITween instance to start or stop by clicking the stage. Once our template is made, the only things that will change from example to example are the name of the class (unless you’d like to overwrite each example), the initTween method which creates our class property tween instance, perhaps the easing method imports, and possibly a few other small details. The general template, then will look like this:
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import org.libspark.betweenas3.BetweenAS3; import org.libspark.betweenas3.easing.Expo; import org.libspark.betweenas3.tweens.ITween; [SWF(width='540', height='200', backgroundColor='#000000', frameRate='60')] public class TweenExample extends Sprite { private var _tween:ITween; private var _tf:TextField; public function TweenExample() { addEventListener(Event.ADDED_TO_STAGE, init); } private function init(event:Event):void { removeEventListener(Event.ADDED_TO_STAGE, init); initText(); initTween(); stage.addEventListener(MouseEvent.CLICK, toggleTween); } private function initText():void { var fmt:TextFormat = new TextFormat("_sans", 12, 0xFFFFFF); _tf = new TextField(); _tf.selectable = false; _tf.mouseEnabled = false; _tf.autoSize = TextFieldAutoSize.LEFT; _tf.defaultTextFormat = fmt; _tf.text = "Click to start/stop tween."; _tf.x = Math.round(stage.stageWidth * .5 - _tf.width * .5); _tf.y = Math.round(stage.stageHeight - _tf.height - 10); addChild(_tf); } private function initTween():void { // will change from example to example } private function toggleTween(event:MouseEvent):void { // if the tween is playing, stop it if (_tween.isPlaying) { _tween.stop(); } else { // otherwise if the tween is at the end, start it over if (_tween.position == _tween.duration) { _tween.gotoAndPlay(0); // if it's not at the end, simply play the tween } else { _tween.play(); } } } } }
Now that we are armed with these basic items, let’s roll up our sleeves and dig into a concrete example of BetweenAS3 in action. As mentioned earlier, the syntax for creating a basic tween looks like this:
_tween = BetweenAS3.tween(target, toObject, fromObject, time, easing);
In more detail, the target is the obejct whose property or properties we wish to tween, the toObject is an object containing properties and their corresponding ending values. Likewise, the fromObject is an object containing the same properties as the toObject, but with their beginning value. Time is the duration of the tween in seconds, and easing is an IEasing interface instance which we can get from the org.libspark.betweenas3.easing package. In our initTween method, then, let’s create a TweenTarget instance, add it to the display list and use it as the target of our tween instance. So initTween becomes:
private function initTween():void { var targ:TweenTarget = new TweenTarget(); targ.x = 20; targ.y = 60; addChild(targ); _tween = BetweenAS3.tween(targ, { x:500 }, null, 2.0, Expo.easeOut); }
Notice that we passed a null in place of a fromObject. By doing so, we are telling BetweenAS3 to use the tween target’s current properties. We already created the toggleTween method which will play the tween when you click the stage, so go ahead and compile the .swf. You should get something like the example below:
Delaying a Tween
As mentioned, complex tweens can be built with BetweenAS3 by nesting various static methods of the class. So far we have looked at only one of those methods, tween. Let’s look at another, now, delay. Like the tween method, delay returns an instance of an ITween interface. The delay method syntax looks like this:
var tween:ITween = BetweenAS3.delay(ITweenToDelay, delayTime, postDelayTime);
Here, the ITweenToDelay is a tween instance we wish to delay, the delay time is how many seconds the tween should be delayed before the tween starts and the postDelayTime is how many seconds the tween should be delayed before the next tween starts. The postDelayTime defaults to 0.0 and, in this example, we will leave it so.
So, let’s go back to our TweenExample template and change the initTween method to look like this:
private function initTween():void { var targ:TweenTarget = new TweenTarget(); targ.x = 20; targ.y = 60; addChild(targ); var t:ITween = BetweenAS3.tween(targ, { x:500 }, null, 2.0, Expo.easeOut); _tween = BetweenAS3.delay(t, 1.0); }
Now, when we compile our example, we see that when we click on the stage, there is a one second delay before the tween target tweens its way across the stage like below.
Parallel and Serial Tweens
The next static methods of the BetweenAS3 class we’ll consider are parallel and serial. The parallel method lets you run any number of tween instances at the same time. Similarly, the serial method allows you to run any number of tween instances in a row (one after another). Syntactically, both methods are identical, you simply pass any number of ITween instances as parameters.
For our parallel example, let’s change our initTween method to look like this:
private function initTween():void { var targ1:TweenTarget = new TweenTarget(); targ1.x = 20; targ1.y = 25; addChild(targ1); var targ2:TweenTarget = new TweenTarget(); targ2.x = 20; targ2.y = 60; addChild(targ2); var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Expo.easeOut); var t2:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Expo.easeOut); _tween = BetweenAS3.parallel(t1, t2); }
Once compiled, that will look like this:
And for our serial example, try this for the initTween method:
private function initTween():void { var targ1:TweenTarget = new TweenTarget(); targ1.x = 20; targ1.y = 25; addChild(targ1); var targ2:TweenTarget = new TweenTarget(); targ2.x = 20; targ2.y = 60; addChild(targ2); var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut); var t2:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeOut); _tween = BetweenAS3.serial(t1, t2); }
Note that we’ve tried a different easing method here which means a different easing method will need to be imported from the org.libspark.betweenas3.easing package before compiling. Once compiled, though, you’ll see something like this:
At this point it should start to be obvious what was meant when it was stated that very complex tweens could be built up by nesting simpler tweens. For example, you could easily pass two parallel tweens and a delayed tween to the BetweenAS3 serial method and so on.
Reversing and Repeating Tweens
The next two static methods we’ll look at are reverse and repeat. The reverse method, as its name suggests, reverses a tween and takes two parameters, the tween to reverse and a boolean indicating whether or not to reverse the tween’s position. Most often you would simply leave this boolean set to its default state of true. The repeat method, true to it’s name, repeats a specified tween. Repeat accepts two arguments: the tween instance to repeat and the number of times it should repeat.
This time our initTween method will be a little bit trickier and we’ll really start to see the power of nesting BetweenAS3 calls. We’ll begin by creating a basic tween that moves our TweenTarget instance across the stage with a nice elasticity. Nothing new there. Then, though, we’ll create a repeated tween that repeats four times. That repeated tween will be passed an instance of a serial tween that tweens first the basic tween then the reverse of the basic tween. Our initTween method then will look like this:
private function initTween():void { var targ1:TweenTarget = new TweenTarget(); targ1.x = 20; targ1.y = 60; addChild(targ1); var t1:ITween = BetweenAS3.tween(targ1, { x:500 }, { x:targ1.x }, 2.0, Elastic.easeInOut); _tween = BetweenAS3.repeat(BetweenAS3.serial(t1, BetweenAS3.reverse(t1)), 4); }
Compiled, you should get a result such as this:
Adding and Removing Children
One of the interesting benefits of using BetweenAS3 is the ability to add and remove child display objects as a tween using the static methods addChild and removeFromParent respectively. The addChild method expects two arguments: the child to add and the parent the child should be added to. The removeFromParent accepts only a single argument, the child to remove. Both of these methods, again, return ITween instances and can be nested into other BetweenAS3 methods to build up complex tween sequences.
In this example, we’ll instantiate three TweenTarget instances, use the BetweenAS3.addChild method to add them to the display list with a .5 second delay, tween them all across the stage in parallel, then use the removeFromParent method to get remove them from the display list. This time our initTween method looks like this:
private function initTween():void { var targ1:TweenTarget = new TweenTarget(); targ1.x = 20; targ1.y = 25; var targ2:TweenTarget = new TweenTarget(); targ2.x = 20; targ2.y = 60; var targ3:TweenTarget = new TweenTarget(); targ3.x = 20; targ3.y = 95; var t1:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ1, this), .5); var t2:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ2, this), .5); var t3:ITween = BetweenAS3.delay(BetweenAS3.addChild(targ3, this), .5, .5); var t4:ITween = BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut); var t5:ITween = BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeIn); var t6:ITween = BetweenAS3.tween(targ3, { x:500 }, null, 2.0, Elastic.easeInOut); var t7:ITween = BetweenAS3.parallel(t4, t5, t6); var t8:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ1), .5); var t9:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ2), .5); var t10:ITween = BetweenAS3.delay(BetweenAS3.removeFromParent(targ3), .5); _tween = BetweenAS3.serial(t1, t2, t3, t7, t8, t9, t10); }
Note that for the sake of legibility, I created several single ITween instances, but if you really wanted to, you could actually create the _tween instance in a single line like so:
_tween = BetweenAS3.serial(BetweenAS3.delay(BetweenAS3.addChild(targ1, this), .5), BetweenAS3.delay(BetweenAS3.addChild(targ2, this), .5), BetweenAS3.delay(BetweenAS3.addChild(targ3, this), .5, .5), BetweenAS3.parallel(BetweenAS3.tween(targ1, { x:500 }, null, 2.0, Elastic.easeOut), BetweenAS3.tween(targ2, { x:500 }, null, 2.0, Elastic.easeIn), BetweenAS3.tween(targ3, { x:500 }, null, 2.0, Elastic.easeInOut)), BetweenAS3.delay(BetweenAS3.removeFromParent(targ1), .5), BetweenAS3.delay(BetweenAS3.removeFromParent(targ2), .5), BetweenAS3.delay(BetweenAS3.removeFromParent(targ3), .5));
Such is the power of nesting tween methods in BetweenAS3.
Whichever method you choose to create your tween instance, the compiled .swf will look like the example below:
Color Tweening
In addition to nesting ITween instances it is also possible with BetweenAS3 to nest property objects to build complex tweens. In this example we’ll take a look at how to tween the properties of the colorTransform property of the transform property of the TweenTarget instance. This time in our initTween method, we’ll enlarge our TweenTarget instance, then tween its color. Our initTween method will look like this:
private function initTween():void { var targ:TweenTarget = new TweenTarget(); targ.x = 235; targ.y = 40; targ.scaleX = 3; targ.scaleY = 3; addChild(targ); _tween = BetweenAS3.tween(targ, { transform:{colorTransform:{ redOffset:-255, greenOffset:-255, blueOffset:255 }} }, null, 4.0, Expo.easeOut); }
Compiled, you should see something like the below:
Filter Tweening
Again, by nesting property objects, BetweenAS3 can also be used to tween DisplayObject filters. In this quick example, we’ll take a look at tweening a glow filter back and forth. Before we begin though, there is one other property of the ITween interface to take a look at: the stopOnComplete property. This property is a boolean that tells the tween to stop when it is finished. If set to false, as it will be in this example, the tween will run indefinitely starting at it’s beginning each time it completes. In this example, we’ll again enlarge our TweenTarget instance, add a GlowFilter object to its filters property, then, finally tween that filter. Our initTween method will look like this:
private function initTween():void { var targ:TweenTarget = new TweenTarget(); targ.x = 235; targ.y = 40; targ.scaleX = 3; targ.scaleY = 3; addChild(targ); targ.filters = [new GlowFilter(0xFFFF00, 1, 0, 0, 2, 3)]; var t1:ITween = BetweenAS3.tween(targ, { _glowFilter: { blurX:32, blurY:32 } }, { _glowFilter: { blurX:0, blurY:0 }}, 1.0, Expo.easeOut); _tween = BetweenAS3.serial(t1, BetweenAS3.reverse(t1)); _tween.stopOnComplete = false; }
Compiled our glow filter example will look like this:
In case you’re wondering where that “_glowFilter” property came from, BetweenAS3 will, by default, recognize the following filter properties: “_bevelFilter”, “_blurFilter”, “_colorMatrixFilter”, “_convolutionFilter”, “_displacementMapFilter”, “_dropShadowFilter”, “_glowFilter”, “_gradientBevelFilter”, “_gradientGlowFilter”, and “_shaderFilter”.
Tweening Array Members
Possibly one of my favorite features of BetweenAS3 is its ability to tween the members of Array instances just like they were properties of an object (which, when you think about it, they are). Why would you want to tween array members, you ask. Well, one possible example is that you want to tween an image from “normal” to “embossed” and back again. You can easily emboss an image using a ConvolutionFilter, but to tween the transition will require tweening an array.
If you’re not familiar with the ConvolutionFilter object, you create one using a matrix array. Creating a ConvolutionFilter with an array like this (0,0,0,0,1,0,0,0,0), will not affect the image to which it’s applied. Using an array like this however, (-2,-1,0,-1,1,1,0,1,2), will emboss the image.
So, in this example, we will embed an image, create a tween that tweens the members of the array that change and then tweens them back, and add an event listener to that tween so as the tween updates, a ConvolutionFilter has its matrix property updated then the filter is applied to our image.
Because this script is so completely different from all the others, I will present it in its entirety below:
package { import flash.display.Sprite; import flash.events.MouseEvent; import flash.filters.ColorMatrixFilter; import flash.filters.ConvolutionFilter; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import org.libspark.betweenas3.BetweenAS3; import org.libspark.betweenas3.easing.Quad; import org.libspark.betweenas3.events.BetweenEvent; import org.libspark.betweenas3.tweens.ITween; [SWF(width='540', height='450', backgroundColor='#000000', frameRate='60')] public class MatrixTween extends Sprite { [Embed(source = "../assets/prague.jpg")] private var JPG:Class; private var _image:Sprite; private var _tween:ITween; private var _tf:TextField; private var _matrixArray:Array = [ 0, 0, 0, 0, 1, 0, 0, 0, 0]; private var _convFilter:ConvolutionFilter = new ConvolutionFilter(3, 3, _matrixArray); // IDENTITY ARRAY: (0,0,0,0,1,0,0,0,0) // EMBOSS ARRAY: (-2,-1,0,-1,1,1,0,1,2); public function MatrixTween() { _image = new Sprite(); _image.addChild(new JPG()); addChild(_image); initText(); initTween(); stage.addEventListener(MouseEvent.CLICK, toggleTween); } private function initText():void { var fmt:TextFormat = new TextFormat("_sans", 12, 0xFFFFFF); _tf = new TextField(); _tf.selectable = false; _tf.mouseEnabled = false; _tf.autoSize = TextFieldAutoSize.LEFT; _tf.defaultTextFormat = fmt; _tf.text = "Click to start/stop tween."; _tf.x = Math.round(stage.stageWidth * .5 - _tf.width * .5); _tf.y = Math.round(stage.stageHeight - _tf.height - 10); addChild(_tf); } private function initTween():void { var t1:ITween = BetweenAS3.tween(_matrixArray, { 0: -2, 1: -1, 3: -1, 4:1, 5:1, 7:1, 8:2 }, { 0:0, 1:0, 3:0, 4:1, 5:0, 7:0, 8:0 }, 2.0, Quad.easeInOut); _tween = BetweenAS3.serial(t1, BetweenAS3.reverse(t1)); _tween.addEventListener(BetweenEvent.UPDATE, applyFilter); } private function applyFilter(event:BetweenEvent):void { _convFilter.matrix = _matrixArray; _image.filters = [_convFilter]; } private function toggleTween(event:MouseEvent):void { if (_tween.isPlaying) { _tween.stop(); } else { if (_tween.position == _tween.duration) { _tween.gotoAndPlay(0); } else { _tween.play(); } } } } }
Notice how the index numbers of the array members are used as property names. Compiling the .swf will yield the results below:
In conclusion, even though still in version 0.00 (Preview) at the time of writing this, BetweenAS3 is already a powerful tween engine addition to any actionscript coder’s arsenal. As always, I always recommend using the right tool for the job and BetweenAS3 may certainly not be right for every time a tween is required, but at the cost of a measly 14k or so, it is definitely worth keeping handy. For more information regarding BetweenAS3, check out this slide show created by the engine’s author.