Home  >  

The Official "visible vs alpha vs removeChild()" Showdown

Author photo
November 24, 2008 | | Comments (18)
AddThis Social Bookmark Button

Flash Player's display API offers three different tools for hiding display objects from the screen: the visible variable, the alpha variable, and the removeChild() method. All three tools achieve the same end result—hiding a graphic—but each tool serves a different structural need. Hence, there is no single answer to the question "Should I hide graphics with visible, alpha, or removeChild()?" Instead, developers must choose the approach that suits the task at hand based on a variety of factors. Before we consider those factors, let's take a look at visible, alpha, and removeChild() in action.

First, let's create two Sprite objects and assign them to two variables, background and ball:

// A background rectangle
var background:Sprite = new Sprite();
background.graphics.beginFill(0x656600);
background.graphics.lineStyle(6);
background.graphics.drawRect(0, 0, 400, 300);

// A circular "ball" graphic
var ball:Sprite = new Sprite();
ball.graphics.beginFill(0xDFDA98);
ball.graphics.lineStyle(6);
ball.graphics.drawCircle(0, 0, 40);

Next, let's move ball over a bit:

ball.x = 275;
ball.y = 100;

Now we'll make ball a display child of background:

background.addChild(ball);

And, finally, we'll place background on the display list. Assume, for this example, that mainCanvas refers to a Sprite that is already on the display list:

mainCanvas.addChild(background);

Figure 1 shows the result of the preceding code.

Figure 1. The background and ball objects.

Now let's consider three different ways to hide ball from the screen. First, we could set visible to false, as follows:

ball.visible = false;

Or, we could hide ball by setting alpha to 0, as follows:

ball.alpha = 0;

Finally, we could hide ball by removing it from the display list entirely, as follows:

background.removeChild(graphic);

In all three cases, ball is hidden from view. However, in the first two cases (using visible and alpha), even though ball is removed from the screen, it remains a child of the background container. By contrast, in the final case (using removeChild()), ball is both removed from the screen and removed from the background container object. That structural distinction is an important difference to consider when choosing between visible, alpha, and removeChild().

Choosing Between visible, alpha, and removeChild()

When deciding whether to use visible, alpha, or removeChild() in an application, developers should consider the following factors.

In the remainder of this article the term "non-visible" refers to an object hidden via the visible variable. The term "zero-alpha" refers to an object hidden via the alpha variable.

Factor 1: Non-visible children stay in the stacking order

For the first factor, let's revise our ball example so that instead of placing ball inside background, we place both background and ball directly inside mainCanvas. Here's the revised code:

// A background rectangle
var background:Sprite = new Sprite();
background.graphics.beginFill(0x656600);
background.graphics.lineStyle(6);
background.graphics.drawRect(0, 0, 400, 300);

// A circular "ball" graphic
var ball:Sprite = new Sprite();
ball.graphics.beginFill(0xDFDA98);
ball.graphics.lineStyle(6);
ball.graphics.drawCircle(0, 0, 40);

ball.x = 275;
ball.y = 100;

// Add background to mainCanvas, below ball
mainCanvas.addChild(background);
// Add ball to mainCanvas, above background
mainCanvas.addChild(ball);
Now let's consider the difference between visible and removeChild() in an example. Suppose we're making an application where the ball graphic from the preceding code must always appear on top of the background graphic, and the background must sometimes be hidden. Because the preceding code already adds background to mainCanvas before ball, background automatically appears behind ball. Hence, to "sometimes hide" background, we can simply toggle background.visible between true and false. Any time background reappears, it is correctly stacked behind ball. The methods required to hide and show background are as follows:

public function hideBackground ():void {
   background.visible = false;
}

public function showBackground ():void {
   background.visible = true;
}

By contrast, if we were to use removeChild() to hide background, then background would be removed from mainCanvas's stacking order. When showing background again, we would need to carefully re-add background at the correct depth behind ball (using addChildAt(), not addChild()). The required hide and show methods would be as follows:

public function hideBackground ():void {
   // Use an instance variable, oldBackgroundDepth, to 
   // remember background's depth
   oldBackgroundDepth = mainCanvas.getChildIndex(background);
   mainCanvas.removeChild(background);
}

