Source Code and Comments

We examine two important AS3 gesture events: GESTURE_ZOOM and GESTURE_PAN. We show how to use the AS3 MatrixTransformer class to zoom around a specific point in an image rather than around the registration point. We make panning smooth by removing GESTURE_ZOOM listener in the BEGIN phase of GESTURE_PAN. We provide here complete source code. If you want to see the app in action, open the Android Market on your phone, search for flashandmath, and locate Zoom and Pan in AIR on our apps list. You can then download and install the app. Below are sceen shots of the app.

Download

  • Download all source files corresponding to this app (Flash CS5 and CS5.5 formats): zpcode.zip

Working with the Source Files

The zip file contains ZoomPanFM.fla - a Flash CS5.5 file and ZoomPanCS5.fla - a Flash CS5 file. In Flash CS5.5, you simply open ZoomPanFM.fla. Under AIR for Android Publish settings, in General select Portrait mode, under Deployment enter the location or create your p12 certificate and choose deployment settings that you want, under Permissions don't check anything. In addition, the package contains our useful InfoMobile AS3 class whose instance added as a child of the MainTimeline provides basic information about the frame rate and memory consumption of any app at runtime.

You can use ZoomPanFMCS5.fla if you have AIR for Android Extension for Flash CS5 installed.

Code and Comments

Below is complete Timeline source code with comments within the code.

import flash.display.Bitmap;

import flash.display.BitmapData;

import flash.display.Sprite;

import flash.display.StageAlign;

import flash.display.StageScaleMode;

import flash.events.MouseEvent;

import flash.events.TransformGestureEvent;

import flash.ui.Multitouch;

import flash.ui.MultitouchInputMode;

import fl.motion.MatrixTransformer;

import flash.geom.Matrix;

import flash.geom.Point;

 

stage.align = StageAlign.TOP_LEFT;

stage.scaleMode = StageScaleMode.NO_SCALE;

/*
We set Multitouch input mode to MultitouchInputMode.GESTURE, so gesture events are recognized. You can check if and what gesture events are supported on a given device using Multitouch.supportsGestureEvents Boolean property, and get the list of supported gestures from Multitouch.supportedGestures Vector.<String> property. We skip this check here.
*/

Multitouch.inputMode = MultitouchInputMode.GESTURE;

/*
Our Bitmap image will be placed in a Sprite called 'container'.
*/

var container:Sprite=new Sprite();

this.addChild(container);

/*
We create an instance of our custom performance monitor 'InfoMobile' to display the frame rate and memmory consumption of our app at runtime.
*/

var info:InfoMobile=new InfoMobile();

this.addChild(info);

/*
We place the MovieClips created on the stage in front of 'container'.
*/

setChildIndex(mcPanel,numChildren-1);

setChildIndex(mcHelp,numChildren-1);

mcHelp.visible=false;

/*
We placed a jpeg in the Library and linked it to AS3 as BitmapData named 'Nancy'. Its dimesions are 1500 by 2000 pixels. Within the function 'init' we add liteners to buttons.
*/

var bdWidth:Number=1500;

var bdHeight:Number=2000;

var bitmap:Bitmap;

 

init();

 

function init():void {

mcPanel.btnExit.addEventListener(MouseEvent.CLICK, exitApp);

mcPanel.btnStart.addEventListener(MouseEvent.CLICK,startRestart);

mcPanel.btnHelp.addEventListener(MouseEvent.CLICK, showHelp);

}

/*
When the function 'startRestart' runs for the first time, we create a Bitmap, bitmap, containing our BitmapData 'Nancy', scale it down, so it fits on the stage, and add it as a child of 'container. In the part that runs each time the button start/restart is tapped, we add to 'container' listeners to GESTURE_ZOOM and GESTURE_PAN if they are not there. (A listener to GESTURE_ZOOM could have been removed by panning. See onPan.) We reset the scale on position of 'container'.
*/

function startRestart(e:MouseEvent):void {

if(container.numChildren==0){

var ratio:Number;

bitmap=new Bitmap(new Nancy(bdWidth,bdHeight));

container.addChild(bitmap);

ratio=Math.min(stage.stageHeight/bdHeight,stage.stageWidth/bdWidth);

ratio=Math.min(ratio,1);

bitmap.width = bdWidth * ratio;

bitmap.height = bdHeight * ratio;

}

if(!container.hasEventListener("gestureZoom")){

container.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);

}

