R – Actionscript strange coords after removeChild is called


Can anyone help me make sense of the following?

I create a few squared sprites and then remove the first one
and display the coords.
The result is:

  (x=0, y=0, w=208, h=40) 0 208
  (x=42, y=0, w=166, h=40) 0 166

The x coordinate is still 0, though width has changed,
getBounds shows correct values.
I would expect x to change as well.
Because of the wrong value of x, globalToLocal and localToGlobal work incorrectly.

If you click somewhere on the left side of the second (still visible) rectangle
you get:

2 28 (x=2, y=28)

which is good for nothing. The values in brackets should be in stage coords and they are not.

The code:

    public function test():void {
        var s:Sprite;
        var i:int;

        var arr:Array = new Array();
        for (i = 0; i < 5; ++i)
            s = new Sprite();
            s.graphics.drawRect(0, 0, 40, 40);
            s.x = i * 42;
        trace(this.getBounds(stage), x, width);
        removeChild(arr[0]); arr[0] = null;
        trace(this.getBounds(stage), x, width);
        addEventListener(MouseEvent.CLICK, click);

    private function click(e:MouseEvent):void {
        trace(e.localX, e.localY, localToGlobal(new Point(e.localX, e.localY)));

Best Solution

You're confused on two separate points, but I think they each stem from an incomplete view of how Flash deals with coordinate systems. Recall that every display object carries around its own coord system. In your first question, an object's "x" and "y" properties aren't calculated dynamically to reflect the object's upper-left corner, they simply denote the location of that object's origin, with respect to its parent's coordinate system. So an object's "x" value won't change just because the object's contents changed - it changes when you move the object itself.

The problem with your stage coordinates is that the localToGlobal method converts coordinates from the scope in which it's called - and you're calling it from the parent of the rectangles, but you're passing in coordinates from the local system inside the rectangle. Stare that following for a bit and it should make sense:

function click(e:MouseEvent):void {
    // bad - uses rectangle coords in scope of "this"
    //trace(e.localX, e.localY, localToGlobal(new Point(e.localX, e.localY)));
    // works - call localToGlobal from the scope of the rectangle
    trace( e.target.localToGlobal(new Point(e.localX, e.localY)));
    // alternately, call it from any scope with coordinates taken from that scope:
    trace( localToGlobal( new Point( mouseX, mouseY )));
    trace( e.target.localToGlobal( new Point( e.target.mouseX, e.target.mouseY )));

Update: Part of what makes this confusing is how event bubbling works in AS3. Read the first few paragraphs of this excellent article to get the general idea, and then understand that in this example the events are initially issued from a rectangle (because that's where the graphic that got clicked is), but you only catch them as they bubble through the scope where you set your listener. So e.target is the rectangle, and e.currentTarget is the scope of your listener. Then the final piece of the puzzle is to understand that e.localX and e.localY are always in the coordinate system of the event's original target. (This is because they are properties of the event, and it wouldn't make sense for them to keep changing as the event bubbled up the display list.)

As a final note, if you're wondering how beginners usually cope with this stuff, the answer is that people usually attach listeners directly to the display object that has the graphical contents that will be clicked. In that case, e.target and e.currentTarget will always be the same, and most beginners won't need to understand any of this.

Related Question