4
votes

I am using TransformGestureEvent and StartDrag and StopDrag events and I am having some issues.

I will do my best to explain, When I zoom in, my image zooms in and its centered while zooming. After I zoom in, I start to drag to the top of my image, when I reach the top I cant scroll now more (everything is working as expected so far) but when I goto zoom out my image y axis is no longer at the top, there is a blank white space where my image should be.

Here is my code:

mapMC.addEventListener(TransformGestureEvent.GESTURE_ZOOM, zoom);
mapMC.addEventListener(MouseEvent.MOUSE_DOWN, dragStart);
mapMC.addEventListener(MouseEvent.MOUSE_UP, dragEnd);

//Zooming Method

function zoom (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=mapMC.scaleX;
    var prevScaleY:Number=mapMC.scaleY;
    var mat:Matrix;
    var externalPoint=new Point(stX,stY);
    var internalPoint=new Point(locX,locY);

    mapMC.scaleX *= event.scaleX;
    mapMC.scaleY *= event.scaleY;


    if(mapMC.scaleX > 6){
        mapMC.scaleX=prevScaleX;
        mapMC.scaleY=prevScaleY;
    }

    if(mapMC.scaleY > 6){
        mapMC.scaleX=prevScaleX;
        mapMC.scaleY=prevScaleY;
    }

    if(mapMC.scaleX < 1){
        mapMC.scaleX=1;
        mapMC.scaleY=1;
    }

    if(mapMC.scaleY < 1){
        mapMC.scaleX=1;
        mapMC.scaleY=1;
    }

    mat=mapMC.transform.matrix.clone();
    MatrixTransformer.matchInternalPointWithExternal(mat,internalPoint,externalPoint);
    mapMC.transform.matrix=mat;

}

//Dragging Start Method

function dragStart(e:MouseEvent):void
{
    mapMC.startDrag(false, new Rectangle(0,0,-mapMC.width + stage.stageWidth, -mapMC.height + stage.stageHeight));
}

//Dragging Stop Method

function dragEnd(e:MouseEvent):void
{
    mapMC.stopDrag();
}

What I am expected to happen is to zoom in on centre, be able to scroll within the Rectangle in my startDrag and zoom out on center while the top of the image stays at the top of the stage and the left of the image stays on the left of the stage.

I have tried adding these two lines of code in my zoom method, it works but I cant zoom in on center:

mapMC.y = 0;
mapMC.x = 0;

Here is a link to a sample project that will show you what I mean

https://drive.google.com/open?id=0B-p_9V3gzZZTcXpQZzI0RU9ZY0k

1
do you want the map to keep the ratio, or should it be possible to scale the x axis independently from the y axis? - Manuel Otto

1 Answers

2
votes

Since the builtin touch gestures of Air are not very handy for this, I've implemented a selfmade zoom functionality using MultitouchInputMode.TOUCH_POINT.

import flash.events.Event;
import flash.geom.Rectangle;
import flash.events.MouseEvent;
import flash.ui.MultitouchInputMode;
import flash.events.TouchEvent;
import flash.geom.Point;


//Vars

var touchpoints:Array = []
var touch_mode:String = null
var touch_start:Point
var map_start:Point
var map_zoom:Number
var map_width:Number
var map_height:Number
var zoom_startdist:Number

//Config

var max_zoom:Number = 6
var min_zoom:Number = 1
var map_bounds:Rectangle = new Rectangle(0,0,mapMC.width,mapMC.height) // this defines the area in which the map should fit


//Allow Touch Gestures

Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;

//Zoom and Drag Event Listeners

stage.addEventListener(TouchEvent.TOUCH_BEGIN, touchBegin)
stage.addEventListener(TouchEvent.TOUCH_MOVE, touchMoved)
stage.addEventListener(TouchEvent.TOUCH_END, touchEnd)

// Touch Handlers

