AS Reference  :  Notes Index  :  Resources  :  About/Contact  :  Downloads

Distorting Magnifier

The concept

Now that we've looked at how to make a movieclip draggable, let's look at a specific application of that. The distorting magnifier is a fun toy that's been around since at least the days of Flash 5 and maybe before (who can remember?) In those days, the picture had to be embedded in the fla and duplicateMovieClip was used to produce a series of graduated "lenses" that provided the distortion as their holder was dragged around the stage. The example above uses this method, with code updated for Flash 7 (MX 2004) and 8 -- that is, all in frame 1 of the movie, not in clipEvents placed on the movieclips, as was done in Flash 5. After we look at that method, we'll talk about how to update the sample to make it dynamic.

In the movie above, the background layer contains the photo to be distorted, and the layer above contains a movieclip with instance name magnifier. At the start of the movie, distorter contains another movieclip lens, with a center registration point (important), which contains inside it a mask shape (any shape can be used -- this movie uses an oval) in a mask layer above a layer containing the same photo that has been converted to a movieclip and called image. When the movie is run, the lens movieclip will be duplicated some specified number of times to provide a series of child lenses, each of which independently masks of its own copy of the photo, and each of which will reside inside the draggable magnifier movieclip.

duplicateMovieClip

There are two ways to produce copies of a movieclip while a movie is running: duplicateMovieClip and attachMovie, both of which are methods of the MovieClip class. When you use attachMovie, you attach copies of a movieclip from the library (that has been given an export identifier), to the movieclip calling the method. With this code:

ocean.attachMovie("fish", "fish1", 1);

a movieclip named fish1 (an instance of fish) is attached to movieclip ocean at depth 1. That is, fish1 is a child of ocean, and may be addressed as ocean.fish1.

duplicateMovieClip is used to make and attach copies of a movieclip already on stage in a particular timeline to that timeline. Thus, if there is a movieclip fish inside movieclip ocean, more copies of fish can be made, also inside ocean, with commands like this one, which creates ocean.fish1:

ocean.fish.duplicateMovieClip("fish1", 1)

In the movie above, there is a movieclip lens inside movieclip magnifier, which is used to make n number of copies with this code:

for (i=1; i<=n; i++) {
   magnifier.lens.duplicateMovieClip( "lens"+i,i );
}

In that same loop, a graduated scale is applied to each child lens (lens1, lens2, etc) and also to the image movieclip contained in each child lens clip. All of that code is put inside function setupLenses, which is called at program startup, along with function setupMagnifier, which includes code to assign functions to the onPress, onRelease and onEnterFrame event properties of magnifier. Another function, moveLenses is called on each frame to move the lenses within magnifier to the new position they need to be in based on magnifier's current position. (This could be done in response to onMouseMove instead of onEnterFrame to cause updates only when the mouse is moved). This is all the code included in the fla, which you can download via the link at right:

// more lenses = higher precision, more processing power required
var nlenses:Number = 25;
// distortRatio = difference between middle and outer edges of magnified image
var distortRatio:Number = 5;

// setup: create lenses and set their sizes (and photo sizes)
function setupLenses() {
   // lens is only used to make duplicates (not to display)
   magnifier.lens._visible=false;
	
   // create lenses in graduated sizes (progressively smaller)
   for(i=1; i<=nlenses; i++) {
      var lensSize:Number = 10 + 90*(nlenses+1-i)/nlenses;
      // each lens+i will be created in magnifier (not magnifier.lens)
      magnifier.lens.duplicateMovieClip( "lens"+i,i );
      magnifier["lens"+i]._xscale=lensSize;
      magnifier["lens"+i]._yscale=lensSize;
      distortion = 100+distortRatio*i;
      magnifier["lens"+i].image._yscale=100*distortion/lensSize;
      magnifier["lens"+i].image._xscale=100*distortion/lensSize;
   }
}

// makes movieclip to which this is attached draggable
function makeDraggable() {
   this.startDrag();
}

// makes movieclip to which this is attached undraggable
function stopDraggable() {
   this.stopDrag();
   // uncomment to figure out where to place magnifier initially:
   //trace(this._x+' '+this._y);
}

// set up magnifier position and event handlers
function setupMagnifier() {
   // set initial position of magnifier 
   magnifier._x = 226;
   magnifier._y = 133;

   // set up event handlers for magnifier
   magnifier.onEnterFrame = moveLenses;
   magnifier.onPress = makeDraggable;
   magnifier.onRelease = stopDraggable;
   magnifier.onReleaseOutside = stopDraggable;
}
	
