Ahmed Nuaman


builder of internets ~ developer of dreams ~ tamer of Dachshunds

Scale Any DisplayObject with My ‘ScaleObject’ Class

Update (another): I’m no longer updating the code on here, instead it’s all on my Github account.

Update: there was a slight issue with the “height()” overrride, thanks to the comments I’ve updated the code with the fix.

Most designers and developers will be familiar with Actionscript 3′s “scale9Grid“. Now when you can use “scale9Grid”, it’s awesome, but there are times when you can’t use it or it doesn’t work:

  • You can’t use it on Bitmaps or non-vector objects
  • If the object has children, they won’t have the grid enforced on them
  • And more…

Here’s a nice run down of when it works and when it doesn’t work: http://www.ovidiudiac.ro/blog/2009/05/scale9grid-work-and-fail/

So what do you do if your object has children or is a bitmap? Simple, use my class that I painstakingly wrote this morning!

I’m currently working on a project where I’ve created some assets in the Flash IDE, exported them as classes via a SWC and am using them in the Actionscript 3 code. One of the assets contains multiple children, rather than a straight vector object, meaning that it can’t have a “scale9Grid” thrust upon it.

So I got thinking about it and decided the best way was to do this:

  1. Convert the object to bitmap data
  2. Slice the object up into a 9 piece grid
  3. Align those grid pieces accordingly
  4. Override the “height” and “width” functions that would expand the centre grid items leaving the outside ones to scale

Simples! So I got on creating the class. One of the biggest problems was getting over the way Actionscript 3′s BitmapData “draw()” function misleads you. You see it says that you can pass a “clipRect” parameter as a “Rectangle()” which leads one to the assumption that it will clip the bitmap data according the rectangle you pass. Wrong, how very wrong. What it does, instead, is leave empty space where it’s been clipped. Not fun.

After much Googling, I came across this post: http://pixelwelders.com/blog/actionscript-3/2008/as3-bitmapdata-foibles/ which explained a little hack around this failed method. I’ve taken his code and just re-factored it so it fits within my code base, here it is:

package com.firestartermedia.lib.as3.utils
{
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    public class BitmapUtil
    {
        public static function grab(source:DisplayObject, rect:Rectangle, smoothing:Boolean=true):BitmapData
        {
            var draw:BitmapData = new BitmapData( source.width, source.height, true, 0 );
            var copy:BitmapData = new BitmapData( rect.width, rect.height, true, 0 );
           
            draw.draw( source, null, null, null, null, smoothing );
            copy.copyPixels( draw, rect, new Point( 0, 0 ) );
           
            draw.dispose();
           
            return copy;
        }
    }
}

So by using this static function in the class, I can create a proper grid of any display object, and here’s the class that does it all:

package com.firestartermedia.lib.as3.display.tools
{
    import com.firestartermedia.lib.as3.utils.BitmapUtil;
   
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.geom.Rectangle;

    public class ScaleObject extends Sprite
    {
        private var bitmapTL:Sprite;
        private var bitmapTC:Sprite;
        private var bitmapTR:Sprite;
        private var bitmapML:Sprite;
        private var bitmapMC:Sprite;
        private var bitmapMR:Sprite;
        private var bitmapBL:Sprite;
        private var bitmapBC:Sprite;
        private var bitmapBR:Sprite;
        private var master:DisplayObject;
        private var scaleGrid:Rectangle;
       
        public function ScaleObject(master:DisplayObject, scaleGrid:Rectangle)
        {
            this.master = master;
            this.scaleGrid = scaleGrid;
           
            init();
        }
       
        private function init():void
        {
            var tlX:Number = 0;
            var tlY:Number = 0;
            var tlWidth:Number = scaleGrid.x;
            var tlHeight:Number = scaleGrid.y;
            var tcX:Number = tlWidth;
            var tcY:Number = tlY;
            var tcWidth:Number = scaleGrid.width;
            var tcHeight:Number = tlHeight;
            var trX:Number = tlWidth + tcWidth;
            var trY:Number = tlY;
            var trWidth:Number = master.width - trX;
            var trHeight:Number = tlHeight;
           
            var mlX:Number = 0;
            var mlY:Number = tlHeight;
            var mlWidth:Number = tlWidth;
            var mlHeight:Number = scaleGrid.height;
            var mcX:Number = tlWidth;
            var mcY:Number = mlY;
            var mcWidth:Number = tcWidth;
            var mcHeight:Number = mlHeight;
            var mrX:Number = trX;
            var mrY:Number = mlY;
            var mrWidth:Number = trWidth;
            var mrHeight:Number = mlHeight;
           
            var blX:Number = 0;
            var blY:Number = tlHeight + mlHeight;
            var blWidth:Number = tlWidth;
            var blHeight:Number = master.height - blY;
            var bcX:Number = tlWidth;
            var bcY:Number = blY;
            var bcWidth:Number = tcWidth;
            var bcHeight:Number = blHeight;
            var brX:Number = trX;
            var brY:Number = blY;
            var brWidth:Number = trWidth;
            var brHeight:Number = blHeight;
           
            bitmapTL = slice( tlX, tlY, tlWidth, tlHeight );
            bitmapTC = slice( tcX, tcY, tcWidth, tcHeight );
            bitmapTR = slice( trX, trY, trWidth, trHeight );
            bitmapML = slice( mlX, mlY, mlWidth, mlHeight );
            bitmapMC = slice( mcX, mcY, mcWidth, mcHeight );
            bitmapMR = slice( mrX, mrY, mrWidth, mrHeight );
            bitmapBL = slice( blX, blY, blWidth, blHeight );
            bitmapBC = slice( bcX, bcY, bcWidth, bcHeight );
            bitmapBR = slice( brX, brY, brWidth, brHeight );
        }
       
        private function slice(x:Number, y:Number, width:Number, height:Number):Sprite
        {
            var rect:Rectangle = new Rectangle( x, y, width, height );
            var sliceData:BitmapData = BitmapUtil.grab( master, rect );
            var slice:Sprite;
           
            slice = new Sprite();
           
            slice.graphics.beginBitmapFill( sliceData );
            slice.graphics.drawRect( 0, 0, width, height );
            slice.graphics.endFill();
            slice.x = x;
            slice.y = y;
           
            addChild( slice );
           
            return slice;
        }
       
        override public function set width(value:Number):void
        {
            var targetWidth:Number = value - bitmapTL.width - bitmapTR.width;
            var targetX:Number = value - bitmapTL.width;
           
            bitmapTC.width = targetWidth;
            bitmapMC.width = targetWidth;
            bitmapBC.width = targetWidth;
           
            bitmapTR.x = targetX;
            bitmapMR.x = targetX;
            bitmapBR.x = targetX;
        }
       
        override public function set height(value:Number):void
        {
            var targetHeight:Number = value - bitmapTL.height - bitmapBL.height;
            var targetY:Number = value - bitmapBL.height;
           
            bitmapML.height = targetHeight;
            bitmapMC.height = targetHeight;
            bitmapMR.height = targetHeight;
           
            bitmapBL.y = targetY;
            bitmapBC.y = targetY;
            bitmapBR.y = targetY;
        }
    }
}

The basic usage is like so:

var sprite:Sprite = new ScaleObject( displayObject, new Rectangle( 5, 2, 78, 6 ) );

sprite.width = 300;

And here’s a little example:

Where have comments gone?

Good question my old fruity. I'm now sticking any post discussions on Google+. Why? Well simply it's better. WordPress's comment system isn't very elegant and nor are ones like Disqus or Livefyre, so to save hassle I've just shipped them off to a social network, like a real boss.

Search

My social skills

Latest blog posts

  • Loading posts...

Subscribe in a reader

Latest tweets

  • Loading tweets...