This project demonstrates a draggable see-through mask--a useful device which is much harder to implement than you might think. Click on the thumbnail above to view the project. When you move your cursor into the Flash window, the cutaway or X-ray mask will follow the cursor everywhere. (Close the browser window when you've seen enough; there's no other exit.)
If you want to use this project as a basis for your work in Assignments 4 or 5, please read this document completely. Then see the separate page with step-by-step instructions for modifying dynoMask.
Concept
If moving masks are interesting, draggable masks are at least twice the fun: the user gets to dig through the main image to find buried content. Now, animated masks are simple enough: you just set up keyframe states, motion-tween the intervening frames, and you have a moving mask. Likewise, the ActionScript that makes buttons and movie clips follow the cursor requires only a single statement. So what's the big deal? Masks can move in keyframe animation--why can't they be used in scripted animation as well?
If you ever try scripting movement on a masking object, you'll hit a very nasty wall. By itself, any layer set to Masking is closed to ActionScript. None of its properties can be changed either by a script on the masking object itself or by other scripts in the timeline. So since scripting control is required to make an object draggable, it would seem impossible to create a draggable see-through window. And yet, as the example above shows, it's very possible indeed. All the trick requires is a few layers within layers.
Layer architecture
The schematic above shows the main movie timeline (the three layers, above) and the timeline for the movie clip called masker (two layers, below). The dotted line indicates that the two-layer movie clip masker is installed as an instance in the second layer of the main movie.
This curious arrangement allows us to sneak past the limitation on script control of masking layers. While we can't link the behavior of a masking layer directly to the cursor, we can script the movie clip that contains it. So we simply make the entire movie clip that appears in the second layer follow the cursor. We do this by placing the following script on the instance of the masking circle:
onClipEvent(enterFrame){
startDrag(_root.masker, true);
}
This command links the X and Y coordinates of the masker movie clip to the current X and Y of the cursor.
But now we encounter a problem. Masker consists of two layers. Its first, masking layer contains an opaque circle that supplies the see-through window. Its second, masked layer contains the "night" image that is meant to be glimpsed mysteriously through the floating aperture. Presently, the "night" layer moves just as the masking layer above it does--which means that the same bit of the night image always shows through the window. This is not what we want.
Ideally, we would like to paste the "night" image into the same fixed X-Y coordinates as the "day" image in the main timeline. But we can't do that, because the "night" image must be attached to its masking layer in order for masking to work.
The solution is fairly elegant: though the layer containing the "night" image must remain fixed, we can change the position of the image within that layer. But how exactly is this done?
The system variables _xmouse and _ymouse record how far the cursor lies from the origin point (0,0) of the main movie. Thus we can know where the cursor is at any given moment. The masking circle follows the cursor and the masked "night" image follows the masking circle: so whenever the mouse moves, we know where all the players stand. Ultimately we don't want them all to stand together, so we need to introduce a little choreography.
With a little figuring, we can determine where we want the "night" image to sit in relationship to the cursor. We want the cursor to move over this masked image in the same way it moves over the "day" image in the main timeline. When the cursor (and the mask) moves to the left, the "day" image stays put, so the mask travels over different bits of that image as it goes. Now, the "night" image is currently glued to the masking circle, so that the same bit always shows even when we move the cursor.
The solution to this problem requires a certain conceptual dodge. Though we can't separate the two layers of the masker movie clip, we can change the position of content in one of its layers.
We can programmatically shift the X and Y placement of the "night" image in the lower, masked layer. Imagine a stack of trays in which the topmost is mounted on little bearings so that it can slide back and forth. Into the upper sliding tray we put another set of sliding trays with the same arrangement, so that the very top tray can slide relative to its downstairs neighbor even as both it and the neighbor are moving relative to the tray on the bottom.
If you're not a good visualizer, consider the code, which is refreshingly terse. We turn the "night" image into a movie clip, so that it can respond to a a ClipEvent, and give it the following script:
onClipEvent(enterFrame){
this._x = -1*_root._xmouse;
this._y = -1*_root._ymouse;
}
"This" in the script above indicates the object that contains the script, which is to say, the night clip itself. The script tells night to move to a position whose coordinates are the inverse of those of the mouse. When the mouse is at 0,0 (the upper lefthand corner of the 600x200 movie rectangle), night is also at 0,0, since the inverse of 0 is still 0. But when the cursor moves 300 pixels to the right (300,0), the night clip shifts to minus-300,0.
That point lies 300 pixels to the left of the original position. It's actually outside the visible movie window, whose left margin is at X=0; but what we're placing at that point is the left edge of the "night" image. The image is 600 pixels wide, so half its contents remain onstage. The see-through window sits over part of this visible bit, which is, notably, a different bit than it originally revealed.
Of course, what you get, in the scripting terms just described, differs quite a bit from what you see as you move the cursor over the demo. Your innocent eye sees the magical window moving to the right, apparently revealing a hidden state of the "day" image on a layer beneath it. Unless you're hip to the trickery, you probably don't realize that the revealed content is above, not below the "day" image, or that the movie clip that contains it is moving in the opposite direction from the window. Since the movements of masking and masked layers coincide pixel-for-pixel, but in opposite directions, the aperture seems to be traveling over a stationary image.
If you think there is an easier way to do all this, you could very well be right. Do let us know if you find one.
If all you really care about is the cool visual effect of a draggable x-ray window, you can download a simplified source file, dynoMaskTemplate.fla from MMShare/dynoMask on Crow. The page on modifying dynoMask gives step-by-step instructions for replacing the visual content in the template.
Note: the radiating-circle effect seen in the demo has nothing to do with the draggable mask and was added strictly for show. It's just a cycling movie clip sitting on a normal layer in the main timeline. Its only clever feature is a script very much like the one attached to masker that makes it follow the cursor. The radiating graphic is not included in dynoMaskTemplate.fla.
Updates and variations
The project shown at left (demonstrated in class March 20) includes some simple variations on the original dynoMask. The masked layer has a 20% alpha value until the viewer holds the mouse button down, at which point alpha progressively increases to 100%. The pulsing circle motif only appears when the mouse button is down, and its alpha changes along with that of the masked layer.
This variation project suggests some of the complications and improvements you might make to the original concept. The in-class demonstration shows how to introduce new visual content to the existing frame and code architecture.
Source file for the variation (dynoMask02b.fla) is available in MMShare/dynoMask on Crow.
