When you layer Bitmap images on top of each other in Flash/Flex/AIR, an interesting thing happens. Mouse events are captured by the top-most image and aren’t passed through to anything underneath. Even when hovering over, or clicking on transparent areas, the events just don’t get through. Moses over at MosesSupposes created an InteractivePNG solution that does some interesting mouse handling and hit detection.

In the above example (click the image, right-click to view src), I have two transparent png bitmaps layered on top of each other. On the left side, I’m using the standard BitmapImage tag. When you hover over any part of the images (even transparent areas), you’ll notice that you get the SNOWFLAKE! tooltip. Since the snowflake is on top, it is capturing all mouse activity so nothing gets through to Santa underneath.

On the right, I’m using BitmapSprite. When you have a Sprite that is rendered using graphics drawing, mouse events are only captured in areas where the graphics object has physically been drawn on. BitmapSprite is designed to take an input Bitmap, convert it into 1-pixel tall rectangle bitmap fills that are drawn to the sprite’s graphics object. In this manner, any transparent pixel doesn’t capture mouse events and everything is passed down to objects beneath. When you hover over a snowflake pixel, you get the tooltip SNOWFLAKE!, and when you hover over santa, you get a SANTA! tooltip. With this solution, we’re also caching the result as a bitmap so we only pay the rendering penalty once. No mouse-handling or hit detection kung-fu necessary. Nifty!

Post to Twitter

Posted by Andrew, filed under as3, Flex. Date: December 1, 2010, 12:07 pm | 10 Comments »

10 Responses

  1. Billy Says:

    Nice work Andrew!

    this works well with mouse interactions.
    I am trying to manage hitTest between multiple transparent PNGs. I was wondering if your class could be useful for hitTest detection as much as it is for mouse Interaction

  2. Andrew Says:

    @Billy, Not sure about hit detection. If you get it working, post the code somewhere and drop a link to it in the comments.

  3. Chris Says:

    Hi. I ran into the very same problem a couple of days ago. Then I found out that flex lets you define a sprite as a hitarea for all components that extend UI component. I wrote a function to convert a png image (with transparent areas) to a sprite-hitarea: http://www.webverwirklichung.com/en/blog/programming/flex/creating-hitarea-png-image-transparent-alpha-regions-flex

    In your case I would just put both images inside a wrapper (f.ex. a canvas) and then assign a hitarea to that wrapper.

  4. Al Koty Says:

    Is there anyway to do this in Flash CS5?

    I have the same problem, but I can’t seem to get the interactivePNG class to work. Even his demo crashes on me.

    I have multiple overlapping ‘draggable’ movie clips and I need to be able to interact with a button that is partially blocked by the bounding boxes of the PNG files.

    Can you throw me some pointers, or recommend a simple way of even using that interactivePNG class with just a couple of overlapping images?

  5. Al Koty Says:

    var hitTestPoint1:Boolean = false;
    var myHitTest1:Boolean = false;
    var objects:Array;

    clip.addEventListener(MouseEvent.MOUSE_DOWN, doHitTest);
    clip.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
    clip.buttonMode = true;
    clip.mouseEnabled = true;
    clip.mouseChildren = true;

    clip2.addEventListener(MouseEvent.MOUSE_DOWN, doHitTest);
    clip2.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
    clip2.buttonMode = true;
    clip2.mouseEnabled = true;
    clip2.mouseChildren = true;

    clip.rotation = 60;

    function doHitTest(event:MouseEvent):void
    {
    objects = stage.getObjectsUnderPoint(new Point(event.stageX, event.stageY));
    trace(“Which one: ” + event.target.name);
    trace(“What’s under point: ” + objects);
    for(var i:int=0; i<objects.length; i++)
    {
    if(realHitTest(DisplayObject(objects[i].parent), new Point(event.stageX, event.stageY)))
    {
    trace("Image hit!" + event.target.name);
    objects[i].parent.startDrag();
    }
    }
    }

    function stopDragging(event:MouseEvent):void
    {
    event.target.stopDrag();
    }

    function realHitTest(object:DisplayObject, point:Point):Boolean
    {
    /* If we're already dealing with a BitmapData object then we just use the hitTest
    * method of that BitmapData.
    */
    if(object is BitmapData)
    {
    return (object as BitmapData).hitTest(new Point(0,0), 0, object.globalToLocal(point));
    }
    else {

    /* First we check if the hitTestPoint method returns false. If it does, that
    * means that we definitely do not have a hit, so we return false. But if this
    * returns true, we still don't know 100% that we have a hit because it might
    * be a transparent part of the image.
    */
    if(!object.hitTestPoint(point.x, point.y, true))
    {
    return false;
    }
    else {
    /* So now we make a new BitmapData object and draw the pixels of our object
    * in there. Then we use the hitTest method of that BitmapData object to
    * really find out of we have a hit or not.
    */
    var bmapData:BitmapData = new BitmapData(object.width, object.height, true, 0×00000000);
    bmapData.draw(object, new Matrix());

    var returnVal:Boolean = bmapData.hitTest(new Point(0,0), 0, object.globalToLocal(point));

    bmapData.dispose();

    return returnVal;
    }
    }
    }

  6. Hugh Campbell Says:

    Hi,
    far and away the most elegant solution I’ve found, for what is a vexing issue! I can’t get over how fast it runs either, considering it analyses every pixel in the image… I guess that’s the Vector thing?

    One thing to flag in the source code is there are includes for some Flex classes that are not required for the code to run. Removing the reference to the ‘BItmapAsset’ class also allows the class to be used in pure AS3 (non-Flex) projects.
    cheers,
    Hugh

  7. Richard Huntley Says:

    Thanks for sharing a get solution.

    When I used in Flex 4.6 I was getting a null pointer exception in the ‘creationCompleteHandler’ method.

    To fix this I needed to change the triggering event from ‘creationComplete’ to ‘contentCreationComplete’

  8. liuyf Says:

    I encountered a weird bug: i use this BitmapSpirte as a dragable element,and with width and height contricted,but the opaque image is distorted.
    to solve this bug,just write:
    graphics.drawRect(0, 0, 0, 0);
    follow:
    graphics.beginBitmapFill(data, null, false, false);

    may helpfull, good luck~~

  9. Dennis Day Says:

    I am building a pure AS3 app with FlashDevelop. Are there any examples that show how to tell a Sprite (a PNG) to utilize this class? I’m banging my head here.

  10. Dennis Day Says:

    genericSprite = new Sprite(); // NORMAL SPRITE
    genericSprite = new InteractivePNG(); // BETTER

    Why couldn’t someone post some code somewhere showing this?

Leave a Comment

 
Your comment

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.