Draggable Mask, or the X-Ray Trick

Click within the rotating circle and drag to re-position the cutaway view.

This demonstration shows a useful visual effect: a moveable cutaway view that appears to dissolve an outer layer, as if we had the fabled X-ray vision of Superman. There are many, many uses for this trick, from product illustrations to traveling spot effects for text highlighting. (For one admittedly wacky application, see my art project, Marginal Effects.)

This posting will detail a slightly simplified version of the movie shown at right, leaving out the rotating red ring, which has nothing to do with the cutaway effect, and is added just to make the X-ray window easier to track.

I. How the trick works

As you might guess, there are two JPEG images in play here. Both show a figure of exactly the same propotions standing in identical positions. In one image (skinOn.jpg), the figure appears normally, with his skin and clothes intact. In the second image (skinOff.jpg), he's reduced to a skeleton. The two images were rendered with Poser from SmithMicro, a 3-D modeling program, using two texture sets for Michael 3, a human figure model from Digital Art Zone.

We can only see a small piece of the skeletal image, defined by a circular mask, which consists of a MovieClip containing a filled circle graphic. By using certain properties of the MovieClips that contain these images, we can allow that circle to mask in just the pixels that lie under it. We can also attach our now-familiar code for setting up draggable objects to make this masking circle go where the user takes it.

II. Architecture

The main movie file is very simple. It contains one MovieClip, masker_mc, which in turn contains a solid, black disk. This MovieClip is linked to a class defined in maskerClass.as.

There is also a document class, defined in draggableMaskDocClass.as. Let's start with that code.

III. Document class

Here's the entire document class, leaving out its package{} container:

  import flash.display.MovieClip;
	
  public class draggableMaskDocClass extends MovieClip
  {
    public var unmaskedImg:MovieClip = new underClass();
    public var maskedImg:MovieClip = new overClass();

    public function draggableMaskDocClass():void
    {
      addChild(unmaskedImg);
      addChild(maskedImg);
    }
  }

Not much happens here: we create variables for two new MovieClips, unmaskedImg and maskedImg. Each accepts an instance of a class to which we are coming, underClass and overClass, respectively. Once we've declared and instantiated, we perform addChild() on the main movie (or root) to add each object to the Display List.

The order of these operations matters. Remember, ActionScript works from inside to out, so any object added later, sits above, or nearer the user (virtually speaking). So in our case, maskedImg sits in the upper layer, with unmaskedImg below it. You'll see why this is important in just a bit.

IV. underClass (lower image)

The class definition for underClass is similarly simple, and composed entirely of familiar bits. Again, here's everything inside the package:

  //THIS CLASS LOADS THE SKELETON IMAGE (BOTTOM LAYER)
  import flash.display.MovieClip;
  import flash.display.Loader;
  import flash.net.URLRequest;
	
  public class underClass extends MovieClip
  {
    public var myLoader:Loader = new Loader();
    public var imgRequest:URLRequest;
		
    public function underClass():void
    {
      imgRequest = new URLRequest("skinOff.jpg");
      myLoader.load(imgRequest);
      addChild(myLoader);
    }
  }

This code is highly similar to the imageClass we used in the Image Browser project. It simply establishes a Loader object and does the necessary transactions to bring a JPEG graphic in via that means. The graphic we load, as the opening note indicates, is the skeletal image, showing the figure without his skin and clothes. This is the unmasked or background image in our setup.

We need to add a note that will by now seem annoyingly familiar: our actual code uses a fully-qualified URL for the URLRequest object, owing to our use of Server-Side Includes. You can probably get away with a relative URL, as shown here.

V. overClass (upper image, and traveling mask)

Now for the slightly more complex overClass, which handles the trickier bit of this system. Here's everything inside the package for this class:

  //THIS CLASS LOADS THE NORMAL IMAGE (UPPER LAYER)
  //ALSO, THE TRAVELING X-RAY DISK
	
  import flash.display.MovieClip;
  import flash.display.BlendMode;
  import flash.display.Loader;
  import flash.net.URLRequest;
  import flash.events.Event;
	
  public class overClass extends MovieClip
  {
		
    public var myLoader:Loader = new Loader();
    public var imgRequest:URLRequest;
	
    public var masker:MovieClip = new maskerClass();
		
    public function overClass():void
    {
      imgRequest = new URLRequest("skinOn.jpg");
      myLoader.load(imgRequest);
      this.blendMode = BlendMode.LAYER;
      addChild(myLoader);

      masker.blendMode = BlendMode.ERASE;
      masker.x = 170;
      masker.y = 70;
      addChild(masker);
    }	
  }

