Actionscript 3 and dynamic masks

10,910

Solution 1

this.masker.mouseEnabled = false; //set on this.masker

That should work. Right now it is intercepting your events before they get to anything else in the container. Not 100% sure, but I believe a mask will sit on top of the container. Another option would be to add another masking child to MyContainer at the bottom of the displayList and add panel as its sibling. You will want to set mouseEnabled and mouseChildren to false on the masked sprite/mc still though.

package
{
    import flash.display.Shape;
    import flash.display.Sprite;

    public class MyContainer extends Sprite
    {
       protected var masker : Shape = null;
       protected var panel:MyPanel;

       public function MyContainer()
       {

          this.masker = new Shape();
          this.masker.graphics.clear();
          this.masker.graphics.beginFill( 0x00ff00 );
          this.masker.graphics.drawRect(0, 0, 1, 1);  // 1x1 pixel.
          this.masker.graphics.endFill();
          addChild( this.masker );

          this.panel = new MyPanel();
          this.addChild(panel);
          this.mask = this.masker;
       }

       // called by it's parent when the stage is resized.
       public function resize( width : Number, height : Number ) : void
       {
          // set the mask to half the size of the stage.
          this.masker.width  = width  / 2;
          this.masker.height = height / 2;

          // set the panel to half the size of the stage.
       }
    }
}

-

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;

    public class MyPanel extends Sprite
    {
        public function MyPanel()
        {
            this.graphics.beginFill(0xFF0000)
            this.graphics.drawRect(0,0,10,10);
            this.addEventListener(MouseEvent.MOUSE_OVER, this.handleMouseOver)
            this.addEventListener(MouseEvent.MOUSE_OUT, this.handleMouseOut)
        }

        public function handleMouseOver(event:Event):void
        {
            this.graphics.clear();
            this.graphics.beginFill(0x00FF00)
            this.graphics.drawRect(0,0,10,10);
        }

        public function handleMouseOut(event:Event):void
        {
            this.graphics.clear();
            this.graphics.beginFill(0xFF0000)
            this.graphics.drawRect(0,0,10,10);
        }
    }
}

Solution 2

One thing i noticed is if the mask in a child of the masked object, the position of that mask for all mouse related event or interactivity will be off, while visually everything seems fine.

For example i had a clip with a button inside end it was masked by one of it's child. What happened is only half the button could be clicked and if I moved the clip a bit to the left, only a quarter of the button remained click-able.

Basically the mouse event masking seems to position itself relative to the parent of the masked object while the visual mask does not.

You need to move the mask to the parent clip. Here how i did it :

class MaskedObject extends MovieClip{
  public var maskClip:MovieClip;
  public var maskOrigin:Point

  public function MaskedObject (){
     maskOrigin = new Point(maskClip.x,maskClip.y);
     parent.addChild(maskClip);
     maskClip.x = maskOrigin.x + this.x;
     maskClip.y = maskOrigin.y + this.y;
     mask = maskClip;
  }

  override function set x(val:Number):void{
    super.x = val;
    maskClip.x = maskOrigin.x + this.x;
  }


  override function set y(val:Number):void{
    super.y = val;
    maskClip.y = maskOrigin.y + this.y;
  }
}
Share:
10,910
Luke
Author by

Luke

Updated on June 04, 2022

Comments

  • Luke
    Luke about 2 years

    I have a container MovieClip that serves as a content area that I need to mask off. When I create a mask inside this container using a Shape I can't seem to interact with the contents of other containers I create here, like buttons etc.

    This is what I'm doing in code (I've left out all the import's etc.):

    class MyContainer extends MovieClip
    {
       protected var masker : Shape = null;
       protected var panel : MyPanel = null;
    
       public function MyContainer()
       {
          this.masker = new Shape();
          this.masker.graphics.clear();
          this.masker.graphics.beginFill( 0x00ff00 );
          this.masker.graphics.drawRect(0, 0, 1, 1);  // 1x1 pixel.
          this.masker.graphics.endFill();
          addChild( this.masker );
    
          this.panel = new MyPanel();  // has buttons and stuff.
          addChild( this.panel );
    
          this.mask = this.masker;
       }
    
       // called by it's parent when the stage is resized.
       public function resize( width : Number, height : Number ) : void
       {
          // set the mask to half the size of the stage.
          this.masker.width  = width  / 2;
          this.masker.height = height / 2;
    
          // set the panel to half the size of the stage.
          this.panel.resize( width / 2, height / 2);
       }
    }
    

    When I add the mask (Shape) to the display hierarchy I can no longer interact with whatever buttons are defined in MyPanel. However, not adding the mask to the display hierarchy does let me interact with the contents on MyPanel but the mask is not sized/positioned correctly. I guess that when the mask is not added to the display hierarchy it sits in the top-left corner of the movie (I cannot prove this though).

    How do I go about making my mask size/position correctly and let the user interact with the buttons in MyPanel?

  • Luke
    Luke almost 15 years
    But masker is a Shape and therefore doesn't have a mouseEnabled property. It's a reason I used a Shape instead of a Sprite.
  • Luke
    Luke almost 15 years
    Just tested it with a Sprite instead of a Shape (with the mouseEnabled property set to false). Doesn't work either. :(
  • Joel Hooks
    Joel Hooks almost 15 years
    Perhaps the problem is in your MyPanel class? I tested it with my added code above and it works as expected.
  • Luke
    Luke almost 15 years
    The MyPanel class is just content of the container, it could have been anything. The point I'm tying to make is that by adding the mask to the display hierarchy you can't interact with any objects in the scope of that masked display object.
  • Luke
    Luke almost 15 years
    Yes. The example above works indeed. However, my application doesn't. There must be something else at work here since my actual application is a lot more complex of course. In the meantime I've solved my problem by not adding the mask to the display hierarchy and use a localToGlobal to position the mask correctly. Thanks for your help so far!
  • Luke
    Luke almost 15 years
    OK. I'm still not there but I found out that the Glow filter applied to the container was impacting the mask somehow. Removing the Glow filter from the container made it work as described above. However, I was not able to reproduce the problem with the above example. If I find out more I will comment here.
  • Triynko
    Triynko over 12 years
    From the documentation: "When display objects are cached by setting the cacheAsBitmap property to true an the cacheAsBitmapMatrix property to a Matrix object, both the mask and the display object being masked must be part of the same cached bitmap." ------------------------------------------------------------‌​--------------------‌​--------------------‌​Note that cacheAsBitmap is automatically true when any filters are applied, so that's why this applies in that circumstance.
  • Triynko
    Triynko over 12 years
    continued... "Thus, if the display object is cached, then the mask must be a child of the display object. If an ancestor of the display object on the display list is cached, then the mask must be a child of that ancestor or one of its descendents. If more than one ancestor of the masked object is cached, then the mask must be a descendent of the cached container closest to the masked object in the display list."