Skip to main content
October 28, 2011
Answered

How to centre of map remains centered during map scale

  • October 28, 2011
  • 2 replies
  • 775 views

Hi all,

I've created an mc which functions as my map and controls to go with that which allow the map to be moved around and key points navigated to and zoomed in on at varying zoom levels.

I'm using scaleX and scaleY to scale the map mc and a list of x and y positions to correctly position the map for each key point. The map mc starts at 1:1 scale ratio with the screen and it's registration point is at 0,0.

When I want to go to a certain area I perform this calculation (offsetX and offsetY are the center of the screen and posX and posY are predined offsets on the map):

newX = posX * scale + offsetX;

newY = posY * scale + offsetY;

Then I tween the position and scale of the map to smoothly scale and move the map to the correct position:

var tween = new TweenLite(_background, EASING_SPEED, {x:newX, y:newY,scaleX:scale.getScale(),scaleY:scale,ease:E ASING_TYPE,onComplete:moveToComplete,onCompletePar  ams:[room]});

I now need to implement a zoom in / out function and this is what I'm struggling with. The zoom should use the center of the screen as the reg point for the scale, so I've been trying something along the lines of the following to get the correct x and y position to set after the scale:

var newX =  (_background.x * scale) + offsetX;

var newY =  (_background.y * scale) + offsetY;

I know this is wrong but I've shown this to give you an idea of my train of thought, I need to work out what the new x and y will be to ensure that the area that is being zoomed in on, stays in the center of the screen.

So in my mind this gets the distance from the current position of the map relative to the center point of the screen (offsetX, offsetY) then scales that distance by the new scale.

However, this is clearly wrong because it's not working and the positioning of the map is wrong.

If someone could shed some light on where I'm going wrong I'd be really grateful as I've a deadline for first thing monday!

Thanks in advance

eb_dev

This topic has been closed for replies.
Correct answer

Ok, realised I'd made a silly mistake in the constraints code, it's now all working perfectly:

