LogoLogo
  • Home
  • Projects
  • About
  • Contact

Billboard Transition Effect in Flash Player 10

Devon O. · August 03, 2008 · Actionscript, Flash · 9 comments
4

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:

C#
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:

C#
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..

  Facebook   Pinterest   Twitter   Google+
  • Another Year, Another Look
    March 21, 2010 · 2 comments
    1803
    4
    Read more
  • Glitchy Video
    October 17, 2010 · 6 comments
    2946
    8
    Read more
  • Using Attributes for Property Validation
    December 24, 2019 · 0 comments
    <p>Programmers are always looking for ways to write safer more bullet proof
    1520
    4
    Read more
9 Comments:
  1. I’m getting an empty movie and when run in debug mode it complains about missing images.

    Phil · August 03, 2008
  2. 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.

    Devon O. · August 04, 2008
  3. 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

    question4you · August 06, 2008
  4. 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..

    Devon O. · August 07, 2008
  5. 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)

    question4u · August 07, 2008
  6. 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

    question4u · August 07, 2008
  7. 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/

    Devon O. · August 07, 2008
  8. 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

    Al · November 21, 2008
  9. 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

    matt · June 05, 2009

Leave a Comment! Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Devon O. Wolfgang

AIR | Unity3D | AR/VR

Unity Certified Developer

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development” and “Starling Game Development Essentials”

Reviewer of “The Starling Handbook”

Unity Engineer at Touch Press.

Categories
  • Actionscript (95)
  • AIR (16)
  • Flash (99)
  • Games (7)
  • Liberty (13)
  • Life (53)
  • Shaders (20)
  • Unity3D (21)
Recent Comments
  • Devon O. on Unity Ripple or Shock Wave Effect
  • Feral_Pug on Unity Ripple or Shock Wave Effect
  • bavvireal on Unity3D Endless Runner Part I – Curved Worlds
  • Danielius Vargonas on Custom Post Processing with the LWRP
  • Luca G on Unity Ripple or Shock Wave Effect
Archives
  • December 2020 (1)
  • December 2019 (1)
  • September 2019 (1)
  • February 2019 (2)
  • December 2018 (1)
  • July 2018 (1)
  • June 2018 (1)
  • May 2018 (2)
  • January 2018 (1)
  • December 2017 (2)
  • October 2017 (1)
  • September 2017 (2)
  • January 2017 (1)
  • July 2016 (1)
  • December 2015 (2)
  • March 2015 (1)
  • September 2014 (1)
  • January 2014 (1)
  • August 2013 (1)
  • July 2013 (1)
  • May 2013 (1)
  • March 2013 (2)
  • December 2012 (1)
  • November 2012 (1)
  • September 2012 (3)
  • June 2012 (2)
  • May 2012 (1)
  • April 2012 (1)
  • December 2011 (2)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (1)
  • May 2011 (2)
  • April 2011 (2)
  • March 2011 (1)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (3)
  • October 2010 (5)
  • September 2010 (1)
  • July 2010 (2)
  • May 2010 (5)
  • April 2010 (2)
  • March 2010 (7)
  • February 2010 (5)
  • January 2010 (5)
  • December 2009 (3)
  • November 2009 (1)
  • October 2009 (5)
  • September 2009 (5)
  • August 2009 (1)
  • July 2009 (1)
  • June 2009 (2)
  • May 2009 (6)
  • April 2009 (4)
  • March 2009 (2)
  • February 2009 (4)
  • January 2009 (1)
  • December 2008 (5)
  • November 2008 (2)
  • September 2008 (1)
  • August 2008 (6)
  • July 2008 (6)
  • June 2008 (9)
  • May 2008 (4)
  • April 2008 (3)
  • March 2008 (4)
  • February 2008 (9)
  • January 2008 (7)
  • December 2007 (6)
Copyright © 2017 Devon O. Wolfgang