// set position of each child lens (relative to magnifier position)
// this function is attached to magnifier and called at intervals = frame rate
function moveLenses() {
   for(i=1; i<=nlenses; i++) {
      var lensSize:Number = 10 + 90*(nlenses+1-i)/nlenses;
      distortion = 100 + distortRatio*i;
      this["lens"+i].image._y = -1*distortion/lensSize*this._y;
      this["lens"+i].image._x = -1*distortion/lensSize*this._x;
   }
}

setupLenses();
setupMagnifier();

Making it dynamic

The code above requires that the jpg to be distorted be embedded in the magnifier and lens movieclips at movie startup. To make the movie work with an external jpg instead, you can replace the image in the main timeline and the contents of the image movieclip with a blank movieclip with instance name picholder. Because you can't duplicate a movieclip with loaded content (and have the duplicate contain the loaded content), we switched to using attachMovie in the dynamic version. Replacing image with picholder in the lens movieclip, we then removed lens from the stage (no need for it to be there), found it in the library, right-clicked it, chose Linkage, checked Export for Actionscript, and gave it an export Identifier of lens. We (my rabbit and I) created three buttons and set their onRelease handlers to call setupLenses, passing the link to the external jpg to load. In this example, all the jpgs were created the same size so we wouldn't have to worry about resizing, repositioning, etc. setupLenses was also changed to accept a url (indicating where to find the jpg), and loadMovie was used to load the jpg into each attached lens movieclip, immediately after the attach. This is the revised setupLenses function:

// setup: create lenses and set their sizes (and photo sizes)
// thispic = url of picture to use as background
function setupLenses(thispic:String) {
   // set initial position of magnifier 
   magnifier._x = 232;
   magnifier._y = 146;

   picholder.loadMovie(thispic);
   // create lenses in graduated sizes (progressively smaller)
   for(i=1; i<=nlenses; i++) {
      var lensSize:Number = 10 + 90*(nlenses+1-i)/nlenses;
      magnifier.attachMovie("lens", "lens"+i, i);
      magnifier["lens"+i].image.picholder.loadMovie(thispic);
      // each lens+i will be created in magnifier 
      magnifier["lens"+i]._xscale=lensSize;
      magnifier["lens"+i]._yscale=lensSize;
      distortion = 100+distortRatio*i;
      magnifier["lens"+i].image._yscale=100*distortion/lensSize;
      magnifier["lens"+i].image._xscale=100*distortion/lensSize;
   }
}

The completed fla for this example can be obtained by subscription from the link at right. In the fla, I put the magnifier frame graphic in a guide layer to hide it (uncheck Guide for that layer if you want the frame). You could also add embellishments like displaying the currently selected button, choosing the name of the picture from a dropdown box component, or reading the pictures from an XML file.

Intro
Flash: What & How
Example Sites
Create
Draw, Edit Shapes
Gradients
More Drawing Tips
Import
A Sample
Animate
Frames, Keyframes
Motion Tweens
More Motion Tweens
Shape Tweens
Masks
Control
Stop/Replay
Movieclips Intro
Movieclip Reference
Site Structure 1
Slideshow Movieclip
Contact Form
Scroll Resume
Preloader
Site Structure 2
Publish
Display Options
Player Detection
Optimize
AS 2.0 Basics
Intro to Syntax
Playhead Commands
Playhead Cmds 2
Coded Tween
onEnterFrame
Intro to Classes
Declare/Assign
Comments, Trace
Simple Data Types
Arrays & Objects
Code Blocks
Operators
Beyond Buttons
Code Structure
Toggle Controls
Group of Buttons
Drag and Hit
Distort Magnifier
Scroll Text
Bee Game
Dart Shooter
Sound Control
Easing Slider
Easing Slider 2
Components Intro
Timers & Delays
Dynamic Content
Intro
Drawing API
Create Text
Attach Movieclips
Easing Slider 3
Easing Slider 4
Load jpg/swf
Sliding Viewer
Preload swf
XML
Easing Slider 5
Server Comm
LoadVars (w/ PHP)
AS - PHP Lookup
Text File
Database 1:LoadVars
Database 2:Remoting
Read from directory
AS 2.0 Classes
Intro
Math
Key
Date
Color
EventDispatcher
New Samples
Pie Chart
Event-model Emailer
Tween Sequence
Fuse Sequence
SVG in Flash
Bitmap Topo
SWF as Data Holder
Two-level Menu
Yahoo! Flash Maps
Class-based Game
ASTB Samples
Disclaimer
3D Outlines
Bounce Collide
Address Book
Save Drawings
Home  :  Notes Index  :  Resources  :  About/Contact  :  Downloads