package map {

   

    import flash.events.Event;

    import flash.events.MouseEvent;

    import flash.display.MovieClip;

    import flash.display.DisplayObject;

    import flash.geom.Point;

    import flash.geom.Matrix;

    import flash.geom.Rectangle;

    import utils.scale.Scale;

    import utils.scale.events.ScaleEvent;

    import com.greensock.TweenLite;

    import com.greensock.TimelineLite;

    import com.greensock.easing.Quint;   

    import map.events.*;

   

    public class Map extends MovieClip {

        private var scale:Scale;

        public var screenSize:Rectangle = null;

        public var dragLimits:Rectangle;

        private var mapWidth:Number;

        private var mapHeight:Number;

        private var moveTweensComplete:Object;

        private var zoomLevels = [];

        private var zoomLevelIndex = 0;

       

        public var scaleContainer:MovieClip;

       

        private const ANGLE_DEGS_NE = -24;

        private const ANGLE_DEGS_SE = 32.7;

        private const ANGLE_DEGS_SW = 155.8;

        private const ANGLE_DEGS_NW = -147.1;

        private const MOVE_DISTANCE = 100;   

        private const BG_MOVE_SPEED = 4;       

        private const ZOOM_INCREMENT = 0.33;

        private const EASING_TYPE = com.greensock.easing.Quint.easeInOut;

        private const EASING_SPEED = 0.6;

        private const SCALE_AMOUNT = 0.10;

        private const ZOOM_LEVEL_1 = 0.33;

        private const ZOOM_LEVEL_2 = 0.67;

        private const ZOOM_LEVEL_3 = 1;

        private const ZOOM_LEVEL_4 = 1.3;

       

        public function Map() {

           

            /**

             * Listen for when added to the stage

             */   

            addEventListener(Event.ADDED_TO_STAGE, init);

        }

        private function init(event:Event)

        {

            /**

             * Set vars

             */

            zoomLevels = [

                              ZOOM_LEVEL_1,

                            ZOOM_LEVEL_2,

                            ZOOM_LEVEL_3,

                            ZOOM_LEVEL_4

                         ];

           

            scale = Scale.getInstance();

            scaleContainer = this;

            mapWidth = panContainer.width;

            mapHeight = panContainer.height;

            screenSize = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);

            moveTweensComplete = {pan:false,scale:false};

            getLimits();

        }

        public function getLimits()

        {           

            var left = Math.round((screenSize.width/2)/(scale.getScale()) - (mapWidth/2));

            if (left > 0) {

                left=0;

            }

            var top = Math.round((screenSize.height/2)/(scale.getScale()) - (mapHeight/2));

            if (top > 0) {

                top=0;

            }

            var right = Math.round((mapWidth/2) - (screenSize.width/2)/(scale.getScale()));

            if (right < 0) {

                right=0;

            }

            var bottom = Math.round((mapHeight/2) - (screenSize.height/2)/(scale.getScale()));

            if (bottom < 0) {

                bottom=0;

            }

            dragLimits = new Rectangle(left, top, (right-left), (bottom-top));

            //trace("dragLimits left: "+dragLimits.x+" top: "+dragLimits.y+" right-left: "+(right-left)+" bottom-top: "+(bottom-top));

           

        }               

        public function moveToPos(newX:Number = 0, newY:Number = 0, scale:Number = 0.33)

        {   

            this.scale.setScale(scale);

            getLimits();

           

            var newX = Math.round(newX);

            var newY = Math.round(newY);

           

            //trace("panContainer"+panContainer);

            //trace("dragLimits"+dragLimits);

           

            /**

             * Check extents and lock x / y coords if outside.

            */

            if (newX < dragLimits.left) {

                trace("Left bound reached " + dragLimits.left + " " + panContainer.x);

                newX = dragLimits.left;

            }

            if (newY < dragLimits.top) {

                trace("Left bound reached " + dragLimits.top + " " + panContainer.y);

                newY = dragLimits.top;

            }

            if (newX > dragLimits.right) {

                newX = dragLimits.right;

            }

            if (newY > dragLimits.bottom) {

                newY = dragLimits.bottom;

            }

           

            trace("moveToPos - x: "+newX+" y: "+newY);

           

            //var moveTweenTimeLine:TimelineLite = new TimelineLite({onComplete:moveComplete});

            //moveTweenTimeLine.append( new TweenLite(panContainer, EASING_SPEED, {x:newX, y:newY,ease:EASING_TYPE}),0);

            //moveTweenTimeLine.append( new TweenLite(scaleContainer, EASING_SPEED, {scaleX:scale,scaleY:scale,ease:EASING_TYPE}),-2);

            var panTwn = new TweenLite(panContainer, EASING_SPEED, {x:newX, y:newY,ease:EASING_TYPE,onComplete:panComplete});

            var scaleTwn = new TweenLite(scaleContainer, EASING_SPEED, {scaleX:scale,scaleY:scale,ease:EASING_TYPE,onComplete:scaleComplete})

        }

        public function moveToPosNoTween(newX:Number = 0, newY:Number = 0, scale:Number = 0.33)

        {           

            this.scale.setScale(scale);

            getLimits();

           

            var newX = Math.round(newX);

            var newY = Math.round(newY);

           

            /**

             * Check extents and lock x / y coords if outside.

            */

            if (panContainer.x < dragLimits.left)

            {

                newX = dragLimits.left;

            }

            if (panContainer.y < dragLimits.top)

            {

                newY = dragLimits.top;

            }

            if (panContainer.x > dragLimits.right)

            {

                newX = dragLimits.right;

            }

            if (panContainer.y > dragLimits.bottom)

            {

                newY = dragLimits.bottom;

            }

           

            trace("moveToPosNoTween - x: "+newX+" y: "+newY);

           

            /**

             * Pan, scale and dispatch complete event.

             */

            panContainer.x = newX;

            panContainer.y = newY;

            scaleContainer.scaleX = scale;

            scaleContainer.scaleY = scale;

            moveComplete();

        }               

        public function nudge(direction:String,tween:Boolean,distance:Number = MOVE_DISTANCE)

        {

            var newX = 0;

            var newY = 0;

           

            switch(direction)

            {

                case "NW":

/*                    var angleRadiansNW = ANGLE_DEGS_NW * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansNW));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansNW));    */

                    newX = panContainer.x + (distance);

                    newY = panContainer.y + (distance);   

                break;

                case "NE":

/*                    var angleRadiansNE = ANGLE_DEGS_NE * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansNE));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansNE));    */

                    newX = panContainer.x - (distance);

                    newY = panContainer.y + (distance);   

                break;

                case "SW":

/*                    var angleRadiansSW = ANGLE_DEGS_SW * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansSW));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansSW));*/

                    newX = panContainer.x + (distance);

                    newY = panContainer.y - (distance);   

                break;

                case "SE":

/*                    var angleRadiansSE = ANGLE_DEGS_SE * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansSE));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansSE));    */

                    newX = panContainer.x - (distance);

                    newY = panContainer.y - (distance);                       

                break;                   

            }

            if(tween)

            {

                moveToPos(newX,newY,scale.getScale());

            }

            else

            {

                moveToPosNoTween(newX,newY,scale.getScale());

            }

           

        }       

        public function zoomIn()

        {

            if(zoomLevelIndex < zoomLevels.length - 1)

            {

                zoomLevelIndex = zoomLevelIndex + 1;

                scale.setScale(zoomLevels[zoomLevelIndex]);

                trace("In(): "+scale.getScale());

                moveToPos(panContainer.x,panContainer.y,scale.getScale());               

            }

/*            if(scale.getScale() + SCALE_AMOUNT <= 1)

            {   

            }*/

        }

        public function zoomOut()

        {

            if(zoomLevelIndex > 0)

            {

                zoomLevelIndex = zoomLevelIndex - 1;

                scale.setScale(zoomLevels[zoomLevelIndex]);

                trace("In(): "+scale.getScale());

                moveToPos(panContainer.x,panContainer.y,scale.getScale());

            }

/*               

            if(scale.getScale() - SCALE_AMOUNT > 0.33)

            {               

            }*/

        }       

        public function zoomFullExtents()

        {

            if(scale.getScale() != ZOOM_LEVEL_1)

            {

                zoomLevelIndex = 0;

                scale.setScale(ZOOM_LEVEL_1);

                moveToPos(0,ZOOM_LEVEL_1);

            }           

        }               

        private function panComplete()

        {

            moveTweensComplete.pan = true;

            moveComplete();

        }

        private function scaleComplete()

        {

            moveTweensComplete.scale = true;

            moveComplete();

        }       

        private function moveComplete()

        {

            if(moveTweensComplete.pan && moveTweensComplete.scale)

            {

                dispatchEvent(new MapEvent(MapEvent.MOVE_COMPLETE,{}));

            }

        }       

       

    }

   

}

