Scripting animation has many benfits including improved performance, more control, and most importantly it saves time. There’s a great animation library called Fuse which let’s you animate everything from a MovieClip’s position to the anmount of blur. While a complete animation library it’s also pretty complicated with pages and pages of documentation and I – being a lazy programmer – like to keep things simple so I wrote my own very basic Tweener class.
It let’s you animate any property (or multiple properties) that isn’t controlled by a filter. It also has simple ‘onStart’ and ‘onComplete’ callbacks for letting you know when it starts and finishes animating. Here’s a FLA with an example, and the code…
In your FLA file (the only confusing bit is that I shortened the easeStrengths and easeTypes ‘r’ = ‘Regular’ and ‘eio’ = ‘easeInOut’ see the class for the details)…
import com.vixiom.animation.Tweener;
tweener = new Tweener();
// event listeners
tweener.addEventListener("onStart", this.onStart);
tweener.addEventListener("onComplete", this.onComplete);
// this tween is relative to it's start position
// addTween (movieClip, property, easeStrength, easeType, beginValue, finsihValue, seconds, delay, relative?)
tweener.addTween(circle, "_x", "r", "eio", circle._x, 400, 2, 0, true);
// these tweens use fixed values
tweener.addTween(circle2, "_x", "r", "eio", 50, 450, 1, 2);
tweener.addTween(circle2, "_alpha", "r", "eio", 0, 100, 1, 2);
// start the animation
tweener.start();
// the onStart method
function onStart ()
{
trace("// on start!");
}
// the onComplete method
function onComplete ()
{
trace("// on complete!");
}
The Tweener class…
/**
@class Tweener
@author Alastair Dawson
@copyright 2007 Vixiom Communcations, LLC
*/
import mx.transitions.*;
import mx.transitions.easing.*;
import mx.utils.Delegate;
class com.vixiom.animation.Tweener
{
private var tweens:Array;
private var tweenerFinished:Function;
private var runTime:Number;
private var delayID:Number;
private var callBackID:Number;
private var i = 0;
// event dispatching
function dispatchEvent() {};
function addEventListener() {};
function removeEventListener() {};
/**
* Constructor
*/
public function Tweener ()
{
// initialize as a broadcaster
mx.events.EventDispatcher.initialize(this);
// tweens array
tweens = [];
// runtime
runTime = 0;
}
/**
* Add Tween
*
* @param mc movieClip
* @param p property
* @param es easeStrength
* @param et easeType
* @param b begin value
* @param f finish value
* @param s seconds (duration)
* @param d delay (duration in seconds)
* @param r relative (move relative to current position, false by default)
*
*/
// add properties
public function addTween (mc:MovieClip, p:String, es:String, et:String, b:Number, f:Number, s:Number, d:Number, r:Boolean)
{
tweens[i] = new Tween();
tweens[i].obj = mc;
tweens[i].prop = p;
if (r == true) {
tweens[i].begin = tweens[i].obj[tweens[i].prop] + b;
tweens[i].finish = tweens[i].obj[tweens[i].prop] + f;
} else {
tweens[i].begin = b;
tweens[i].finish = f;
}
tweens[i].duration = s;
tweens[i].useSeconds = true;
tweens[i].func = convertEase(es,et);
// set delay
tweens[i].delay = d;
// set this tween's runTime
tweens[i].runTime = s + d;
// increment
i++;
}
// start
public function start ()
{
for (var j = 0; j < i; j++)
{
// does it have a delay if so set interval
if (tweens[j].delay != undefined && tweens[j].delay != 0) {
tweens[j].delayID = setInterval (this, "startTween", (tweens[j].delay * 1000), tweens[j]);
} else {
startTween(tweens[j])
}
// check if it's the longest running tween
if (tweens[j].runTime > runTime) {
runTime = tweens[j].runTime;
}
}
// onStart
var eventObj:Object={target:this,type:"onStart"}
dispatchEvent(eventObj);
// onComplete
callBackID = setInterval (this, "onComplete", (runTime * 1000));
}
// startTween
private function startTween(t:Object)
{
t.start();
clearInterval(t.delayID);
}
// stop
private function stop()
{
for (var j = 0; j < i; j++) {
tweens[j].stop();
}
}
// resume
private function resume()
{
for (var j = 0; j < i; j++) {
tweens[j].resume();
}
}
// rewind
private function rewind()
{
for (var j = 0; j < i; j++) {
tweens[j].rewind();
}
}
// fforward
private function fforward()
{
for (var j = 0; j < i; j++) {
tweens[j].fforward();
}
}
// callBack
private function onComplete()
{
// broadcast message
var eventObj:Object={target:this,type:"onComplete"}
dispatchEvent(eventObj);
// clear interval
clearInterval(callBackID);
}
// convert easeStrenght
private function convertEase (es:String, et:String):Object
{
var easeStrength:String = es;
var easeType:String = es;
var esObj:Object;
switch (es) {
case "b":
esObj = Bounce;
break;
case "k":
esObj = Back;
break;
case "e":
esObj = Elastic;
break;
case "n":
esObj = None;
break;
case "r":
esObj = Regular;
break;
case "s":
esObj = Strong;
break;
default:
esObj = None;
}
switch (et) {
case "e":
et = "easeNone";
break;
case "ei":
et = "easeIn";
break;
case "eo":
et = "easeOut";
break;
case "eio":
et = "easeInOut";
break;
default:
et = "easeNone";
}
return esObj[et];
}
/**
* Event dispatcher
*
* @param d data
* @param et eventType
*
*/
function dispatch(et)
{
// broadcast message
var eventObj:Object={target:this,type:et}
dispatchEvent(eventObj);
}
}
That’s it! no too heavy man
*UPDATE*
If you’re using this in a class it’s best to use Delegate to access the onComplete or onStart methods so you don’t lose scope
tweener.addEventListener("onComplete", Delegate.create (this, this.onComplete));
2 Comments
great stuff – been working on a similar thing – the relative option is a favourite
Thank you very much for this great idea! We will try it in the next few days…