— tomauger.com

ActionScript 2.0 – MovieClip “Bring to Front”

To me this seems like a basic problem in many Flash-based interfaces. I think Adobe did too, which is why in AS3 they completely revamped their approach to layering display objects.

This leaves those of us stuck back in AS2.0-land (because some customers insist they want their Flash published as FlashPlayer 8! ) scratching our heads. Kirupa, Senocular and other worthies have come up with an interesting technique which keeps a “top-layer” counter, increments it by 2 every time you want to swap depths. While this works perfectly well, there’s something about the technique that, to me (no offense guys: you’re genii), seems like quick-and-dirty (with the emphasis on dirty) code. At some point, I reason, one just HAS to hit that upper layer limit. I’m sure it’s like layer 65538 or something, but still – since you’re using SwapDepths() anyway, why can’t you just find that currently top MovieClip and swap the one you want with it. That way you’re never increasing the top layer count and away you go.

So here’s my take on a bringToFront function. (Note: this is an update to the previous version with some substantial improvements).

Here I’ve added the bringToFront function to the MovieClip prototype, so that you can use it with any movieclip as if it were a native method, like this: myMC.bringToFront();

/*
USAGE:
    myMC.bringToFront();
    myMC.bringToFront({protect:[myMC2, myMC3]});

    The second form allows you to protect certain movieclips from having
    their stacking order affected. This is useful if you have some movieclips
    you want to stay in front of everything else. You pass the actual
    movieclip objects, not a string of the movieclip name.
*/

MovieClip.prototype.bringToFront = function(optionsObj:Object):Void{
    // define an array to hold a snapshot of the display list
    var displayList:Array = new Array();

    // traverse the current parent object's children
    for (var i:String in this._parent){
        // and only capture movieclips (not other properties) whose depth is greater than
        // the movieclip we're bringing to the front.
        // the _parent == this._parent is there to get around a strange bug I encountered
        // where we were digging into children's descendants. don't know why.
        if (typeof (this._parent[i]) == "movieclip" 
                                    && this._parent[i]._parent == this._parent 
                                    && this._parent[i].getDepth() > this.getDepth()){

            var includeItem:Boolean = true;
            // check to make sure this item is not one of the "protected" items
            for (var j:Number = 0; j < optionsObj.protect.length; j++){
                if (this._parent[i] == optionsObj.protect[j]){
                    includeItem = false;
                    break;
                }
            }

            // store it in the array.
            if (includeItem) displayList.push(this._parent[i]);
        }
    }

    // for (x in y) usually traverses the object backwards, so here we're
    // iterating over displayList in reverse, and just swapping the
    // original object's depth all the way up the list. Can't really think
    // of a more efficient way of doing this.
    for (var i:Number = displayList.length-1; i>=0; i--){
        this.swapDepths(displayList[i]);
    }
}
  • Patareco

    I read this! You actually helped me with your shared object post! Keep posting your ingenious solutions!
    On Post: I don’t know if i can help you cause I’m only middle experienced in AS2!

    PS- registering for comments sucks a bit!

  • http://www.tomauger.com admin

    Hey man, glad I was able to help! I know registering for comments sucks, but you wouldn’t believe the amount of spam we get from garbage websites if they don’t have to register.

    I really appreciate you taking the time to register and say thank you! I’m glad somebody is out there!

  • http://www.tomauger.com admin

    I have updated the code section to a much improved version. Not only does this version now extend the MovieClip prototype, but it actually works exactly as it should, rather than just swapping the current and the topmost movie clips – fancy that.

  • eighty4proof

    Who knows how much time you just saved me. I can’t thank you enough!

  • http://www.tomauger.com admin

    Hey man, my pleasure. Thanks for taking the time to leave a comment!

  • dlbrown06

    Wow man, thank you so much. I registered for this site, just so I could leave a comment and say thank you. I’m new with actionscript and this saved me an immense amount of time.

  • http://www.tomauger.com admin

    Thanks so much for taking the time to leave a note. It’s comments like this that encourage me to keep on posting new tips!

  • ady624

    There’s a problem with your logic in this script. It is assuming that the list of objects that you get from _parent is sorted by depth, which is not the case, firstly because Flash does not guarantee it, secondly because after a few swaps using this function, they will no longer be sorted. With a high count of children, the function works randomly. What I have done to improve it is to add a method for swapping depths in the depth order. Since sort() is out of the question, I have rewritten the end of the function as follows:

    MovieClip.prototype.bringToFront = function(optionsObj:Object):Void{
      // define an array to hold a snapshot of the display list
      var displayList:Array = new Array();
    
      // traverse the current parent object’s children
      for (var i:String in this._parent){
        // and only capture movieclips (not other properties) whose depth is greater than
        // the movieclip we’re bringing to the front.
        // the _parent == this._parent is there to get around a strange bug I encountered
        // where we were digging into children’s descendants. don’t know why.
        if (typeof (this._parent[i]) == “movieclip”
          && this._parent[i]._parent == this._parent
          && this._parent[i].getDepth() > this.getDepth()){
    
          var includeItem:Boolean = true;
          // check to make sure this item is not one of the “protected” items
          for (var j:Number = 0; j < optionsObj.protect.length; j++){
            if (this._parent[i] == optionsObj.protect[j]){
              includeItem = false;
              break;
            }
          }
    
          // store it in the array.
          if (includeItem) displayList.push(this._parent[i]);
        }
      }
    
      // for (x in y) usually traverses the object backwards, so here we’re
      // iterating over displayList in reverse, and just swapping the
      // original object’s depth all the way up the list. Can’t really think
      // of a more efficient way of doing this.
      //
      // modified by Adrian Caramaliu, Feb 24th, 2009. Swapping the items in their depth order
      while (true) {
        var idx:Number = -1;
        var min:Number = 0;
        var obj:Object = undefined;
        for (var ii:Number = 0; ii< min))) {
            idx = ii;
            min = displayList[ii].getDepth();
            obj = displayList[ii];
          }
        }
        if (idx < 0)
        return;
        this.swapDepths(displayList[idx]);
        displayList[idx] = undefined;
      }
    }
    
  • http://www.tomauger.com admin

    Thanks, Adrian for this follow-up. I haven’t had a lot of time to research your idea, but it seems to make sense what you’re saying. I wonder if there’s not an even easier way to go about doing this…

    Instead of building up the “displayList” and THEN doing this lengthy second pass-through to make sure we get the actual layer order right, is there some way to store the layer order while we’re building the displayList array up in the first section of the script?

    Something like:
    if (includeItem) displayList[this._parent[i].getDepth()] = this._parent[i];

    I am not 100% sure this wouldn’t give us duplicate items. I have to research it further to see whether the depth number resets at 1 within each nested timeline (which would suck) or continues in some kind of incremental order (which would rock).

    Thanks again Adrian for your great fix, and sorry for all the trouble editing your comments. I’ve complied everything into 1 comment to keep things tidy-ish.