Long long ago, I created a 3d circular menu thingie you can see here. It wasn’t much long after, that Lee Brimelow came up with his 3d carousel tutorial. Well, to be honest, I’ve never read Mr. Brimelow’s tutorial, but still I vowed then and there that I’d never make another one these things again – kinda like Bob Dylan vowing never to play “All Along the Watchtower” after hearing Hendrix’s version. Today, though, I’ve decided to renege on that vow. A freind over at ShavedPlatypus was asking how to make a carousel thing more similar to mine than Lee’s but in AS3, so I figured what the hell, why not update.
Below is an example of what I came up with:
Not many comments added, but the full script is below. In a nutshell, you just create a new instance of OBO_3DCarousel and pass it a few 3d properties (if you want). After that you add pictures to it using the addItem() method. Easy-peasy, man. Oh, and you can set the useBlur boolean property if you want a blur for more distant images.
The main class:
package com.onebyonedesign.td { import flash.display.DisplayObject; import flash.display.Sprite; import flash.events.Event; import flash.filters.BlurFilter; /** * Creates a 3D Carousel when you add images (DisplayObjects) to an instance using the addItem() method. * @author Devon O. */ public class OBO_3DCarousel extends Sprite { private var _imageList:XMLList; private var _angleStep:Number; private var _fl:Number; private var _items:Array = []; private var _currentLoaded:int = 0; private var _blur:BlurFilter = new BlurFilter(0, 0, 2); private var _zRotation:Number; private var _targetRotation:Number; private var _numItems:int; private var _radius:Number; private var _zpos:Number; private var _useBlur:Boolean = false; public function OBO_3DCarousel(focalLength:int = 800, radius:int = 300, zpos:Number = 0):void { _fl = focalLength; _radius = radius; _zpos = zpos; } public function get zRotation():Number { return _zRotation; } public function set zRotation(value:Number):void { _zRotation = value; } public function get targetRotation():Number { return _targetRotation; } public function set targetRotation(value:Number):void { _targetRotation = value; } public function get zpos():Number { return _zpos; } public function set zpos(value:Number):void { var i:int = _items.length; while (i--) { _items[i].zpos = value; _items[i].updateDisplay(); } _zpos = value; } public function get radius():Number { return _radius; } public function set radius(value:Number):void { var i:int = _items.length; while (i--) { _items[i].radius = value; _items[i].updateDisplay(); } _radius = value; } public function get useBlur():Boolean { return _useBlur; } public function set useBlur(value:Boolean):void { _useBlur = value; } // READ-ONLY public function get numItems():int { return _numItems; } public function addItem(image:DisplayObject):void { _numItems = _items.length + 1; _targetRotation = -(90 - (360 / _numItems)); _zRotation = _targetRotation; _angleStep = (2 * Math.PI) / _numItems; var item:TDCarouselItem = new TDCarouselItem(image); _items.push(item); var i:int = _items.length; while (i--) { var ci:TDCarouselItem = _items[i]; ci.radius = _radius; ci.radians = _zRotation * Math.PI / 180; ci.angle = _angleStep * i; ci.focalLength = _fl; ci.zpos = _zpos; ci.ypos = y; ci.updateDisplay(); } addChild(item); // if at least one item, go ahead and init the sucker if (_numItems == 1) initCarousel(); } public function kill():void { removeEventListener(Event.ENTER_FRAME, frameHandler); var i:int = _items.length; while (i--) { var ci:TDCarouselItem = _items[i]; ci.data.dispose(); removeChild(ci); ci = null; } } private function initCarousel():void { addEventListener(Event.ENTER_FRAME, frameHandler); } private function frameHandler(event:Event):void { var rads:Number = _zRotation * Math.PI / 180; _items.sortOn("zpos", Array.NUMERIC); var i:int = _items.length; while (i--) { var item:TDCarouselItem = _items[i]; item.radians = rads; if (_useBlur) { if (!isNaN(item.zpos)){ // play with this blur amount - to taste _blur.blurX = _blur.blurY = int(((item.zpos - _zpos) + 200) / 40); item.filters = [_blur]; } } item.updateDisplay(); // need better z sorting addChild(item); } } } }
The item class (instances of this class are the pictures in the carousel):
package com.onebyonedesign.td { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Sprite; /** * 3D items for OBO_3DCarousel class * @author Devon O. */ public class TDCarouselItem extends Sprite { private var _radius:Number; private var _radians:Number; private var _angle:Number; private var _focalLength:int; private var _orgZPos:Number; private var _orgYPos:Number; private var _data:BitmapData; private var _zpos:Number; public function TDCarouselItem(image:DisplayObject):void { _data = new BitmapData(image.width, image.height, true, 0x00FFFFFF); _data.draw(image); var bmp:Bitmap = new Bitmap(_data, "auto", true); bmp.x -= bmp.width * .5; bmp.y -= bmp.height * .5; updateDisplay(); addChild(bmp); } internal function updateDisplay():void { var angle:Number = _angle + _radians; var xpos:Number = Math.cos(angle) * _radius; _zpos = _orgZPos + Math.sin(angle) * _radius; var scaleRatio:Number = _focalLength / (_focalLength + _zpos); x = xpos * scaleRatio; y = _orgYPos * scaleRatio; scaleX = scaleY = scaleRatio; } internal function get angle():Number { return _angle; } internal function set angle(value:Number):void { _angle = value; } internal function get radius():Number { return _radius; } internal function set radius(value:Number):void { _radius = value; } internal function get focalLength():int { return _focalLength; } internal function set focalLength(value:int):void { _focalLength = value; } internal function get radians():Number { return _radians; } internal function set radians(value:Number):void { _radians = value; } // must remain public for Array.sortOn() method in OBO_3DCarousel instance. public function get zpos():Number { return _zpos; } public function set zpos(value:Number):void { _orgZPos = value; } internal function set ypos(value:Number):void { _orgYPos = value; } internal function get data():BitmapData { return _data; } } }
And a quick document class that created the .swf file above:
package { import caurina.transitions.Tweener; import com.onebyonedesign.td.OBO_3DCarousel; import flash.display.Bitmap; import flash.display.Loader; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.net.URLLoader; import flash.net.URLRequest; import flash.text.TextField; /** * Just a test of the OBO_3DCarousel class * @author Devon O. */ public class CarouselTest extends MovieClip { // on stage of .fla public var right_mc:MovieClip; public var left_mc:MovieClip; public var loading_txt:TextField; public static const XML_URL:String = "images.xml"; private var _carousel:OBO_3DCarousel; private var _imageList:XMLList; private var _numImages:int; private var _currentImage:int = 0; public function CarouselTest():void { _carousel = new OBO_3DCarousel(500, 250, 220); _carousel.useBlur = true; _carousel.y = 90; _carousel.x = 250; addChild(_carousel); right_mc.addEventListener(MouseEvent.CLICK, rightClickHandler); left_mc.addEventListener(MouseEvent.CLICK, leftClickHandler); loading_txt.text = "loading xml"; var uloader:URLLoader = new URLLoader(); uloader.addEventListener(Event.COMPLETE, xmlHandler); uloader.addEventListener(IOErrorEvent.IO_ERROR, xmlHandler); uloader.load(new URLRequest(XML_URL)); } private function xmlHandler(event:*):void { event.currentTarget.removeEventListener(Event.COMPLETE, xmlHandler); event.currentTarget.removeEventListener(IOErrorEvent.IO_ERROR, xmlHandler); if (event is IOErrorEvent) { loading_txt.text = "could not load xml file"; } else { var xml:XML = new XML(event.currentTarget.data); _imageList = xml..image; _numImages = _imageList.length(); loadImage(); } } private function loadImage():void { loading_txt.text = "loading image " + (_currentImage + 1).toString() + " / " + _numImages.toString(); var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageHandler); loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, imageHandler); loader.load(new URLRequest(_imageList[_currentImage].toString())); } private function imageHandler(event:*):void { event.currentTarget.removeEventListener(Event.COMPLETE, imageHandler); event.currentTarget.removeEventListener(IOErrorEvent.IO_ERROR, imageHandler); if (event is IOErrorEvent) { loading_txt.text = "could not load image no " + _currentImage; } else { var image:Loader = event.currentTarget.loader; _carousel.addItem(image); } if (++_currentImage < _numImages){ loadImage(); } else { loading_txt.text = "complete"; } } private function rightClickHandler(event:MouseEvent):void { _carousel.targetRotation += 360 / _carousel.numItems; Tweener.addTween(_carousel, { zRotation:_carousel.targetRotation, time:1, transition:"easeOutBack" } ); } private function leftClickHandler(event:MouseEvent):void { _carousel.targetRotation -= 360 / _carousel.numItems; Tweener.addTween(_carousel, { zRotation:_carousel.targetRotation, time:1, transition:"easeOutBack" } ); } } }
Go nuts and let me know what troubles you run into. It wasn’t too extensively tested.
EDIT:
Download a sample .fla (including script, xml, some pictures and all that crap) of the above application here.
In other news, The Bureau of Public Secrets website (a site dedicated to Situationist and related thought ramblings) turned ten years old today. Ken Knabb, creator, translator, and, I’m sure, all around fun guy, shares his thoughts on the subject here.









I made a 3D carousel in Flex 4 and Flash Player 10 , acouple of days ago. Here is the link:
http://pradeekonflex.wordpress.com/2008/08/04/3d-carousel-in-flash-player-10/
Devon… man, thank you. You needn’t…
looks good, Pradeek, but the z sorting is a bit off. I know it’s a pain in AS3 FP 9 or 10.. I’m still looking for a better method than I used here. Open to suggestions..
Thanks, I’m working on a more cleaner and easy to use method. I’ll let you know if it works.
Yes, this is just a newbie stupid question…
…but I’m an AS3 newbie, and was 37 yo… so I don’t will be ashamed to question….PLEASEEE… write two lines to make the example running with flash…
((After a few years sellin’n riding bycicles (and a couple of IronMan(s) too), I’m back in the arena. I spent my summer holidays trying to understand AS3 and I’m only a few ephiphany from “the programmer’s dawn”, as defined by my programming teacher @ school 20 years ago (remember Peek 33,33?)
Hey Luciano, I’d be happy to help if I can, but can you describe what you’d like to see it do. Not sure what you mean by “running with flash”. And I haven’t heard “peek” in years.. There used to be a whole lot of peeking and poking going on 20 years ago..
Hi Devon,
your gallery is fast and clean, I’d like to use it for my site building. With click-on-image feature to show before-after slider effects on photos. But I did not installed FLEX yet, and having some trouble with class assignment in FLASH and stage composing. I’ll appreciate very much a simple .fla AS3 script that refers to Class OBO_3DCarousel. Not much explanation…. just to let me understand…. thanx a lot!
Luciano
ps Just for fun a little story, referring to your “coffee for coder”…In Naples, Italy, my city, there’s the habit for those don’t have money, to go to the famous “Caffè Gambrinus” and have an espresso coffee for free, that someone unknown payed for him just telling the barman: “someone payed coffee for me??” ;-)
Hey Luciano,
I just edited my post and included a download of the .fla and all the trimmings. I hope that might help you out.
That free Italian coffee sounds great. I lived in Sicily for a couple years and miss the cappuccino the most. You wouldn’t happen to be related to “Lucky” would you? (Just kidding.. :) )
Ciao Devon!
great job! now I’m trying to add some click-on-image feature… a long night awaits me (probably more than one night!)
Grazie!
LL
Prego! Good luck with the image interaction. Not sure how I would do that myself.
Hi Devon, nice work on the site. I noticed that if I click a “subject” in the right column of this blog (Actionscript, Flash, AIR, etc) I get the category results “one by one”. That is, one page at a time. Is that by design? I was hoping I might click a category and browse all your posts on a subject.
Cheers,
Skye
Interesting.. I’ve never actually clicked on one before.. :) It may because I have it set up to only display one blog post per page and that gets carried over to subject browsing as well. I’ll play around and see if I can change that.
Made the carousel as a gumbo skin. Not the best of ways, but… pl. check it out here
http://pradeekonflex.wordpress.com/2008/08/29/3d_carousel_as_flex_skin/
Cool, Pradeek.. I’ve downloaded the SDK, but haven’t began playing with Gumbo yet.. Might have to play around this weekend.
Ciao Devon!
I worked hard on my project with your 3D Gallery (www.digitalmakeup.it), a sort of training-on-job (addicted to script!)… but now there’s a problem: I can’t really understand how the carousel is building. I placed a new tag in the XML to recover (using an XMLLIST class) an alternative address and open High res images on click. Tracking each left or right click I got an index to read XML, but the carousel’s items was not placed with the same XML’s order and wrong image is opening.
I know that one simple way is changing order of the XML’s tag of the High res images, but I’m still trying to understand HOW the _carousel build itself by add(ing)Items.There’s something in the way it builds that I don’t understand…. please can you explain in “blocks” how it works? I missed the “kill” function!
thanks devon!
luciano
I use a custom query string to display categories, archives etc in a complete list .. overriding front page settings.
http://moshublog.com/2007/10/30/custom-query-string-reloaded-for-wordpress-23-with-tag-support/
Luciano, it looks good so far. I’ll try to put together a more commented version when I get some time. I’m not altogether sure how I’d go about doing what you want though. Maybe finding the angle increment to present a new picture and keeping track of what’s up front that way.. Or maybe just starting from scratch and creating a new one with that purpose in mind.
DEIRDRE! I can’t download that thing. Don’t know if that’s site or my browser.. If I use it will mess up previous posts?
Thanks for sharing this – I am trying to use it.
I downloaded the zip and compiled it to baseline that I had everything in place.
I get
caurina\transitions\Tweener.as:1054:
1046: Type was not found or was not a compile-time constant: TweenListObj.
line 1054 is handleError function of Tweener, so might be a side effect?
Never mind – I put tweener in my com folder
doh!
Hopefully a better comment/question: how can I change the orientation of this carousel to make it into an upright wheel?
Answer: Switch x and y values in the CarouselItem class
Glad you got that sorted, David.. Hope it helps you out..
Hi I am playing with this class and making buttons to load a specific item in the carousel and it works good, the only problem I have is that the order of images in the carousel does not correspond with the ones in the xml file.. after like 4 items it gets a different order.
Is there a way to keep the same order of files from the xml in the carousel ?
how do we send the carousel to lowest level layer so that other movie clips are on top of it?
Hey Chris, I would simply watch the order in which the images appear and plan accordingly. The images are loaded in order but then positioned according to the logic in the addItem() method.
Fred, the carousel inherits from the Sprite class and can be handled like any other Sprite. Using addItemAt(0, myCarousel) will always push it below anything else already on the Display List.
Hello Devon, I was wondering how can i add the reflection for the items… I tried different ways but couldn’t get it right. can you help me out ?
Tite! thanks for sharing.
trying to add button functionality to the images so I could click on them and bring them forward or run another function or …..
wondering if you have any suggestions for turning the images into clickable items?
Wow… WOW… OMG WOW!
You have NO idea how long I have been looking for someone that coded this in AS3, CORRECTLY, WITH all the appropriate handlers and linked it to an xml list of images. I don’t know how many resources exactly, but in the past 48hrs, this is the FIRST that has what I was looking for!
THANK YOU!
Nick
Devon,
Not sure how often you check this blog anymore, but if you could let me know if you could answer a few questions about the code, it’d be appreciated. Just trying to add some nifty things to it :)
Nick
Hey, Nick. Been crazily busy of late, having just changed jobs and countries, but I still read comments. What kind of questions you got? And thank you for the kind words..
Hey man,
Thank you for taking the time out of your schedule first off. Secondly, I’m having issues (though I’m close to the solution) importing the swf of the carousel into a movie clip. The issue is that even though the movie clip is the same size as the the carousel was in it’s file, it’s being displayed at like 3-4 times the size, and there doesn’t seem to be a way to resize it.
The code I have thus far for the movie clip looks like this atm:
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.*;
import flash.net.URLRequest;
var movieLoader:Loader = new Loader();
var movieURL:URLRequest = new URLRequest(“carousel.swf”);
movieLoader.load(movieURL);
carousel_mc.addChild(movieLoader);
trace(carousel_mc.height, carousel_mc.width);
var a:Number = carousel_mc.height;
var b:Number = carousel_mc.width;
movieLoader.contentLoaderInfo.addEventListener(Event.INIT, initHandler);
function initHandler(event:Event):void {
var movieLoader:Loader = Loader(event.target.loader);
var info:LoaderInfo = LoaderInfo(movieLoader.contentLoaderInfo);
movieLoader.height = a;
movieLoader.width = b;
}
Any hints you could throw my way? I’m not new to actionscript, but I am definitely new to doing something so complicated.
[...] handy tool for 3D rotation is this class, from [...]
Thanks devon.
I’ve tried to modify this source to make look smaller and to fit on my purpose onto revising my company’s website.
The image modification was nothing, but it is very hard to find the place of initial location of the images and hence, hard to find to contracting the images the main file is displaying.
Anyway, it’s nice to see the codes insides of folders which were developed by MIT guys. It’s very very hard to understand and I actually dropped it off :)
Well, thanks for your devotion.
Devon,
Thanks a lot for a great carousel. As other people have commented it is easy to use and seems to be built well.
I have been working with it and had a quick question.. Is there a way to add more info to each of the carousel items? ie: add text about the items that would also come from the xml file (think file card)
Ive been playing around with it and so far havent had any luck. Im assuming i would create the text fields in the code area where the _data bitmap is created?
Thanks again and I appreciate any additional insight!
NIck
Wonderful Job Devon! I like your style of coding and got a chuckle about the side story on Lee’s carousel. :)
I’ve played with Lee’s in Flex but gave yours a try with a platform called Koolmoves and added a few bits just to test the KM compiler (it just recently added AS3 support) which is running here:
http://krazyaboutpizza.co.uk/?p=90
We’re ramping up to evangelize for KM’s new version so the domain will change to KM-CODEX.com in the next few days and I’ll be sure to send KM users your way to learn from your expertise. Thanks for being so generous and I look forward to your future offerings!
Chris
first off, great carousel. I’m actually trying to figure out how to tilt the angle of the carousel slightly, as though you can see the carousel more at a top view looking down. Anyway you could help me with it? I’m not a coder as you can tell, but any help would be appreciated. Thanks in advance.
Tom
hi devon,
i have no idea how to sort the images correctly. i have the same problem chris had bevor, but cant finid any solution. maybe you can give me one more hint how to manage this problem.
thank you
christian
Hi Devon,
really cool one – but how can i change the cirlce into an elliptical movement
what are the settings so that the main item is not scalled?
Hello I’m knew to this whole thing and I’m trying to get this carousel to work without using xml and loading the movie clips from the library as carousel items. Any suggestions on how to do this. I’ve tried scaling back the code to make it work this way but I haven’t been successful.
Is there anyway to make the far back two blurred images be on the corresponding sides of the first two images? [][] [] [][]? Almost like a card stack?
Loving this!
this is the most amazing carousel component i’ve seen done in flash/flex.. very well done! thank you :)
Hello Devon, nice work! Have you the solution for the problem with the sort the images in correctly order?
Thanks in advance!
RSS feed for comments on this post. / TrackBack URI