if(!container.hasEventListener("gesturePan")){

container.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);

}

container.x=0;

container.y=0;

container.scaleX=1;

container.scaleY=1;

}

/*
Within the GESTURE_ZOOM handler, we make sure that 'container is not scaled more than 600% or less than 80%.
 
We use the MatrixTranformer class from the package fl.motion and its static method MatrixTransformer.matchInternalPointWithExternal to keep the point on which we are zooming in its original place in relation to the stage. That creates an impression of zooming about a point. We use TransformGestureEvent's properties localX, localY, stageX, stageY to remember the position of our zoom center relative to 'container' and to the stage so we can realign them after zooming using MatrixTransformer.
*/

function onZoom(event:TransformGestureEvent):void {

var locX:Number=event.localX;

var locY:Number=event.localY;

var stX:Number=event.stageX;

var stY:Number=event.stageY;

var prevScaleX:Number=container.scaleX;

var prevScaleY:Number=container.scaleY;

var mat:Matrix;

var externalPoint=new Point(stX,stY);

var internalPoint=new Point(locX,locY);

container.scaleX *= event.scaleX;

container.scaleY *= event.scaleY;

if(event.scaleX > 1 && container.scaleX > 6){

container.scaleX=prevScaleX;

container.scaleY=prevScaleY;

}

if(event.scaleY > 1 && container.scaleY > 6){

container.scaleX=prevScaleX;

container.scaleY=prevScaleY;

}

if(event.scaleX < 1 && container.scaleX < 0.8){

container.scaleX=prevScaleX;

container.scaleY=prevScaleY;

}

if(event.scaleY < 1 && container.scaleY < 0.8){

container.scaleX=prevScaleX;

container.scaleY=prevScaleY;

}

mat=container.transform.matrix.clone();

MatrixTransformer.matchInternalPointWithExternal(mat,internalPoint,externalPoint);

container.transform.matrix=mat;

}

/*
GESTURE_ZOOM and GESTURE_PAN events have phases. During GESTURE_PAN BEGIN phase, we remove the listener to GESTURE_ZOOM to prevent annoying zooming and panning at the sam time. We restore the listener in the END phase.
*/

function onPan(event:TransformGestureEvent):void {

if(event.phase==GesturePhase.BEGIN){

container.removeEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);

}

var prevX:Number=container.x;

var prevY:Number=container.y;

container.x+=event.offsetX;

container.y+=event.offsetY;

if(event.offsetX >= 0 && container.x > stage.stageWidth/2){

container.x=prevX;

}

if(event.offsetX < 0 && container.x < -container.width+stage.stageWidth/2){

container.x=prevX;

}

if(event.offsetY >= 0 && container.y > stage.stageHeight/2){

container.y=prevY;

}

if(event.offsetY < 0 && container.y < -container.height+stage.stageHeight/2){

container.y=prevY;

}

if(event.phase==GesturePhase.END){

container.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);

}

}

/*
When the user taps on Exit button, the app quits.
*/

function exitApp(event:MouseEvent):void {

NativeApplication.nativeApplication.exit(0);

}

/*
btnHelp shows the MovieClip with the description of the app.
*/

function showHelp(e:MouseEvent):void {

mcHelp.visible=true;

container.visible=false;

info.visible=false;

mcHelp.addEventListener(MouseEvent.CLICK,hideHelp);

}

 

function hideHelp(e:MouseEvent):void {

mcHelp.visible=false;

container.visible=true;

info.visible=true;

mcHelp.removeEventListener(MouseEvent.CLICK,hideHelp);

}

Note:   Our experiments indicate that on Android phones for TransformGestureEvent.GESTURE_ZOOM, at least the phones on which we checked, scaling in the X and Y directions are always the same:

event.scaleX=event.scaleY

Thus, the code within the onZoom function could be simplified.

You can find more detailed explanations of the

MatrixTransformer.matchInternalPointWithExternal(mat,internalPoint,externalPoint);

method in our tutorial: How To Zoom In on a Point in an Image and Pan in AS3 Flash.

This tutorial was written by Barbara Kaskosz of flashandmath.

Back to AIR for Android              Back to Flash and Math Home

We welcome your comments, suggestions, and contributions. Click the Contact Us link below and email one of us.

Adobe®, Flash®, ActionScript®, Flex® are registered trademarks of Adobe Systems Incorporated.