Stage RESIZE and the stageWidth and stageHeight Bug
A while ago, I came across a situation where my Flash application would launch but the stage.stageWidth and stage.stageHeight properties would always return 0 within my document class’s constructor. As I needed to access those properties at runtime and I did not want to hardcode those values to a pair of variables, it drove me to seek an answer to this problem.
Turns out that when the stage is initialized, its stageWidth and stageHeight properties are 0 until a Stage RESIZE event is triggered. This event is dispatched when the scaleMode property of the Stage object is set to StageScaleMode.NO_SCALE and the SWF file is resized ie. when the stage is rendered by the Flash Player. So if you need to add a display object onto the stage and its positioning is dependent on the stageWidth or stageHeight properties at runtime, it’s best handled by a listener that is subscribed to the Stage RESIZE event.
Interestingly, despite universal claim that the Flash Player behaves exactly the same on all browsers, this problem only appears in a couple of browser/plugin configurations, namely FireFox/OSX and IE/Win. And strangely, on Safari and Firefox/Win, the RESIZE event is not even dispatched on page load, and neither does the Flash Player register the stageWidth and stageHeight properties as being 0. I have not tested the problem on other platforms but you are certainly welcome to do so yourselves and report your observations in the comments section. Eventually, it would be nice if we can get a straight answer from Adobe concerning this bug.
In the following demo code, I’ve attached a Sprite containing a Rectangle graphic and a TextField, to the stage once before the RESIZE event and once, after. You can see the results here.
package
{
import flash.text.*;
import flash.display.*;
import flash.events.*;
public class Demo extends Sprite
{
private var rect1:Sprite;
private var rect2:Sprite;
public function Demo()
{
rect1 = createNewRect("Before stage resize event.");
rect1.x = 0;
rect1.y = 0;
addChild(rect1);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
stage.addEventListener(Event.RESIZE, resizeHandler);
}
private function createNewRect(str:String):Sprite
{
var rect:Sprite = new Sprite();
rect.graphics.beginFill(0x99CCFF);
rect.graphics.drawRect(0, 0, 200, 100);
rect.graphics.endFill();
var fmt:TextFormat = new TextFormat();
fmt.font = "Arial";
fmt.size = 12;
var tf:TextField = new TextField();
tf.width = 200;
tf.height = 100;
tf.multiline = true;
tf.defaultTextFormat = fmt;
tf.text = str + "\n" + "stage.stageWidth: " + stage.stageWidth + "\n" +
"stage.stageHeight: " + stage.stageHeight;
rect.addChild(tf);
return rect;
}
private function resizeHandler( event:Event ):void
{
if (rect2 != null && contains(rect2)) {
removeChild(rect2);
}
rect2 = createNewRect("After stage resize event.");
rect2.x = (stage.stageWidth - rect2.width) / 2;
rect2.y = (stage.stageHeight - rect2.height) / 2;
addChild(rect2);
}
}
}
October 3rd, 2008 at 2:53 pm
A solution that I have found that seems to work across all browsers – although its very dirty – is to set a timer when the app begins. Every time the timer goes off (every 200 ms or so) it checks to see if stage.stageWidth == 0. If that is the case, do nothing. If that’s NOT the case, then stop the timer and begin the app.
October 6th, 2008 at 3:01 pm
Thanks, Adam. Yes, that’s one solution. The other option is to use an ENTER_FRAME listener and perform the same check on stage.stageWidth.
October 22nd, 2008 at 5:28 pm
Now everyone is talking about the American economy and eclections, nice to read something different. Eugene
November 9th, 2008 at 2:48 pm
I just came across this problem…
It appears to only be the case when I compile via Flex SDK (instead of IDE).
Any better solutions?
December 3rd, 2008 at 9:26 am
A good old FrameDelay usually does the trick….. AsapLibrary has this class, which is pretty great
http://asaplibrary.org/api/html/org_asaplibrary_util_FrameDelay.html
December 11th, 2008 at 4:01 am
[...] Stage RESIZE and the stageWidth and stageHeight Bug | hubflanger.com | adventures in code [...]
December 11th, 2008 at 3:51 pm
Erik, cool. Will give that a try.
December 29th, 2008 at 10:42 pm
I’m familiar with this, it usually happens when you’re subclassing objects from the library that have a lot of other classes imported. Use the flash.events.Event.ADDED_TO_STAGE and therefore delay your init. You’ll get your stage properties from the root at that point.
February 2nd, 2009 at 1:56 am
Wilson: I have been able to replicate this in simple one Class tests with out any Objects in the Library
February 23rd, 2009 at 4:48 am
Code below, this seems to work for me on all browsers :
Preloader is the document class of my project
// ———————————————
public function Preloader()
{
_isReady = false;
stage.scaleMode = StageScaleMode.NO_SCALE;
LayoutManager.getInstance().setStage(this.stage);
Model.STAGE = stage;
stage.frameRate = 31;
stage.addEventListener( Event.RESIZE,stage_resizeHandler);
_stageTimer = new Timer(200);
_stageTimer.addEventListener( TimerEvent.TIMER,timer_tickHandler);
_stageTimer.start();
}
private function timer_tickHandler(event : TimerEvent) : void
{
if ( Model.STAGE.stageWidth > 0 && Model.STAGE.stageHeight > 0 )
{
initPreloader();
}
}
private function stage_resizeHandler(event : Event) : void
{
if ( Model.STAGE.stageWidth > 0 && Model.STAGE.stageHeight > 0 )
{
initPreloader();
}
}
private function initPreloader() : void
{
if ( !_isReady )
{
_isReady = true;
_stageTimer.stop();
stage.removeEventListener( Event.RESIZE,stage_resizeHandler);
initBackground();
initContainers();
init( );
}
}
February 23rd, 2009 at 11:08 am
@dietlev This looks like an excellent solution. Thanks!
March 5th, 2009 at 4:52 pm
[...] swf files differently from other browsers. You can read more in depth about this issue here and here, so I won’t repeat what others have said. My solution to this issue was to add a ENTER_FRAME [...]
March 16th, 2009 at 3:18 pm
i wrote generic code that will handle this problem both in flash and flex…
http://juandevelops.wordpress.com/2009/03/16/issues-happening-when-event-listener-on-stage-added-occurs/
thanks for all your posts..
March 16th, 2009 at 3:19 pm
[...] in IE for windows having swf being non-scale, and being embedded using swfobject. In this site: http://hubflanger.com/stage-resize-and-the-stagewidth-and-stageheight-properties. They mentioned that the stage dimensions can be returned as 0. they recommend to target instead [...]
March 20th, 2009 at 4:41 pm
I see this as a bug only if the Demo is serving as the document class.
If it’s not serving and the document class then I assume this is the code you are using:
var d:Demo = new Demo(); <– at this point the DisplayObject doesn’t get a reference to stage
addChild(d); <– at this point the DisplayObject does have a reference to stage.
The stage reference gets set on DisplayObjects once they are added to the stage.
I get why they did this, and DisplayObject shouldn’t know anything about the stageWidth or stageHeight, before it is added to a certain stage. Just as the DisplayObject doesn’t have a parent before it is added to the Display List.
But I also understand why you expect stage to be defined, because for all intents and purposes the stage object is a global variable and should be accessible by any subclass of DisplayObject. Problem is when do you set the stage reference. In the DisplayObject constructor, of course…. Wait… The DisplayObject is an abstract class and a call the the constructor throws an ArgumentError.
So the only logical place to set it is when a DisplayObject is added to the stage.
March 27th, 2009 at 2:51 pm
Great! I was pulling my hair out trying to figure out why this was happening. The following code seems to work for me without the need to mess around with timers, frame events etc. The following would go in a document class called Main.
public function Main():void {
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
this.loaderInfo.addEventListener(Event.COMPLETE, init);
}
private function init(e:Event):void {
if (stage.stageWidth == 0 || stage.stageHeight == 0) {
stage.addEventListener(Event.RESIZE, init);
} else {
// Init stuff goes here!
}
}
May 12th, 2009 at 4:20 pm
Try your demo in Google Chrome. Grabbing the bottom of the window and resizing vertically does not trigger the RESIZE event. Horizontal resize works as expected. Also the same case in Safari for Mac/OSX although tougher to replicate since you can only resize with the corner of the window. Looks like a problem with WebKit. Very frustrating.
September 9th, 2009 at 2:55 pm
[...] http://hubflanger.com/stage-resize-and-the-stagewidth-and-stageheight-properties/ « FITC Edmonton [...]
September 11th, 2009 at 4:41 pm
I am having the same problem as Randall mentioned. My flex app do not load in Google Chrome ; if I resize the window, it loads up fine.