Workshop Chapter 5. Mapping and Scripted Masks

CONTENTS

    In this workshop chapter, you'll learn a valuable technique even though the exercise has limited practical value. Although mapping is a good concept to understand, this exercise involves a specific design that may or may not ever arise in the projects you build. The result is pretty cool, but the idea is to learn more than just a special effect.

    Mapping is the technique of translating coordinates from one space (such as a tiny map) to another space (such as the territory represented by the map). Mapping is like those mechanical tracing arms that let you trace and scale at the same time. In this workshop chapter, you'll do it with ActionScript.

    Practical applications for mapping do exist. You might have a giant map of a city as well as a smaller one that the user can click on. By using mapping, you can calculate the location that was clicked and move the giant map proportionally to display the correct area. You can also use this technique with an assortment of sliders so that you can calculate the portion of a tall text field that should appear based on the location of a scroll bar. Finally, a really cool example of mapping is to make a magnifying glass that can be moved around the Stage. In this workshop, we'll do an adaptation of part of a real project I worked on. The "boards" section of the 1999 www.m-three.com web site included a mechanism that "zoomed in" on a snowboard graphic (see Figure W5.1). When users move the crosshairs to the left, they "zoom in" on the left side of the large version of the board; when they move the cursor to the right, the board moves to the left to reveal the right side. The effect was pretty cool, but it didn't take a whole lot of work to program (to design, yes, but to program, no).

    Figure W5.1. In the original M3 web site, users could inspect close-up views of the snowboards in a Flash movie that used mapping. Screen shot courtesy of Paris France Inc., Copyright 1999 by MLY Snowboards.

    graphics/21fig01.gif

    Here are the steps for mapping one image to another:

    1. Either import a photograph or draw a large rectangle that contains a variety of colors and shapes (see Figure W5.2). Convert the rectangle to a Movie Clip. Name the instance now on the Stage big.

      Figure W5.2. A very large graphic or picture is turned into a Movie Clip with an instance name of big.

      graphics/21fig02.gif

    2. Copy the big instance and paste it into a new layer. Resize the copy so that it's much smaller and can fit on the Stage. Name this instance small.

    3. Draw a perfect square and scale it so that its width is a little less than half the height of the large rectangle (big). This square will define the viewable area. Place this square in a layer just above the layer containing big. To control how the layers interact, select the Layer Properties (Modify, Layer ) for each layer as follows. Set the layer containing the square to Mask. Make sure that the layer that contains the big instance is set to Masked and the layer containing small is set to Normal (see Figure W5.3).

      Figure W5.3. The small instance is in a Normal layer, and the square shape is masking the big instance in the lowest layer.

      graphics/21fig03.gif

    4. At this point, successively grab the big clip by each corner and use the Info panel to ascertain the x and y coordinates for all four extremes. (Be sure to use the center point option in the Info panel.) Note the x and y values when big is moved all the way down and to the right (when big's top-left corner snaps to the square's top-left corner, as shown in Figure W5.4). Repeat this for the other three corners: when big's top-right corner snaps to the square's top-right corner, when big's bottom-right corner snaps to the square's bottom-right corner, and when big's bottom-left corner snaps to the square's bottom-left corner.

      Figure W5.4. We need to successively snap each corner of big to the square shape to gather the coordinates of the four extremes.

      graphics/21fig04.gif

    5. Regardless of what you named the eight coordinates you gathered in step 4, there are really only four coordinates: the minimum and maximum x plus the minimum and maximum y.

      //

      The terms "minimum" and "maximum" can easily be confused. Consider Figure W5.5, which shows the four extremes for the mouse (a, b, c, and d in the small rectangle at the top left) and the four extreme locations for the big rectangle (a, b, c, and d in the rectangles at the bottom). When the mouse is in location a on small, the instance big should be in location a (down and to the right). Similarly, when the mouse is in location d on small, big should be moved up and to the left (shown as d below). So when the mouse is at what I'd call a "minimum x" and "minimum y" (location a on small), the big instance should be moved to 584x, 147y (location a) which you might call the "maximum" because those numbers are large. However, we're going to call these values the "minimum" because they correspond to when the mouse is in the "minimum" corner (a). You can call the numbers whatever you want, but just realize that clicking the top-left corner of small should move the big clip down and to the right.

      Figure W5.5. This figure demonstrates how the four corners of the small rectangle are mapped to four extreme positions of the big rectangle.

      graphics/21fig05.gif

    6. Now that we've gathered the four extremes for big, we can store those values in variables. Pick a frame in the main timeline to type the following code (and, for that matter, all the code for this workshop):

      b=new Object();  b.xMin=584;  b.xMax=-102;  b.yMin=147;  b.yMax=-47;  b.width= b.xMax- b.xMin;  b.height= b.yMax- b.yMin;

      Naturally, you should use your own gathered values for the four variables. We certainly could have just had 6 separate variables, but I decided to store the values as 6 properties in a generic object b. That will help with consistency later because we'll have other variables with the same name. It's actually going to be easier to say b.xMin than xMin. Notice that I went ahead and created the properties width and height to save some typing later.

    7. Because we're going to be calculating a proportional location of the cursor (on top of small), we should gather some variables containing the coordinates of the four sides of small. Luckily, we can use the getBounds() method, which was first discussed in Workshop Chapter 2, "Creating Custom Cursors." If we assign a variable (say s) to the getBounds() method of small, the variable s will become an object with four properties: s.xMin, s.xMax, s.yMin, and s.yMax. Conveniently, we used similar names to contain the information about big. We just have to remember that b.xMin is for big and s.xMin is for small. This is no problem provided that we can just keep them straight. Add the following script below the code you have in the first frame:

      s=small.getBounds(_root);  s.width=s.xMax- s.xMin;  s.height=s.yMax - s.yMin;

      Notice that the first line assigns values to the four properties in the variable s. That is, using getBounds(), we get the left, right, top, and bottom location of small (in the _root timeline) but they're stored in 4 properties (xMin, xMax, yMin, and yMax). The last two lines just add two new properties (width and height) to the variable s.

    8. It turns out that writing an expression to place the big clip in the proportional location doesn't take quite as many variables as we gathered. For any given mouse location (on top of small), we want to move big so that the mask reveals the correct (mapped) location. There's two parts: writing the script and putting it in an event so that it gets triggered at the right time. The event is easy: mouseMove (or any time the mouse moves). The expression is easiest to deduce using what-if numbers. What if the mouse is in the middle of small? In that case, the _x location of big should be its minimum plus half its width. More generally, big should always appear at its minimum plus the percentage times its width the percentage being the mouse location relative to small. You can calculate that value by taking the mouse location minus the left side of small divided by small's width. You can use some easy numbers to check that expression. If small is 100 pixels wide and it's left side is 200, a mouse position of 250 is indeed 50 percent (250-200)/100. Anyway, here's the code that goes beneath all the rest of the code you've written:

      1 _root.onMouseMove=function(){ 2   var percentX=(_root._xmouse - s.xMin )/ s.width;  3   var percentY=(_root._ymouse - s.yMin )/ s.height;  4   big._x=b.xMin+ (percentX*b.width);  5   big._y=b.yMin+ (percentY*b.height);  6   updateAfterEvent();  7 }

      The _root.onMouseMove callback gets called any time the mouse moves. You can see in lines 2 and 3 that the percentages are calculated by subtracting the left side (or top, for _y) of small from the mouse location and then dividing by the width (or height for _y). Then that percentage is multiplied by the width (or height) of big and added to the left side (or top) of big. Finally, updateAfterEvent() makes it play smoothly. Select Control, Test Movie, and you should see it work very nicely.

      There are a few other things you might want to add. For example, a few if statements could ensure that big never moves out of view of the mask. Something such as, "If big's position is about to be less than its minimum, then set it to the minimum." We won't do that yet, however. One thing that is worth doing is to make big move only while the user drags (instead of all the time). One way to do that is to set a variable (to true when the user clicks and to false when he releases), and then put all the code in the mouseMove within an if statement so that it executes only while the user is clicking.

    9. Replace the old onMouseMove callback with the following code:

      _root.onMouseDown=function(){clicking=true;}  _root.onMouseUp=function(){clicking=false;}  _root.onMouseMove=function(){   if(clicking){     var percentX=(_root._xmouse - s.xMin )/ s.width;      var percentY=(_root._ymouse - s.yMin )/ s.height;      big._x=b.xMin+ (percentX*b.width);      big._y=b.yMin+ (percentY*b.height);      updateAfterEvent();    }

      At this point, the movie should work fine regardless of whether you use those last few enhancements. However (as always), there's one subtle issue I think we should fix. Note that when you click once on the small instance, nothing happens. Only when you click and drag does the big clip start moving. You could just copy the entire code inside the if statement in the mouseMove event (see Figure W5.6) and paste it into the mouseDown event. Doing that, however, places the same code in two locations yuck! Yes, a function is the solution.

      Figure W5.6. You can move this portion of code to a function.

      graphics/21fig06.gif

    10. Select the code shown in Figure W5.6 in the onMouseMove event and cut it (don't delete). In its place, type moveBig(), which calls a function we're about to write. Inside the onMouseDown callback, type moveBig() below where clicking gets set to true so that the mouseDown event executes the same function. The two callbacks should look like this:

      _root.onMouseDown=function(){   clicking=true;    moveBig();  }  _root.onMouseMove=function(){   if(clicking){     moveBig();      updateAfterEvent()    }  }
    11. Finally, we just have to paste our code inside a function called moveBig() (placed anywhere in that first keyframe):

      function moveBig(){   var percentX=(_root._xmouse - s.xMin )/ s.width;    var percentY=(_root._ymouse - s.yMin )/ s.height;    big._x=b.xMin+ (percentX*b.width);    big._y=b.yMin+ (percentY*b.height);  }

      Notice that we create only the framework for the function (function moveBig(){}) and then paste the code that we cut in step 10 in between the curly brackets.

      Now your movie should be complete!

    Just for fun, I want to show you how easy it is to convert this entire workshop into something different. Flash MX enables you to script the movement of masks, so why not move the mask with the mouse? If you place big on top of small (but keep everything in the same layers) and then move the mask, you'll see a zoomed-in version of small. Follow these steps.

    1. Resize big (if necessary) to make it significantly larger than small. Center big on top of small. Be careful to leave all the layers in the same arrangement as before (see Figure W5.7).

      Figure W5.7. By positioning big on top of small (and moving only the mask), you can create a different effect.

      graphics/21fig07.gif

    2. Now just convert the shape in the mask layer to a Movie Clip and give it an instance name of mag (for "magnifier").

    3. Finally, we're going to write a replacement mouseMove script. Instead of trashing what you have, however, you can just rename it with an "X" at the start: "X_root.onMouseMove ." Here's a new mouseMove script to use:

      _root.onMouseMove=function(){   mag._x=_root._xmouse;    mag._y=_root._ymouse;    updateAfterEvent();  }

      That was almost too easy! It's a neat effect, but there's something wrong. Because big is so much bigger, you have to move way off to either side (way past the edge of small) to see the edge of big. We can use mapping to fix this. The idea is that not only will we move the mask (exactly the way it behaves now), but we'll also move big so that it appears in a logical spot. big should move like it did before, but with different minimums and maximums.

    4. Re-gather big's four coordinates for b.xMin, b.xMax, b.yMin, and b.yMax. This time, however, snap big's top-left corner to small's top-left corner, big's bottom-left corner to small's bottom-left corner, and so on. Then take part of the code from the old onMouseMove script and place it in the new mouseMove script. (See why we didn't trash it?) Here is how it should look:

      _root.onMouseMove=function(){   mag._x=_root._xmouse;    mag._y=_root._ymouse;    var percentX=(_root._xmouse - s.xMin )/ s.width;    var percentY=(_root._ymouse - s.yMin )/ s.height;    big._x=b.xMin+ (percentX*b.width);    big._y=b.yMin+ (percentY*b.height);    updateAfterEvent();  }

      The new stuff it at the top, and the old stuff at the bottom. This will work great if you gathered and set the new values for b's properties (such as b.xMin).

      There's one other tiny thing that makes this look sort of rough. When you go past the edges, the big clip moves so far that you can see through to the small clip underneath (see Figure W5.8). It's easy enough to fix that.

      Figure W5.8. The magnifier effect is lost when the cursor goes past the edges.

      graphics/21fig08.gif

    5. Place the following four if statements right before the updateAfterEvent() line in the current onMouseMove callback:

      if(big._x<b.xMax){   big._x=b.xMax;  }  if(big._x>b.xMin){   big._x=b.xMin;  }  if(big._y<b.yMax){   big._y=b.yMax;  }  if(big._y>b.yMin){   big._y=b.yMin;  }

      It might appear as though I made a typo, but realize that b.xMin is a large number and b.xMax is a small number.

      It turns out that we could have skipped re-gathering the coordinates of big (in step 4). That is, we can write expressions based on the coordinates of small. As long as you place the following code after the s variable is assigned, it will save you a lot of manual work. In addition, you can move small to anywhere on the Stage and everything still works:

      b=new Object();  b.xMin=s.xMin+(big._width/2);  b.xMax=s.xMax-(big._width/2);  b.yMin=s.yMin+(big._height/2);  b.yMax=s.yMax-(big._height/2);  b.width=b.xMax-b.xMin;  b.height=b.yMax-b.yMin;

      You might consider trying to do this workshop again with a photograph. You'll get the best results if big is not scaled. That is, import a large photo and put it in clip with the instance name of big. Then drag another instance of this clip from the Library and scale it to be smaller with the instance name small. This way, the magnified version that is revealed will look nice and sharp (see Figure W5.9). In this figure, I set the brightness lower for the small instance.

      Figure W5.9. By using a photograph (scaled down for the small clip), you can create a really nice-looking magnifier effect.

      graphics/21fig09.gif

    This workshop chapter involved many specific steps. We used some hard-wired values that we gathered as well as some values that we calculated with the getBounds() method. Then we used those values to gather coordinates and determine proportional locations of the mouse compared to the small clip.

    CONTENTS


    ActionScripting in MacromediaR FlashT MX
    ActionScripting in MacromediaR FlashT MX
    ISBN: N/A
    EAN: N/A
    Year: 2001
    Pages: 41

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net