public function showBackground ():void {
   mainCanvas.addChildAt(background, oldBackgroundDepth);
}

In the preceding scenario, the stack-management code required in the removeChild() implementation makes the removeChild() approach more cumbersome to implement and maintain than the visible approach.

Factor 2: 'visible = false' executes faster than removeChild()

Hiding a graphic by setting visible to false is faster than hiding it with removeChild(). The difference in speed is negligble, but could be a factor in a demanding application. In testing, setting a display object's visible variable proved to be approximately 43 times faster than calling addChild() or removeChild() on the same object. In Flash Player 9, on an 8-core Mac Pro running Windows Vista, 10000 removeChild() calls took 300ms, whereas 10000 visible assignments took 7ms.

Factor 3: Non-visible and removed children have no rendering cost

From a rendering-performance perspective, there is no practical difference between the removeChild() and visible=false approaches. In both cases, the renderer completely skips rendering any removed or non-visible objects. By contrast, objects with alpha set to 0 do have a minor rendering cost. Consider the following test results, which show the time required to render a single frame in an application containing 1000 instances of a complex vector shape. In the test, Flash Player was set to 24 frames per second, or approximately one frame every four milliseconds.

                  Children on the                     Single-frame 
                  Display List    .visible   .alpha   Elapsed Time (ms)
No Children       0               --         --       4
Non-visible       1000            false      1        4
Zero Alpha        1000            true       0        85
Fully Visible     1000            true       1        1498
90% Transparent   1000            true       .1       1997

In the preceding results, notice that the time to render a frame with no children was exactly the same as the time to render a frame with 1000 non-visible children. However, the time to render a frame with 1000 zero-alpha children was 81ms longer than the time to render a frame with 1000 non-visible children—proving that objects with alpha set to 0 have a minor rendering cost. With all children completely visible, the cost of rendering naturally increases, up to 1498ms in the test. And, of course, rendering 1000 partially visible children took the longest, at 1997ms. In relative terms, rendering transparent, overlapping objects is an expensive operation.

Factor 4: Non-visible children affect parent dimensions

Suppose a container, box, has a single child, icon:

box.addChild(icon);

Assuming box has no other content, if icon's width is 50, box's width will also be 50:

trace(icon.width);  // 50
trace(box.width);   // 50

And even when icon's visible variable is set to false, box's width, perhaps suprisingly, remains 50:

icon.visible = false;
trace(box.width);   // Still 50! (Despite the fact that 
                    // box appears empty on screen.)

The discrepancy between box's on-screen appearance and its programmatic dimensions affects layout and collision code in the application. For example, when icon is non-visible, any layout code that wishes to position graphics around box based on box's on-screen visible size must manually ignore icon's dimensions:

// Place a button to the right of box.
button.x = box.x + box.width;
if (icon.visible == false) {
   button.x -= icon.width;
}

Here's a tighter way to write the preceding code:

// Place a button to the right of box.
button.x = box.x + box.width - (!icon.visible ? icon.width : 0);

By contrast, if icon is hidden via removeChild() rather than visible, then box's width becomes 0, which intuitively matches its on-screen appearance.

box.removeChild(icon);
trace(box.width);   // 0

Once icon is removed, layout code can trust box's reported dimensions when positioning graphics, without any special "ignore non-visible children" conditions:

// Place a button to the right of box.
button.x = box.x + box.width;  // Much nicer...

In a layout engine, recursivley checking containers for non-visible children is cumbersome and slow. I've filed a bug requesting that Adobe address this issue by introducing a flag to exclude non-visible children in parent-bounds calculations. Please vote for the bug if you're affected by this issue.

Factor 5: Non-visible children can get in your way

Imagine you're creating a container, optionsPane, with 100 checkboxes as children. At any given time, 10 of the checkboxes are shown on screen and 90 have visible set to false. To determine which checkboxes are checked, your program loops over optionsPane's children:

var results:Array = new Array();
for (var i:int = 0; i < optionsPane.numChildren; i++) {
   if (CheckBox(optionsPane.getChildAt(i)).checked) {
      results.push(true);
    } else {
      results.push(false);
    }
}