thanks for your advice Ned!

2 replies

Correct answer
October 30, 2011

Ok, realised I'd made a silly mistake in the constraints code, it's now all working perfectly:

package map {

   

    import flash.events.Event;

    import flash.events.MouseEvent;

    import flash.display.MovieClip;

    import flash.display.DisplayObject;

    import flash.geom.Point;

    import flash.geom.Matrix;

    import flash.geom.Rectangle;

    import utils.scale.Scale;

    import utils.scale.events.ScaleEvent;

    import com.greensock.TweenLite;

    import com.greensock.TimelineLite;

    import com.greensock.easing.Quint;   

    import map.events.*;

   

    public class Map extends MovieClip {

        private var scale:Scale;

        public var screenSize:Rectangle = null;

        public var dragLimits:Rectangle;

        private var mapWidth:Number;

        private var mapHeight:Number;

        private var moveTweensComplete:Object;

        private var zoomLevels = [];

        private var zoomLevelIndex = 0;

       

        public var scaleContainer:MovieClip;

       

        private const ANGLE_DEGS_NE = -24;

        private const ANGLE_DEGS_SE = 32.7;

        private const ANGLE_DEGS_SW = 155.8;

        private const ANGLE_DEGS_NW = -147.1;

        private const MOVE_DISTANCE = 100;   

        private const BG_MOVE_SPEED = 4;       

        private const ZOOM_INCREMENT = 0.33;

        private const EASING_TYPE = com.greensock.easing.Quint.easeInOut;

        private const EASING_SPEED = 0.6;

        private const SCALE_AMOUNT = 0.10;

        private const ZOOM_LEVEL_1 = 0.33;

        private const ZOOM_LEVEL_2 = 0.67;

        private const ZOOM_LEVEL_3 = 1;

        private const ZOOM_LEVEL_4 = 1.3;

       

        public function Map() {

           

            /**

             * Listen for when added to the stage

             */   

            addEventListener(Event.ADDED_TO_STAGE, init);

        }

        private function init(event:Event)

        {

            /**

             * Set vars

             */

            zoomLevels = [

                              ZOOM_LEVEL_1,

                            ZOOM_LEVEL_2,

                            ZOOM_LEVEL_3,

                            ZOOM_LEVEL_4

                         ];

           

            scale = Scale.getInstance();

            scaleContainer = this;

            mapWidth = panContainer.width;

            mapHeight = panContainer.height;

            screenSize = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);

            moveTweensComplete = {pan:false,scale:false};

            getLimits();

        }

        public function getLimits()

        {           

            var left = Math.round((screenSize.width/2)/(scale.getScale()) - (mapWidth/2));

            if (left > 0) {

                left=0;

            }

            var top = Math.round((screenSize.height/2)/(scale.getScale()) - (mapHeight/2));

            if (top > 0) {

                top=0;

            }

            var right = Math.round((mapWidth/2) - (screenSize.width/2)/(scale.getScale()));

            if (right < 0) {

                right=0;

            }

            var bottom = Math.round((mapHeight/2) - (screenSize.height/2)/(scale.getScale()));

            if (bottom < 0) {

                bottom=0;

            }

            dragLimits = new Rectangle(left, top, (right-left), (bottom-top));

            //trace("dragLimits left: "+dragLimits.x+" top: "+dragLimits.y+" right-left: "+(right-left)+" bottom-top: "+(bottom-top));

           

        }               

        public function moveToPos(newX:Number = 0, newY:Number = 0, scale:Number = 0.33)

        {   

            this.scale.setScale(scale);

            getLimits();

           

            var newX = Math.round(newX);

            var newY = Math.round(newY);

           

            //trace("panContainer"+panContainer);

            //trace("dragLimits"+dragLimits);

           

            /**

             * Check extents and lock x / y coords if outside.

            */

            if (newX < dragLimits.left) {

                trace("Left bound reached " + dragLimits.left + " " + panContainer.x);

                newX = dragLimits.left;

            }

            if (newY < dragLimits.top) {

                trace("Left bound reached " + dragLimits.top + " " + panContainer.y);

                newY = dragLimits.top;

            }

            if (newX > dragLimits.right) {

                newX = dragLimits.right;

            }

            if (newY > dragLimits.bottom) {

                newY = dragLimits.bottom;

            }

           

            trace("moveToPos - x: "+newX+" y: "+newY);

           

            //var moveTweenTimeLine:TimelineLite = new TimelineLite({onComplete:moveComplete});

            //moveTweenTimeLine.append( new TweenLite(panContainer, EASING_SPEED, {x:newX, y:newY,ease:EASING_TYPE}),0);

            //moveTweenTimeLine.append( new TweenLite(scaleContainer, EASING_SPEED, {scaleX:scale,scaleY:scale,ease:EASING_TYPE}),-2);

            var panTwn = new TweenLite(panContainer, EASING_SPEED, {x:newX, y:newY,ease:EASING_TYPE,onComplete:panComplete});

            var scaleTwn = new TweenLite(scaleContainer, EASING_SPEED, {scaleX:scale,scaleY:scale,ease:EASING_TYPE,onComplete:scaleComplete})

        }

        public function moveToPosNoTween(newX:Number = 0, newY:Number = 0, scale:Number = 0.33)

        {           

            this.scale.setScale(scale);

            getLimits();

           

            var newX = Math.round(newX);

            var newY = Math.round(newY);

           

            /**

             * Check extents and lock x / y coords if outside.

            */

            if (panContainer.x < dragLimits.left)

            {

                newX = dragLimits.left;

            }

            if (panContainer.y < dragLimits.top)

            {

                newY = dragLimits.top;

            }

            if (panContainer.x > dragLimits.right)

            {

                newX = dragLimits.right;

            }

            if (panContainer.y > dragLimits.bottom)

            {

                newY = dragLimits.bottom;

            }

           

            trace("moveToPosNoTween - x: "+newX+" y: "+newY);

           

            /**

             * Pan, scale and dispatch complete event.

             */

            panContainer.x = newX;

            panContainer.y = newY;

            scaleContainer.scaleX = scale;

            scaleContainer.scaleY = scale;

            moveComplete();

        }               

        public function nudge(direction:String,tween:Boolean,distance:Number = MOVE_DISTANCE)

        {

            var newX = 0;

            var newY = 0;

           

            switch(direction)

            {

                case "NW":

/*                    var angleRadiansNW = ANGLE_DEGS_NW * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansNW));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansNW));    */

                    newX = panContainer.x + (distance);

                    newY = panContainer.y + (distance);   

                break;

                case "NE":

/*                    var angleRadiansNE = ANGLE_DEGS_NE * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansNE));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansNE));    */

                    newX = panContainer.x - (distance);

                    newY = panContainer.y + (distance);   

                break;

                case "SW":

/*                    var angleRadiansSW = ANGLE_DEGS_SW * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansSW));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansSW));*/

                    newX = panContainer.x + (distance);

                    newY = panContainer.y - (distance);   

                break;

                case "SE":

/*                    var angleRadiansSE = ANGLE_DEGS_SE * (Math.PI/180);

                    newX = panContainer.x + (distance * Math.cos(angleRadiansSE));

                    newY = panContainer.y + (distance * Math.sin(angleRadiansSE));    */

                    newX = panContainer.x - (distance);

                    newY = panContainer.y - (distance);                       

                break;                   

            }

            if(tween)

            {

                moveToPos(newX,newY,scale.getScale());

            }

            else

            {

                moveToPosNoTween(newX,newY,scale.getScale());

            }

           

        }       

        public function zoomIn()

        {

            if(zoomLevelIndex < zoomLevels.length - 1)

            {

                zoomLevelIndex = zoomLevelIndex + 1;

                scale.setScale(zoomLevels[zoomLevelIndex]);

                trace("In(): "+scale.getScale());

                moveToPos(panContainer.x,panContainer.y,scale.getScale());               

            }

/*            if(scale.getScale() + SCALE_AMOUNT <= 1)

            {   

            }*/

        }

        public function zoomOut()

        {

            if(zoomLevelIndex > 0)

            {

                zoomLevelIndex = zoomLevelIndex - 1;

                scale.setScale(zoomLevels[zoomLevelIndex]);

                trace("In(): "+scale.getScale());

                moveToPos(panContainer.x,panContainer.y,scale.getScale());

            }

/*               

            if(scale.getScale() - SCALE_AMOUNT > 0.33)

            {               

            }*/

        }       

        public function zoomFullExtents()

        {

            if(scale.getScale() != ZOOM_LEVEL_1)

            {

                zoomLevelIndex = 0;

                scale.setScale(ZOOM_LEVEL_1);

                moveToPos(0,ZOOM_LEVEL_1);

            }           

        }               

        private function panComplete()

        {

            moveTweensComplete.pan = true;

            moveComplete();

        }

        private function scaleComplete()

        {

            moveTweensComplete.scale = true;

            moveComplete();

        }       

        private function moveComplete()

        {

            if(moveTweensComplete.pan && moveTweensComplete.scale)

            {

                dispatchEvent(new MapEvent(MapEvent.MOVE_COMPLETE,{}));

            }

        }       

       

    }

   

}

thanks for your advice Ned!

Ned Murphy
Legend
October 30, 2011

You're welcome

Ned Murphy
Legend
October 28, 2011

For what you seem to want to do, one solution is to have the mc within a container, where you keep the container always cnetered and zoom that, not the map mc.  You move the map within the container so that the point of the map you want to zoom is at the center of the container.

October 28, 2011

Hi Ned,

Thanks for your response. Unfortunately at this stage in the game I can't go down that route as there is various bits of functionality which are based on the currrent map setup and I won't have time before monday to change all that to suit.

Is the a way of achieving what I want with the map current setup? Would using a matrix transform in some way be the solution?

Thanks,

eb_dev

Ned Murphy
Legend
October 28, 2011

I honestly don't know any other way to do it, but I offered what I as as "one solution" because if I said it is the only way, then it might be a wrong claim.  Doing what I suggested should not be that difficult to work in.  You are just placing the mapo  inside a container, so the code just needs to be adjusted, not necessarily rewritten.