Didn’t really know what else to call this one. I’m sure you’ve all seen those billboards, though, that periodically spin small sections of themselves around to reveal a whole ‘nother billboard beneath advertising still more crap you don’t need, can’t afford but have to have. I just love those things. Used to mesmerize me for hours as a kid. Well maybe not hours, but for longer periods of time than your usual kid. Anyway, I always though that would be a pretty cool thing to do in Flash. Of course it could have been done using Papervision3d (or one of the countless other 3d packages all the rage these days) long ago. But now, using Flash Player 10 (which the example below, of course, requires), it can be done natively within Flash.
Example:
And the class that does all the work:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
package com.onebyonedesign.transitions { import caurina.transitions.Tweener; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.display.Sprite; import flash.events.Event; import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.Timer; /** * @author Devon O. */ public class OBO_BillboardTransition extends EventDispatcher { public static const HORIZONTAL:String = "horizontal"; public static const VERTICAL:String = "vertical"; private var _imgparent:DisplayObjectContainer; private var _timer:Timer; private var _direction:String; private var _imgfront:DisplayObject; private var _imgback:DisplayObject; private var _numsections:int; private var _sections:Vector.<Sprite>; private var _bmpdataObjects:Vector.<BitmapData>; private var _bmpObjects:Vector.<Bitmap>; private var _numDone:int; private var _sectionHolder:Sprite = new Sprite(); /** * OBO_BillboardTransition class. Transitions two DisplayObject instances in a "spinning billboard" fashion. * Throws Event.COMPLETE when transition is finished. * Requires caurina.transitions package. Google for "Tweener" on GoogleCode. * * @param The DisplayObjectContainer parenting the image that will be transitioned out. * @param The image to be transitioned out (must already be on display list) * @param The image that will be transitioned in (should be same size as imgfront) * @param The number of spinning sections * @param Either OBO_BillboardTransition.HORIZONTAL or OBO_BillboardTransition.VERTICAL ("horizontal" or "vertical") */ public function OBO_BillboardTransition(imgparent:DisplayObjectContainer, imgfront:DisplayObject, imgback:DisplayObject, numsections:int, direction:String = "horizontal") { _imgparent = imgparent; _imgfront = imgfront; _imgback = imgback; _numsections = numsections; _direction = direction; } /** * Call this to begin the transition. */ public function start():void { init(); _timer = new Timer(100, _numsections); _timer.addEventListener(TimerEvent.TIMER, timerHandler); _timer.start(); } public function get direction():String { return _direction; } public function set direction(value:String):void { _direction = value; } public function get imgfront():DisplayObject { return _imgfront; } public function set imgfront(value:DisplayObject):void { _imgfront = value; } public function get imgback():DisplayObject { return _imgback; } public function set imgback(value:DisplayObject):void { _imgback = value; } public function get numsections():int { return _numsections; } public function set numsections(value:int):void { _numsections = value; } private function init():void { _numDone = 0; _sections = new Vector.<Sprite>(); _bmpdataObjects = new Vector.<BitmapData>(); _bmpObjects = new Vector.<Bitmap>(); _sectionHolder = new Sprite(); switch(_direction) { case "horizontal" : createHorizontalSections(); break; case "vertical" : createVerticalSections(); break; } } private function createHorizontalSections():void { var sectionHeight:Number = _imgfront.height / _numsections; var sectionWidth:Number = _imgfront.width; var point:Point = new Point(); var frontData:BitmapData = new BitmapData(_imgfront.width, _imgfront.height); var backData:BitmapData = new BitmapData(_imgback.width, _imgback.height); frontData.draw(_imgfront); backData.draw(_imgback, new Matrix(1, 0, 0, -1, 0, _imgback.height)); _bmpdataObjects.push(frontData, backData); for (var i:int = 0; i < _numsections; i++) { var fbmd:BitmapData = new BitmapData(sectionWidth, sectionHeight); var bbmd:BitmapData = new BitmapData(sectionWidth, sectionHeight); fbmd.copyPixels(frontData, new Rectangle(0, i * sectionHeight, sectionWidth, sectionHeight), point); bbmd.copyPixels(backData, new Rectangle(0, ((_numsections - 1) - i) * sectionHeight, sectionWidth, sectionHeight), point); _bmpdataObjects.push(fbmd, bbmd); var fbmp:Bitmap = new Bitmap(fbmd); var bbmp:Bitmap = new Bitmap(bbmd); _bmpObjects.push(fbmp, bbmp); var s:Sprite = new Sprite(); fbmp.x -= sectionWidth * .5; fbmp.y -= sectionHeight * .5; bbmp.x -= sectionWidth * .5; bbmp.y -= sectionHeight * .5; s.addChild(bbmp); s.addChild(fbmp); s.x = sectionWidth * .5; s.y = i * sectionHeight + sectionHeight * .5; _sections.push(s); _sectionHolder.addChild(s); } _sectionHolder.x = _imgfront.x; _sectionHolder.y = _imgfront.y; _imgparent.addChildAt(_sectionHolder, _imgparent.getChildIndex(_imgfront)); _imgparent.removeChild(_imgfront); } private function createVerticalSections():void { var sectionHeight:Number = _imgfront.height; var sectionWidth:Number = _imgfront.width / _numsections; var point:Point = new Point(); var frontData:BitmapData = new BitmapData(_imgfront.width, _imgfront.height); var backData:BitmapData = new BitmapData(_imgback.width, _imgback.height); frontData.draw(_imgfront); backData.draw(_imgback, new Matrix(-1, 0, 0, 1, _imgback.width)); _bmpdataObjects.push(frontData, backData); for (var i:int = 0; i < _numsections; i++) { var fbmd:BitmapData = new BitmapData(sectionWidth, sectionHeight); var bbmd:BitmapData = new BitmapData(sectionWidth, sectionHeight); fbmd.copyPixels(frontData, new Rectangle(i * sectionWidth, 0, sectionWidth, sectionHeight), point); bbmd.copyPixels(backData, new Rectangle(((_numsections - 1) - i) * sectionWidth, 0, sectionWidth, sectionHeight), point); _bmpdataObjects.push(fbmd, bbmd); var fbmp:Bitmap = new Bitmap(fbmd); var bbmp:Bitmap = new Bitmap(bbmd); _bmpObjects.push(fbmp, bbmp); var s:Sprite = new Sprite(); fbmp.x -= sectionWidth * .5; fbmp.y -= sectionHeight * .5; bbmp.x -= sectionWidth * .5; bbmp.y -= sectionHeight * .5; s.addChild(bbmp); s.addChild(fbmp); s.x = i * sectionWidth + sectionWidth * .5; s.y = sectionHeight * .5; _sections.push(s); _sectionHolder.addChild(s); } _sectionHolder.x = _imgfront.x; _sectionHolder.y = _imgfront.y; _imgparent.addChildAt(_sectionHolder, _imgparent.getChildIndex(_imgfront)); _imgparent.removeChild(_imgfront); } private function timerHandler(event:TimerEvent):void { switch(_direction) { case "horizontal" : flipHorizontalSection(event.currentTarget.currentCount - 1); break; case "vertical" : flipVerticalSection(event.currentTarget.currentCount - 1); } } private function flipHorizontalSection(count:int):void { var section:Sprite = _sections[count]; Tweener.addTween(section, { rotationX:180, time:.25, transition:"easeOutQuad", onUpdate:checkAngle, onUpdateParams:[section], onComplete:tallyDone } ); } private function flipVerticalSection(count:int):void { var section:Sprite = _sections[count]; Tweener.addTween(section, { rotationY:-180, time:.25, transition:"easeOutQuad", onUpdate:checkAngle, onUpdateParams:[section], onComplete:tallyDone } ); } private function checkAngle(s:Sprite):void { if (s["rotationX"] > 90 || s["rotationY"] < -90) { s.getChildAt(1).visible = false; } } private function tallyDone():void { if (++_numDone == _numsections) { swapImage(); cleanup(); dispatchEvent(new Event(Event.COMPLETE)); } } private function swapImage():void { _imgback.x = _sectionHolder.x; _imgback.y = _sectionHolder.y; var ind:int = _imgparent.getChildIndex(_sectionHolder); _imgparent.removeChild(_sectionHolder); _imgparent.addChildAt(ç¹imgback, ind); } private function cleanup():void { var i:int = _bmpdataObjects.length; while (i--) { _bmpdataObjects[i].dispose(); delete _bmpdataObjects[i]; } i = _bmpObjects.length; while (i--) { delete _bmpObjects[i]; } i = _sections.length; while (i--) { delete _sections[i]; } _sections = null; _bmpdataObjects = null; _bmpObjects = null; _sectionHolder = null; _imgfront = null; _timer.removeEventListener(TimerEvent.TIMER, timerHandler); _timer.reset(); _timer = null; } } } |
And for testing purposes, the document class that produces the above .swf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
package { import com.onebyonedesign.transitions.OBO_BillboardTransition; import flash.display.Bitmap; import flash.display.DisplayObjectContainer; import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.filters.DropShadowFilter; import flash.text.AntiAliasType; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; /** * Example of OBO_BillboardTransition usage * @author Devon O. */ [SWF(width="500", height="400", backgroundColor="#869CA7", framerate="31")] public class BillboardTest extends Sprite { private var _trans:OBO_BillboardTransition; [Embed (source = "assets/img1.jpg")] private var ImageOne:Class; [Embed (source = "assets/img2.jpg")] private var ImageTwo:Class; [Embed (source = "assets/img3.jpg")] private var ImageThree:Class; [Embed (source = "assets/img4.jpg")] private var ImageFour:Class; private var _firstImage:Bitmap; private var _imageHolder:Sprite = new Sprite(); private var _images:Array = []; private var _btn:Sprite; public function BillboardTest():void { _images.push(new ImageOne() as Bitmap, new ImageTwo() as Bitmap, new ImageThree() as Bitmap, new ImageFour() as Bitmap); // get the first image ready. Best to keep it in a separate Sprite container for easier management. _firstImage = _images[0]; _imageHolder.x = 75; _imageHolder.y = 60; _imageHolder.addChild(_firstImage); _imageHolder.filters = [new DropShadowFilter(4, 90, 0x000000, 1, 6, 6, 1, 3)]; addChild(_imageHolder); // create the Transition instance _trans = new OBO_BillboardTransition(_imageHolder, _firstImage, _images[1], 5); _trans.addEventListener(Event.COMPLETE, ontranscomplete); // create a button that will start the transition _btn = new Sprite(); _btn.graphics.beginFill(0x660000); _btn.graphics.drawRect(0, 0, 85, 20); _btn.graphics.endFill(); _btn.x = int((_imageHolder.x + (_imageHolder.width / 2)) - (_btn.width / 2)); _btn.y = _imageHolder.y + _imageHolder.height + 8; _btn.buttonMode = true; _btn.useHandCursor = true; var btnText:TextField = new TextField(); btnText.autoSize = TextFieldAutoSize.LEFT; btnText.selectable = false; btnText.mouseEnabled = false; btnText.antiAliasType = AntiAliasType.ADVANCED; var fmt:TextFormat = new TextFormat("_sans", 11); btnText.defaultTextFormat = fmt; btnText.text = "View Transition"; btnText.x = 3; btnText.y = 1; _btn.addChild(btnText); _btn.addEventListener(MouseEvent.CLICK, clickHandler); addChild(_btn); } // when the button is clicked disable the button and start the transition private function clickHandler(event:MouseEvent):void { _btn.mouseEnabled = false; _trans.start(); } // when the transition is complete make a few random changes to it and re-enable the button private function ontranscomplete(event:Event):void { // swap the old back image for the new front image. _trans.imgfront = _trans.imgback; // create a random back image _trans.imgback = _images[Math.floor(Math.random() * 4)]; // create a random direction _trans.direction = (Math.random() < .5) ? OBO_BillboardTransition.HORIZONTAL : OBO_BillboardTransition.VERTICAL; // let user click the button _btn.mouseEnabled = true; } } } |
A few caveats if you plan on using this:
Seems if you run it long enough producing transition after transition, processing power will go through the roof. I'm not sure why. I thought I scrubbed everything away in the cleanup() method of OBO_BillboardTransition.as. If anyone sees something I missed, please don't hesitate to say.
Also, the image (or movieclip or what-have-you) you pass to the class as the imgfront property (the item that will be transitioned out), must already be added to the display list. Bad things may happen otherwise.
And well, it could use a bit of cleaning up. You're welcome to play with it as you desire..
I’m getting an empty movie and when run in debug mode it complains about missing images.
Sorry, Phil. Forgot to specifically mention that you’ll need an “assets” directory containing four image files named “img1.jpg”, “img2.jpg”, “img3.jpg” and “img4.jpg”.
I also forgot to mention they should all be the same size. Though it may be interesting if they aren’t – I haven’t tried different sized pictures yet.
hey devon,
sorry to bring you back to the past but i recently saw this tooltip class you created
http://blog.onebyonedesign.com/?p=11
i was wondering if you could explain why there is a visible rectangular “ghosting” on the tooltip (look closely and you can see a faint rectangle that is probably the container of the movie clip that contains the tooltip)? Is there any way to get rid of it?
fyi, sorry for contacting you here on this post but there was no other way to contact you
thanks
I’m not sure I’m looking at the same thing, but what you may be seeing is the result of having a low alpha object combined with a dropshadow. You could try making sure the alpha is set to 1 and/or remove the DropShadow filter (which is set in the addTip() method). You also might want to pick up the latest copy at http://www.onebyonedesign.com/flash/ui/ (along with a few other things). Hopefully that’ll help out..
a couple of things. if you look at the tooltip example “you” posted (not one that i downloaded an compiled myself in flash) on the link above, it definitely has a square ghost box around the entire tool tip. check it out. you don’t see that?
Now, if you look at the tooltip in your classes documentation at this link: http://www.onebyonedesign.com/flash/ui/.. the tooltip does NOT have a bounding ghost box???!!! wtf?
I played around with this locally. What i discovered is this. It’s definitely the _ds (dropshadow) filter that’s causing the ghosting. If you comment it out, the ghosting goes away. I played with every possible dropshadow parameter (set quality high, no alpha, high strength, etc) all to no avail. I even tried all these combos with alpha of 1 on the the tooltip itself. Still didn’t work.
What i DID discover is this. Not sure if you have any insights. The reason the ghosting is showing up on your blog post example and my downloaded attempts of your classes is that the UNDERLYING SHAPE in the blog post example is a shade of GRAY (eg. 0x3e3e3e). The minute you change it to a non grey tone (could be anything), like black, blue, green, etc, the ghosting goes away.
The reason you don’t see the ghosting in your class examples page is the background shapes are NOT gray but solid black! wtf?? Try it yourself and tell me if you don’t see what i’m seeing.
The dropshadow filter must be having a problem overlaying a neutral shade of gray object. Could it be a blend mode problem??
NOTE: i ask the question because i’ve seen this ghosting on a lot of online web examples and it occasionally has started creeping up in my own work and i couldn’t pinpoint it until now. Sucks when you use a lot of gray / neutrals in your work.
If you have any insights i’m dying to nip this one in the bud. Not being able to use filters over neutral gray would seem like a bug to me.
Sidenote: what tool do you use to make your class documentation. i’ve seen other’s use it. it’s tight and neat and i’d like to use the same myself.
thanks (sorry for long post)
Another separate question. I’d like to see the example you outline in this post. however, it requires flash player 10 which i haven’t installed yet.
If i install FP10, does it install along side of my existing 9 player or does it replace it. It might not be a big deal, but i don’t want it to screw up my existing FP9 dev environment.
thanks
Now that you mention the dark greys, I know what you’re talking about. I have seen this problem both in my own work and others’. I really have no idea what may cause this or what the solution may be. Perhaps a different color shadow (dark grey rather than black, e.g.) would do the trick. It is annoying, but I’ve seen it so much, that I tend to ignore it these days.
The documentation generator is Adobe’s ASDoc.exe which comes with Flex (both Flex Builder and the free Flex SDK). It’s a command line program, but I’ve made two different GUI’s for it using SWFKit for one and AIR for another. See this post for more info: http://blog.onebyonedesign.com/?p=70
When installing the Flash player 10 beta, the new version will only replace (and it will replace) the browser plugin. The standalone player used in Flash development will remain version 9, so current development should not be affected. There’s more information here: http://labs.adobe.com/technologies/flashplayer10/
Hi there
I have been admiring this effect for 2days now and I have tried to get it into Flex but I am having a problem understanding the _numsections bit, etc. I am so used to using effects that have been built for Flex that I don’t know where to begin to get this preped for Flex, using the xmlns=” ” and then just using a viewstack with click functions to have the transition play between navigation.
Please spare some help when you have time to assist with this, but only if you have time, I won’t dare rush you in anyway.
Thanks
Al
hi, I can’t for the life of me figure out what these are:
Vector.
Vector.
and
Vector.
Can someone help me? When I try to compile my code is says “Vector” can’t be found…
Thanks