Scripting animation in ActionScript

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 :P

*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));
This entry was posted in ActionScript, Flash. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

2 Comments

  1. John
    Posted March 2, 2007 at 3:58 pm | Permalink

    great stuff – been working on a similar thing – the relative option is a favourite

  2. Posted July 7, 2007 at 4:03 am | Permalink

    Thank you very much for this great idea! We will try it in the next few days…

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word