February 20th, 2009

Flash AS3 Carousel and New Partnership

One of my colleagues, Shon Taylor of Catapult Imaging Studios, has recently joined forces with Jason Dauphinee, Rod Grainger and Hugh Ruthven (all formerly of DDB Canada) to found a new agency called Bone Creative. They have asked me to join their development team and, as I generally think it a good idea to surround oneself with talented people, I have accepted.


I'm just about finished the Bone Creative website and thought I might share one of my discarded concepts. We were looking for a cool way to display our collective portfolio pieces and the idea of a 'carousel' type display was tabled. This was one of the initial prototypes I came up with.


Hover your mouse pointer over the image below to change the carousels direction:



Although the carousel makes for a nice effect, it wasn't exactly what everyone wanted. I did end up doing something quite similar however but, visually, it looks more like a parabola put on it's side than a carousel. To take a look, check out Bone Creative's portfolio.


Anyway, I thought that I might share my code since it's a fun idea to play with and could easily be elaborated upon. For starters, this is the main or 'document' class:



package 
{
        import ImageObject;
        import flash.display.*;
        import flash.events.*;
        import flash.net.*;

    public class Carousel extends Sprite
    {
                private var numOfItems:Number;
                private var radiusX:Number = 200;
                private var radiusY:Number = 75;
                private var centerX:Number = stage.stageWidth / 2 - 25;
                private var centerY:Number = stage.stageHeight / 2 - 35;
                
                private var scale:Number = 0.66;
                private var speed:Number = 0.005;
                private var angle:Number;
                
                private var xmlData:XML;
                private var xmlLength:uint;
                private var xmlList:XMLList;
                
                private var containerArray = new Array();
                private var imageArray = new Array();
                
                private var imageLoader:Loader;

                public function Carousel():void
                {
                        loadXML();
                }
                
                private function loadXML():void 
                {
                        // Get XML data
                        var loader:URLLoader = new URLLoader();  
                        loader.load(new URLRequest("carousel.xml"));  
                        loader.addEventListener(Event.COMPLETE,addImage); 
                }
                
                private function addImage(e:Event):void
                {
                        xmlData = new XML(e.target.data);
                        xmlList = new XMLList();
                        xmlList = xmlData.portfolio.img.@src
                        xmlLength = xmlData.portfolio.children().length();
						
                        // Cycle through data - add image to stage
                        for (var i:uint = 0; i < xmlLength; i++) {
                                angle = i * ((Math.PI * 2) / xmlLength);
								
                                // Create new object
                                imageArray[i] = new ImageObject(xmlList[i], angle);
								
                                // Load 'em up
                                imageLoader = new Loader();
                                imageLoader.load(new URLRequest('images/' + imageArray[i].img));
								
                                // Move 'em out
                                addChild(imageArray[i]);
                                imageLoader.scaleX = imageLoader.scaleY = scale;
                                imageArray[i].addChild(imageLoader);
								
                                // Reposition everyframe
                                imageArray[i].addEventListener(Event.ENTER_FRAME, positionImage);
								
                                // Add mouse event listener to adjust speed
                                stage.addEventListener(MouseEvent.MOUSE_MOVE, adjustSpeed); 
                        }
                }
                
                private function positionImage(e:Event):void
                {
                        for (var i:uint = 0; i < xmlLength; i++ ) {
                                // A little trig to get the x and y coordinates
                                imageArray[i].x = Math.cos(imageArray[i].ang) * radiusX + centerX;
                                imageArray[i].y = Math.sin(imageArray[i].ang) * radiusY + centerY;
								
                                // Scaling to create 'depth'
                                var imageScale:Number = imageArray[i].y / (centerY + radiusY);
                                imageArray[i].scaleX = imageArray[i].scaleY = imageScale;
								
                                // Change the angle
                                imageArray[i].ang += speed; 
                        }
                        swapDepth();
                }
                
                private function swapDepth():void 
                {  
                        // Sort images 
                        imageArray.sortOn("scaleX");
						
                        // Set depth
                        for(var i:uint = 0; i < xmlLength; i++)  
                        {  
                                setChildIndex(imageArray[i], i);
                        } 
                }  

                private function adjustSpeed(e:MouseEvent):void  
                {  
                        speed = (mouseX - centerX) / 100000;
                } 
        }
}

And the class below, well, that's what pulls it all together - it took me a while to figure this part out. You see, in order to create the illusion of movement, you essentially 'redraw' the object over and over in different positions. The objects position on the ellipse is calculated through the use of some basic trig as you can see in the class above. The key is the objects angle in relation to the ellipses radius.


Now, in order to 'track' the angle of the object from one frame to the next, one has to have a means by which to 'save state' if you will. In order to do this, you need to create an object and an instance of that object that contains both the 'name' of the image as well as the coresponding angle - hence the class below:



package
{
        import flash.display.*;

        public class ImageObject extends Sprite
        {
                public var img:String;
                public var ang:Number;

                private var imgLoader:Loader = new Loader();
                
                // Create object with image and angle properties
                public function ImageObject(i:String,a:Number)
                {
                        img = i;
                        ang = a;
                }
        }
}

There may be another way to do this through the use of static arrays or something but this solution seems to be the most appropriate and makes the most sense from an OO perspective. If you would like to download the complete example you can do so here. Let me know if you come up with anything cool.


Have fun!

Bookmark and Share

October 15th, 2008

Swapping Images with Flash AS3

OK - so this is kinda cool.


I've been trying to come up with a way of organizing thumbnails (in the browser) that's both user friendly and intuitive. Play with it and tell me what you think. All images are loaded externally via XML then dynamically resized and scaled in Flash.


Just drag and drop the images:



More of a proof of concept than anything else but you get the basic idea. At first glance, things might look fairly straight forward but let me tell you - there's a lot more going on here than one might think. I'll share at some point but for the time being I'd like to develop the script a little further myself.


Cheers.

Bookmark and Share

July 16th, 2008

Flash AS3 Cork Board and a Fractured Wrist

To start with, yes, I am still in a 'hand prison' - although this one's new and a different color. The lime-green cast was fun, and great for directing traffic, but it looks like this is going to be something I'll be living with for some time yet so, for this second cast, I decided to go with a color that's a little easier to coordinate with the wardrobe - black (and yes, I know that black is 'technically' not a color).


With black, I don't have to worry so much about keeping it clean (which could be a good thing or a bad thing) and I think it looks just a bit more 'manly' (Hey, considering that the injury was caused by slipping on a hard wood floor while dancing in my sock feet, I could probably use a little help in this department).


The folks at the Royal Jubilee Hospital took some more x-rays of my wrist and an orthopedic surgeon determined that they'd need a CAT scan in order to "figure out exactly what's going on in there". I'm still waiting to have that scan. Hopefully, if all goes well, my Scaphoid (the bone that is fractured) will not require surgery and I can be done with this. Until this is dealt with, certain parts of my life are on hold. Ah well, I didn't really want to go swimming this summer anyway... or ride my bike... or write legibly... Can I get some sympathy here?


Anyway, here's another project I was working on in Flash using AS3. You can find it here or just click on the image below.




The 'cork board' saves state by sending the data to a MySQL table via a simple PHP script. Another PHP script grabs the data from the table and converts it into XML which is then brought into Flash.


You can leave a message on the 'cork board' to see how it works.

Bookmark and Share

1January 27th, 2009 at 1:54 pm

Scott Burns writes:

Hey i think this would be great on my website, of course you would get tje credit for it. But is it avaliable for download?

2August 4th, 2009 at 4:47 pm

Thanos Zitouniatis (Greece) writes:

Hi! I would really love to have that wonderful idea you made on a website I am making for my school. Is there anyway you can share the code? Plz?

July 11th, 2008

Custom Scrollbar Class using Flash and AS3

I recently wrote this AS3 class to create custom scrollbars in Flash. If you would like to download the example and class, you can do so here.


Copy and paste some text or just hit 'Enter'.



This first class is the document class that imports the CustomScroll class. One thing that you'll want to take note of is how we have to pass a reference of the main timelines stage object to the CustomScroll class constructor. For an explanation as to why we have to do this, check out Senocular's comments on the Kirupa.com forum or google "as3 access to stage and root".


 
package {
        import com.francisdaigle.CustomScroll;
        import flash.display.MovieClip;
        import flash.events.MouseEvent;
        import flash.text.TextField;
        
        public class ScrollerExample extends MovieClip
        {
                
                public function ScrollerExample()
                {
                        var example1:CustomScroll = new CustomScroll(stage, track1_mc, output1_txt, up1_btn, down1_btn);
                        var example2:CustomScroll = new CustomScroll(stage, track2_mc, output2_txt, up2_btn, down2_btn);
                        var example3:CustomScroll = new CustomScroll(stage, track3_mc, output3_txt, up3_btn, down3_btn);
                        var example4:CustomScroll = new CustomScroll(stage, track4_mc, output4_txt, up4_btn, down4_btn);
                        
                        submit_btn.addEventListener(MouseEvent.CLICK, onSubmit);
                }
                
                // Submit Button
                private function onSubmit(e:MouseEvent):void
                {
                        output1_txt.text = input_txt.text;        
                        output2_txt.text = input_txt.text;
                        output3_txt.text = input_txt.text;
                        output4_txt.text = input_txt.text;
                }
        }
}

And this is the CustomScroll class:



package com.francisdaigle{
        import flash.display.MovieClip;
        import flash.display.SimpleButton;
        import flash.display.Stage;
        import flash.events.Event;
        import flash.events.MouseEvent;
        import flash.geom.Rectangle;
        import flash.text.TextField;
        
        public class CustomScroll extends MovieClip
        {        
                private var _stage:Stage; 
                private var _track:MovieClip;
                private var _input:TextField;
                private var _output:TextField;
                private var _up:SimpleButton;
                private var _down:SimpleButton;
                
                private var dragging:Boolean = false;
                private var position:Number = 0;
                private var rectangle:Rectangle = new Rectangle(0,0,0,0);
                
                public function CustomScroll(stageRef:Stage, track:MovieClip, output:TextField, up:SimpleButton, down:SimpleButton)
                {
                        _stage = stageRef;
                        _track = track;
                        _output = output;
                        _up = up;
                        _down = down;
                        
                        rectangle.height = _track.height;
                        _track.knob_mc.y = 0;
                        _track.knob_mc.buttonMode = true;
                        
                        _up.addEventListener(MouseEvent.MOUSE_DOWN, pressTheUpButton);
                        _up.addEventListener(MouseEvent.MOUSE_UP, releaseTheUpButton);
                        _up.addEventListener(MouseEvent.MOUSE_OUT, loseUpFocus);
                        _down.addEventListener(MouseEvent.MOUSE_DOWN, pressTheDownButton);
                        _down.addEventListener(MouseEvent.MOUSE_UP, releaseTheDownButton);
                        _down.addEventListener(MouseEvent.MOUSE_OUT, loseDownFocus);
                        _track.knob_mc.addEventListener(MouseEvent.MOUSE_DOWN, dragIt);
                        _stage.addEventListener(MouseEvent.MOUSE_UP, dropIt);
                        _output.addEventListener(MouseEvent.MOUSE_WHEEL, onWheel);
                }
                
                // Up Button
                private function pressTheUpButton(e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, adjustIt);
                        _track.knob_mc.addEventListener(Event.ENTER_FRAME, holdTheUpButton);
                }
                
                private function holdTheUpButton(e:Event):void 
                {
                        position = int(position);
                        if (position > 0) {
                                position -= 1;
                        } else if (position < 0) {
                                position = 0;
                        }
                        _track.knob_mc.y = rectangle.height / (_output.maxScrollV - 1) * position;
                        _output.scrollV = position + 1;
                        //trace ('p up = ' + position);
                }
                
                private function releaseTheUpButton (e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, holdTheUpButton);
                }
                
                private function loseUpFocus(e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, holdTheUpButton);
                }
                
                // Down Button
                private function pressTheDownButton(e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, adjustIt);
                        _track.knob_mc.addEventListener(Event.ENTER_FRAME, holdTheDownButton);
                }
                
                private function holdTheDownButton(e:Event):void 
                {
                        position = int(position);
                        if (position < _output.maxScrollV - 1) {
                                position += 1;
                        } else {
                                position = _output.maxScrollV - 1;
                        }
                        _track.knob_mc.y = rectangle.height / (_output.maxScrollV - 1) * position;
                        _output.scrollV = position + 1;
                        //trace ('p down = ' + position);
                }
                
                private function releaseTheDownButton (e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, holdTheDownButton);
                }
                
                private function loseDownFocus(e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, holdTheDownButton);
                        
                }
                
                // Drag & Drop
                private function dragIt(e:MouseEvent):void
                {
                        _track.knob_mc.startDrag(false,rectangle);
                        dragging = true;
                        _track.knob_mc.addEventListener(Event.ENTER_FRAME, adjustIt);
                }
                
                private function dropIt(e:MouseEvent):void
                {
                        if (dragging) {
                                _track.knob_mc.stopDrag();
                                dragging = false;
                        }
                }
                
                private function adjustIt(e:Event):void
                {
                        position = _track.knob_mc.y / (rectangle.height/(_output.maxScrollV - 1));
                        if (_track.knob_mc.y >= int(rectangle.height)) {
                                position = _output.maxScrollV - 1;
                        }
                        _output.scrollV = int(position + 1);
                        //trace ('p drag = ' + position);
                }
                
                // Mouse Wheel
                private function onWheel(e:MouseEvent):void
                {
                        _track.knob_mc.removeEventListener(Event.ENTER_FRAME, adjustIt);
                        position += e.delta * -1;
                        if (position < 0) {
                                position = 0;
                        } else if (position > _output.maxScrollV - 1) {
                                position = (_output.maxScrollV - 1);
                        }
                        _track.knob_mc.y = rectangle.height / (_output.maxScrollV - 1) * position;
                        //trace ('p wheel = ' + position);
                }
        }
}

If you're new to AS3 or if you're having trouble getting the example to work, make sure to designate a class path to the parent folder of 'CustomScroll.as' called 'classes'. I should also mention that in the examples 1, 3 & 4, the opacity of movie clip 'track_mc' has been set to zero in order that it be made invisible.


Have fun.

Bookmark and Share

1November 12th, 2009 at 7:13 am

Nassar writes:

thank u sooooooooooooooooooo much.