In the importations, you'll notice an unfamiliar object, BlendMode. This feature is the key to our X-ray effect. It's an auxiliary object for DisplayObject, the superclass that contains MovieClip. We can use it to change the way the pixels that make up a visible item on screen are interpreted by the Flash Player.

In the image declarations, we do the usual things to load a JPEG, as we did in underClass. Then we intance a custom MovieClip called maskerClass, whose class definition we'll discuss in just a bit. This is the MovieClip that contains our X-ray disk.

In the Constructor, we finish loading our JPEG, then set the blendMode property of the current MovieClip (this) to BlendMode.LAYER. Note that annoyingly subtle difference in syntax: this.blendMode gets a small b, while BlendMode.LAYER gets a capital B. There's actually a consistent reason for this difference: in the first case, we refer to a property of a MovieClip object, and property names always start with lower-case letters. In the second part of the statement, we refer to an object (BlendMode). Object names are always capitalized.

So why do we do this blendMode (or BlendMode) business? Well, we're trying to set things up so that our draggable disk (or X-ray window) will shine through one graphic, revealing another graphic below it -- just the way X-rays do in real life. In order to give our draggable disk (masker) this ability, we first have to set the image that will be cut out (the one we've brought in via Loader) to a special blendMode called LAYER. This setting tells the Flash Player that the final color and opacity values of pixels in the image will be dependent on interactions with another object.

And so we come to that other object, masker. Before adding it to the Display List, we move it to a certain position on screen, so that the user sees it initially overlapping the human figure. Since the only difference between our upper and lower images rests with that figure, the user would not otherwise be aware of the X-ray effect.

Crucially, we also set the blendMode property of masker, using BlendMode.ERASE. This mode does what you'd expect, punching out any pixels that lie under the masking object, and substituting corresponding pixels from the background image, which is to say, the lower image deployed in underClass. Voila, X-ray vision.

VI. maskerClass (mask interactivity)

All we need to explain at this point is the mechanism behind masker, defined in maskerClass. This class is simplicity itself:

  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.events.MouseEvent;
	
  public class maskerClass extends MovieClip
  {
		
    function maskerClass():void
    {
      this.addEventListener(MouseEvent.MOUSE_DOWN, dragOn);
      this.addEventListener(MouseEvent.MOUSE_UP, dragOff);
    }
		
    function dragOn(event:MouseEvent):void
    {
      this.startDrag(true);
    }
		
    function dragOff(event:MouseEvent):void
    {
      this.stopDrag();
    }	
  }

You've seen this code letter-for-letter before, in every case where we've made an object draggable. There is nothing in this class having anything to do with the X-ray effect.

Put all these pieces together, and you have a traveling cutaway mask, or X-ray vision. Use your powers for good!

VII. Historical notes

If you're worked with Flash as a visual authoring tool (i.e., with the timeline and minimal scripting), you know that it's always been easy to set up a static masking relationship, where a shape in one layer masks in or out pixels from the layer below it. You may know that it's also been possible to put that relationship into motion; though the structures that enable this effect have gone through some interesting changes.

I built my first X-ray vision project in 1999, using Flash 3 and the original version of ActionScript. ("Marginal Effects" uses a variant of this early code.) Back then, it was quite a cute trick, since Flash 3 would not support layer masking on any object animated through a script. To pull it off, I had to set up layers within MovieClips within layers, moving the masked image in opposite directions from the movement of the masking object. (If you're curious, see my writeup from 2002.)

With ActionScript 2.0, things got much better. We could set up layer masking in the timeline, including scripted MovieClips with drag behaviors. Something like the solution you're learning this year became possible -- though actually, it was a good deal simpler.

Then came ActionScript 3.0, and suddenly it was no longer possible to preserve timeline layer masking on a script-animated object. Back to the future!

As you have seen, we can go around this obstacle by summoning up a few more objects and properties.

The question remains, though (as it always does) -- why is Adobe replacing a scripting tool that was relatively lightweight and easy to learn, with one that is full of quicks, idiosyncrasies, and complex levels of detail? Clearly, Adobe wants scripters to stay away from the visual tools in Flash (such as the timeline), while visual designers are encouraged to shun code. That way, according to Adobe, everyone can concentrate on their own areas of strength.

And get paid less, or be subject to outsourcing, since they can only do half the job -- ? So many questions...

VIII. Source files

In the directory draggableMask, which is within MULTIMEDIA in the shared account on student-iat, you'll find the main movie file (.fla), plus the four ActionScript class files, draggableMaskDocClass.as, overClass.as, underClass.as, and maskerClass.as. You'll also find two JPEG graphics, skinOn.jpg and skinOff.jpg.



University of Baltimore Logo

Last updated: 03/30/08 17:06:55
Copyright © 2008 School of Information Arts and Technologies