A problem occured while loading the plugin: krl_flickr_photoset -> : Assigning the return value of new by reference is deprecated on line 18
kelvinluck.com: Second steps with ActionScript 3
Get Firefox! creative commons

Second steps with ActionScript 3

Update: Check out part 3 of this series on my experiences with ActionScript 3.

Update: The example swf doesn’t appear to be working in the new Beta of the Flash 8.5 player… I will look into this when I get a chance…

I have taken my first attempt at ActionScript 3 and refined it a little… The first attempt was a very simple mosaic tool which created a mosaic from a jpeg. In this version I have improved it in a number of ways:

I now first blur each section of the image to get more of an average colour for each tile. This was a good excuse to use one of the flash.filters.* in AS and also gives a slightly better looking result. It still isn’t perfect but I spent quite a while searching the web and couldn’t find any good algorithms to find the average colour of a given bitmap (I’m sure they exist though – if you know one please leave some info in the comments).

I also decided to make the example a little bit more exciting by adding some movement to it. I initally did this by using the mx.effects classes but then I realised that these were adding 210KB to my file (unless I missed a “SimpleTween” class somewhere?). I presume that is because they depend on the whole Flex Framework but in this case (where my swf does pretty much what I want at 2KB) it seemed like some unnecessary overhead.

So I wrote a very simple little Tween class based on the new flash.util.Timer class and borrowed one of Robert Penner’s easing equations...

So here is the resulting swf (note that you will need the flash player 8.5 to view it and that the movement happens when the swf loads so you may have to refresh the page to see it move):

Sorry, you require Flash 8.5 to view this movie.

And here is the ActionScript to make that swf:

ActionScript:
package
{
        import flash.display.Sprite;
        import flash.display.DisplayObjectContainer;
        import flash.display.Bitmap;
        import flash.util.trace;       
        import flash.display.Loader;
        import flash.display.StageScaleMode;
        import flash.display.BitmapData;
        import flash.net.URLRequest;
        import flash.events.*;
        import flash.geom.Rectangle;
        import flash.geom.Point;
        import flash.filters.BlurFilter;
        import flash.util.Timer;
       
        [SWF(width="430", height="287", backgroundColor="#869CA7")]
        public class Mosaic extends Sprite
        {
               
                private var picHolder:Sprite;
                private var picLoader:Loader;
               
                public function Mosaic()
                {
                        stage.scaleMode = StageScaleMode.NO_SCALE;
                       
                        picHolder = new Sprite();
                        picHolder.x = picHolder.y = 10;
                       
                        picLoader = new Loader();
                        picLoader.addEventListener(EventType.COMPLETE, onPicLoaded);
                       
                        // TODO: Add support for displaying load progress, not found files etc..
                        picLoader.load(new URLRequest("IMG_6032.jpg"));
                        this.addChild(picHolder);
                }

                private function onPicLoaded(event:Event):Void
                {
                        picHolder.addChild(picLoader);
                        var mt:MosaicTray = MosaicTray(this.addChild(new MosaicTray(picLoader, picLoader.content)));
                }
        }
       
        private class MosaicTray extends Sprite {
               
                private var TILE_SIZE:uint = 8;
               
                public function MosaicTray(picSprite:DisplayObjectContainer, bitmap:Bitmap)
                {
                        this.x = picSprite.width + 20;
                        this.y = 10;
                        var sourceBitmapData:BitmapData = bitmap.bitmapData;
                        var blurFilter:BlurFilter = new BlurFilter(255, 255, 3);
                        var halfTile:uint = Math.round(TILE_SIZE/2);
                        for (var i:uint=0; i<picSprite.width; i+=TILE_SIZE) {
                                for (var j:uint=0; j<picSprite.height; j+=TILE_SIZE) {
                                        var bd:BitmapData = new BitmapData(TILE_SIZE, TILE_SIZE, false);
                                        var r:Rectangle = new Rectangle(i, j, TILE_SIZE, TILE_SIZE);
                                        var p:Point = new Point();
                                        // blur the bitmap so we get a more acurate "average" for each tile
                                        bd.applyFilter(sourceBitmapData, r, p, blurFilter);
                                        var col:uint = bd.getPixel(halfTile, halfTile);
                                        var mt:MosaicTile = new MosaicTile(i, j, col, TILE_SIZE);
                                        this.addChild(mt);
                                }
                        }
                }
        }
        private class MosaicTile extends Sprite {
               
                private var colour:uint;
               
                public function MosaicTile(x:uint, y:uint, colour:uint, size:uint)
                {
                        this.colour = colour;
                        graphics.beginFill(colour);
                        graphics.drawRect(x, y, size-1, size-1);
                        graphics.endFill();
                        _bounceIn();
                }
               
                private function _bounceIn()
                {
                        x = Math.round(Math.random() * 100) - 50;
                        y = Math.round(Math.random() * 100) - 50;
                       
                        new Tween(this, "x", x, 0, 2000);
                        new Tween(this, "y", y, 0, 2000);
                }
        }
       
        // wrote my own super simple tween class when I realised that the
        // mx.effects ones were adding 210KB to my file!!
        private class Tween {
               
                private static var UPDATE_INTERVAL:Number = 100; // milliseconds
                private static var _id:uint=0;
               
                var target:DisplayObjectContainer;
                var property:String;
                var start:Number;
                var end:Number;
                var duration:Number; // in milliseconds
               
                private var _timer:Timer;
                private var _currentTime:Number;
                private var _currentVal:Number; // to deal with rounding in MovieClip properties... More acurate to keep a seperate record of the actual value...
               
                public function Tween(target:DisplayObjectContainer, property:String, start:Number, end:Number, duration:Number)
                {
                        this.target = target;
                        this.property = property;
                        this.start = start;
                        this.end = end - start; // cos thats how the tweening equation likes it!
                        this.duration = duration;
                        _currentTime = 0;
                        _timer = new Timer(UPDATE_INTERVAL);
                        _timer.addEventListener("timer", onTimer);
                        _timer.start();
                }
               
                public function onTimer(event:TimerEvent):Void {
                        var newVal:Number = easeOut(_currentTime, start, end, duration);
                        _currentVal = newVal;
                        target[property] = newVal;
                        if (duration - _currentTime < 1) {
                                _timer.stop();
                        }
                        // can this be done better? Can't see a Timer.getCurrentTime method or anything similar in the docs...
                        _currentTime += UPDATE_INTERVAL;
                }
               
                // copied from Robert Penner's easing equations (com.robertpenner.easing.Bounce.as):
                // http://www.robertpenner.com/easing/
                static function easeOut (t:Number, b:Number, c:Number, d:Number):Number {
                        if ((t/=d) < (1/2.75)) {
                                return c*(7.5625*t*t) + b;
                        } else if (t < (2/2.75)) {
                                return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
                        } else if (t < (2.5/2.75)) {
                                return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
                        } else {
                                return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
                        }
                }
        }
}

If you compare it to my previous effort you will see that it is pretty similar… The additions are the BlurFilter and the new Tween class… They should be clear enough… The Tween class is very quickly knocked together and isn’t meant to be a proper generic replacement for the Macromedia ones but I think that such a beast may be necessary if the Macromedia ones are going to create such big files… Hopefully the next version of the zigo tween kit from Moses Supposes will include support for AS3…

Anyway – any comments on the code or suggestions for improvement appreciated :)

  1. this is a nice tween class
    really easy to use…
    http://hosted.zeh.com.br/mctween/
    Gustavo Gawryszewski    Nov 8, 13:09    #
  2. The problem with the Tween class was that I needed one which worked with ActionScript 3. I don’t think Zeh’s one does, does it?

    I use the Moses supposes zigo tween kit in AS2 – if you email Moses he will send you the latest mxp file which is a big improvement from the one on the site…
    Kelvin    Nov 8, 13:52    #