function touchBegin(e:TouchEvent){
    if(touchpoints.length<2){ // only accept 2 active touchpoints, ignore the 3rd,4th,etc
        touchpoints.push({id:e.touchPointID,x:e.stageX,y:e.stageY})
        touchpointsChanged()
    }
}
function touchMoved(e:TouchEvent){
    var touchpoint:Object = getTouchPointById(e.touchPointID) // get current touchpoint...
    if(touchpoint){
        // ... and update the position
        touchpoint.x = e.stageX 
        touchpoint.y = e.stageY
        touchpointMoved()
    }
}
function touchEnd(e:TouchEvent){
    removeTouchPoint(e.touchPointID)
    touchpointsChanged()
}



// Pan & Zoom logic

function touchpointsChanged(){
    if(touchpoints.length==1){ // 1 touch point => panning
        startPan()
    }else if(touchpoints.length==2){ // 2 touch points => zooming
        startZoom()
    }
}

function startPan(){
    touch_mode = 'pan'
    touch_start = new Point(touchpoints[0].x,touchpoints[0].y)
    map_start = new Point(mapMC.x,mapMC.y)
}

function startZoom(){
    touch_mode = 'zoom'
    touch_start = getZoomCenter()
    zoom_startdist = getZoomDist()
    map_start = new Point(mapMC.x,mapMC.y)
    map_zoom = mapMC.scaleX
    map_width = mapMC.width
    map_height = mapMC.height
}

function touchpointMoved(){
    switch(touch_mode){
        case 'pan':
            mapMC.x = map_start.x + touchpoints[0].x - touch_start.x
            mapMC.y = map_start.y + touchpoints[0].y - touch_start.y
        break
        case 'zoom':
            var current_scale:Number = map_zoom*getZoomFactor() // get current zoom factor (relative to the previous zoom of the map)

            current_scale = Math.min(max_zoom,Math.max(min_zoom,current_scale)) // limit zoom (as defined in -> Config)

            mapMC.scaleX = mapMC.scaleY = current_scale

            // Set map position to the current center of zoom
            var zoom_pos:Point = getZoomCenter()
            mapMC.x = map_start.x + zoom_pos.x - touch_start.x
            mapMC.y = map_start.y + zoom_pos.y - touch_start.y

            // Correct map position regarding the scaling
            mapMC.x += -(current_scale-map_zoom)*map_width*((touch_start.x-map_start.x)/map_width)/map_zoom
            mapMC.y += -(current_scale-map_zoom)*map_height*((touch_start.y-map_start.y)/map_height)/map_zoom

        break
    }

    // Make sure the map doesnt leave its defined area - see Config (map_bounds)
    if(mapMC.x>map_bounds.x)
        mapMC.x = map_bounds.x
    else if(mapMC.x+mapMC.width<map_bounds.x+map_bounds.width)
        mapMC.x = map_bounds.x+map_bounds.width-mapMC.width

    if(mapMC.y>map_bounds.y)
        mapMC.y = map_bounds.y
    else if(mapMC.y+mapMC.height<map_bounds.y+map_bounds.height)
        mapMC.y = map_bounds.y+map_bounds.height-mapMC.height
}


// Helper functions

function getTouchPointById(id:int):Object{
    for(var i:uint=0;i<touchpoints.length;i++){
        if(touchpoints[i].id==id)
            return touchpoints[i]
    }
    return null
}

function removeTouchPoint(id:int){
    for(var i:uint=0;i<touchpoints.length;i++){
        if(touchpoints[i].id==id){
            touchpoints.splice(i,1)
            return
        }
    }
}

function getZoomCenter():Point{
    //get the average of the 2 touchpoints
    return new Point((touchpoints[0].x+touchpoints[1].x)/2,(touchpoints[0].y+touchpoints[1].y)/2)
}
function getZoomDist():Number{
    // calculate the distance between the 2 touchpoints
    return getDist(touchpoints[0].x,touchpoints[0].y,touchpoints[1].x,touchpoints[1].y)
}
function getZoomFactor():Number{
    // calculate the difference of distance between the 2 touchpoints from when the zoom gesture started
    return getZoomDist()/zoom_startdist
}
function getDist(x1:Number,y1:Number,x2:Number,y2:Number){
    // pythagoras, yo
    var deltaX:Number = x2-x1
    var deltaY:Number = y2-y1
    return Math.sqrt(deltaX*deltaX + deltaY*deltaY)
}

Let me know if anything is unclear, I'll try to add more comments.