But the preceding code returns the results for all 100 checkboxes, not just the visible ones. To determine which visible checkboxes are checked, the loop must conditionally "step over" any checkbox whose visible variable is false:

var results:Array = new Array();
var child:DisplayObject;
for (var i:int = 0; i < optionsPane.numChildren; i++) {
   child = optionsPane.getChildAt(i));
   if (CheckBox(child).checked && child.visible) {
      results.push(true);
    } else {
      results.push(false);
    }
}
Not difficult, but it does add some noise to the loop. If the program had used removeChild() instead of visible to hide the checkboxes, the loop wouldn't need to "step over" non-visible checkboxes.

Factor 6: Objects with alpha set to 0 receive mouse events

Unlike objects with visible set to false, objects with alpha set to 0 are considered interactive, and can receive mouse events. For example, if you place your mouse pointer over a zero-alpha object, Flash Player dispatches a MouseEvent.MOUSE_OVER event targeted at that object. Zero-alpha objects, hence, can be useful for creating non-visual elements that capture user input, such as "invisible buttons" or "easter eggs."

General Guidelines to Follow

With the preceding factors in mind, let's look at a few guidelines to follow when deciding between visible, alpha, and removeChild(). The following strategies are particularly useful in visual applications that repeatedly toggle graphics between visible and hidden states.

Recommendation 1: Use removeChild() for predictability

When in doubt, use removeChild() instead of visible because using visible can produce misleading parent dimensions. See Factor 4. Particularly avoid visible when visual dimensions matter, such as when testing for collisions in a game or when arranging graphics programmatically. Note that this recommendation would be made obsolete if Adobe were to address Flash Player Bug 741.

Recommendation 2: Use visible for performance and convenient depth management

When speed and stacking order are your key concerns, use visible instead of removeChild() because setting visible is faster than calling removeChild() (see Factor 2), and using visible facilitates easy depth management (see Factor 1). But be careful when working with dimensions in layout and collision code (see Factor 4). Furthermore, remember that even in applications that use visible to hide graphics, when a graphic is no longer needed, it should be removed from its parent container via removeChild(), and then dereferenced so it can be garbage collected. See the section "Not visible does not mean deleted", below.

Recommendation 3: Generally avoid alpha

Avoid using alpha to completely hide graphics because zero-alpha graphics take longer to render than both non-visible graphics and removed graphics. See Factor 3 and Factor 6. Hide graphics with alpha only when creating interactive hidden-graphics.

Related Considerations

When removing graphical assets from the screen, bear in mind the following issues, which are important regardless of whether you're using visible, alpha, or removeChild().

Not visible does not mean deleted

Setting an object's visible to false or alpha to 0 does not remove that object from memory. Likewise, removing an object from the display list via removeChild() does not remove that object from memory. Display objects are removed from memory only once they have been deactivated, dereferenced, and garbage collected. Regardless of your application's visibility strategy, be sure to deactivate and dereference your display objects when they are no longer needed.

Off-screen MovieClip timelines can be hazardous

Suppose you have an on-screen MovieClip object whose timeline is playing. If you hide that MovieClip using visible, alpha, or removeChild(), its timeline will continue to play. As the timeline plays, it will have several negative side-effects:

  • The timeline playback will consume system resources.
  • If the playhead advances to a frame with code, that code will execute, potentially wasting system resources and triggering undesirable program side-effects.
  • If the MovieClip object's visual contents change when a new frame is reached, the object's dimensions will also change, potentially affecting parent dimensions (see Factor 4).

Therefore, as a general rule, remember to stop the playback of all MovieClip objects that are not on screen.

Thus ends our examination of visibility management in ActionScript. Hopefully it has helped you navigate some of ActionScript's subtler display-programming issues. My thanks to Jim Corbett, lead engineer on Flash Player's display API, who fielded my research questions during the writing of this article.

Happy coding!

Read more from Colin Moock. Colin Moock's Atom feed

Comments

18 Comments

Richard Davey said:

Very interesting and useful. Do you know if the time taken to render zero alpha objects exists because of the events they can still receive? I.e. would 1000 mouse disabled objects still consume as much render time?

wic said:

Another approach, and a favourite of mine, is the following:

<mx:Panel>
<mx:Button label="b1"
id="b"
visible="{someCondition}"
includeInLayout="{b1.visible}"/>

<mx:Button label="b2"/>

</mx:Panel>
...

This makes the panel resize itself and move b2 according with no need to manually calculate anything (as you do in factor 4).

Rick Winscot said:

Did you experiment with includeInLayout at all? It would be interesting to see how this property plays in your stats.

colin said:

hi rick,
no, in an effort to keep the topic focused, i didn't do any flex framework-specific testing. feel free to post results if you do any.

colin

Fardeen said:

Very interesting ! Thk you for sharing those great infos.

Jason Langdon said:

I knew there was a reason why I used to insert a blank keyframe with a stop() action at the end of my tweens when they alpha'd to 0 ;-)

Tink said:

'Flash Player's display API offers three different tools for hiding display objects'

Actually 4. I use BlendMode.ERASE in Efflex.

david doull said:

another option - set the sprites x value to a value that is off the stage x=-100
Not sure about flash 10 but in earlier versions this gave faster rendering than visible =false;

Chris McAndrew said:

Thanks Colin,
This is one of those topics most people think is a no-brainer, when in reality, it can and does affect performance and depth depending on how it's used. As always, thanks for your research.

Philip Bulley said:

Further to Tink's comment, I always use BlendMode.ERASE in place of alpha=0, as any MouseEvent can still be captured. Haven't benchmarked it myself, but have been assured it is faster than alpha. Would be interesting to see this method added to your tests.

Philip said:

Further to Tink's comment, I always use BlendMode.ERASE in place of alpha=0, as any MouseEvent can still be captured. Haven't benchmarked it myself, but have been assured it is faster than alpha. Would be interesting to see this method added to your tests.

facildelembrar said:

Non-visible objects affect parent dimensions, so they also affect the hitTestPoint function and possibly the hitTestObject too. This is probably the same for masked objects.

I just spend several hours trying to fix a bug related to non-visible objects, when the best solution was to simply remove them...

Colin, thank you so much for this article.
Adobe, please fix this bug 741 or add some visibleWidth/visibleHeight properties for crying out loud.

Guillaume Provost said:

Hey guys,

I had the interesting problem of dealing with (external/legacy) interactive invisible elements that were seriously affecting performance on my codebase with "alpha=0". I've found the following workaround to the problem for Flash 10 apps to keep interactivity working and performance high:

On Event.ENTER_FRAME set visible=true
On Event.EXIT_FRAME call stage.invalidate()
On Event.RENDER call visible=false

This will effectively save the performance cost associated with rendering the primitives, while keeping them active in the interactive event flow.

vito said:

great info, thanks

Simon Hartigan said:

Thank you very much for this! I actually just figured it out by my own (then I searched for an article to explain it) that visible is my preferred choice over removeChild(), however you helped explain it nicely and I appreciate that!

NBCDumb said:

Thx a lot, these informations were exactly what I needed (hesitating between non-visible and removed child to hide a video, so I was worried about do not render the videoDisplay while hidden).
You saved me a lot of time !

(btw: sorry about my language, I'm french... ;-))

Jeffrey said:

Very informative and well written posting. As a Flash noob it was EXACTLY what I was looking for. Thank you sir and my best to you and yours!

jp said:

I wonder how this affect visible children. I experimented with visible = false versus removeChild and I saw a drastic difference in redraw...which is not what I would expect.If a parent's visibility = false, shouldn't the children also be false and not considered for rendering? This is not what I saw. I was already setting visibility to false and had some performance problems. Turned show redraw regions on and saw a lot of redrawing. I then removed that parent (in addition to setting its visibility to false) and saw HUGE performance differences. My guess is that it has to do with the children.

I did not run isolated tests to narrow it down because removeChild() worked fine. But, I will say in my situation I have several MovieClips loaded and attached to a canvas (through their SWFLoader). I was toggling the visiblity of the Canvas where now I also add or remove the Canvas from teh display list altogether. So, I believe there is more to the story.

Leave a comment


Type the characters you see in the picture above.


Tag Cloud

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.