Flash - Fun with Maps
This article is from the original REM DEV blog (2008–2010). Some of the technologies it covers (Flex, Flash, AIR, JavaFX 1.x) have since been retired.
Runs the original map_demo_1.swf in your browser with the open-source Ruffle emulator.
I had to squish it down to fit in this page, hopefully you can get the rough idea.
Other than the Main class there are only two classes. The MapSprite class will get instantiated twice (once for the main map that you see when the app loads, and once for the details map that you see when you click on the main map). When MapSprite gets instantiated, it's constructor creates a few 'Pops', which are the red and green dots that you see on the map.
Note that the image of the US map, which serves as the background for the MapSprite class, is a vector image. This is important for when you zoom in to magnify the details window.
Here's the code (the Main class is long, but the others are pretty short and easy to follow)...
Main.as
package
{
import fl.controls.Button;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.Shape;
import flash.display.DisplayObject;
import flash.text.TextFormat;
import niallkadermap.*;
public class Main extends Sprite
{
//the main map in the bg
private var mainMap:MapSprite;
//sets starting position of mainMap, can't assume 0,0
private var mainMapX:Number = 0;
private var mainMapY:Number = 0;
//create the sprite that contains the detailsMap
//note: it will be cropped when we set it's scrollRect properties
private var detailsWindow:Sprite;
//create the map that sits inside detailsWindow
private var detailsMap:MapSprite;
//set the scrollRect boundaries of the detailsWindow
//ALSO used for zoomIn() and zoomOut()
private var detailsH:Number = 320;
private var detailsW:Number = 320;
//set the position of detailsWindow
private var detailsX:Number = 515;
private var detailsY:Number = 370;
//cross hairs in details window
private var hLine:Shape;
private var vLine:Shape;
//these 2 vars are instances of the map from the library
//one will be used as the background for the main map
//the other will be the background of the details map
public var bgMap1:MapImg = new MapImg();
public var bgMap2:MapImg = new MapImg();
public function Main():void
{
createMainMap();
createDetailsMap();
}
function createMainMap():void
{
//create the main map and pass in the bg image
mainMap = new MapSprite(bgMap1);
//clicking on the main map will make the details
//map visible, so attach the event listener...
mainMap.addEventListener(MouseEvent.CLICK, setDetailsMap)
//set map starting position - can't assumme that it will be 0,0
mainMap.x = mainMapX;
mainMap.y = mainMapY;
addChild(mainMap);
}
function createDetailsMap()
{
//create the details window that will contain the details map
detailsWindow = new Sprite();
//create a window so that we don't show the entire contents of
//the details window,
//the rectangular window will clip everything outside of it
//sw the only part of the details window visible will be inside the rectangle
//make the rect the dimensions that detailsWindow is supposed to be
detailsWindow.scrollRect = new Rectangle(0,0,detailsW,detailsH);
//allows us to use the hand cursor on hover
detailsWindow.buttonMode = true;
detailsWindow.useHandCursor = true;
//create the details map
detailsMap = new MapSprite(bgMap2);
//setup detailsMap for drag functionality
detailsMap.addEventListener(MouseEvent.MOUSE_DOWN, dragMap);
detailsMap.addEventListener(MouseEvent.MOUSE_UP, stopDragMap);
detailsWindow.addChild(detailsMap);
//create an outline around the details window so it's easy to see
var rect = new Shape();
rect.graphics.lineStyle(4,0x000000);
rect.graphics.drawRect(0,0,detailsW,detailsH);
detailsWindow.addChild(rect);
//make cross hairs so it's easy to see the center point of the detailsWindow
hLine = new Shape();
hLine.graphics.lineStyle(2,0x000000);
hLine.graphics.moveTo(0,detailsH/2);
hLine.graphics.lineTo(detailsW,detailsH/2);
detailsWindow.addChild(hLine);
vLine = new Shape();
vLine.graphics.lineStyle(2,0x000000);
vLine.graphics.moveTo(detailsW/2,0);
vLine.graphics.lineTo(detailsW/2,detailsH);
detailsWindow.addChild(vLine);
//set location of detailsWindow on the stage
detailsWindow.x = detailsX;
detailsWindow.y = detailsY;
//create a TextFormat for the buttons that
//we'll make in the next step...
var tf:TextFormat = new TextFormat();
tf.font = "Verdana";
tf.size = 18;
tf.bold = true;
//create the zoom in and out buttons
var btnIn:Button = new Button();
btnIn.height = 50;
btnIn.width = 50;
btnIn.label = "In";
btnIn.move(10,8);
btnIn.name = "btnIn";
btnIn.setStyle("textFormat",tf);
btnIn.addEventListener(MouseEvent.CLICK, zoomIn);
detailsWindow.addChild(btnIn);
var btnOut:Button = new Button();
btnOut.height = 50;
btnOut.width = 50;
btnOut.label = "Out";
btnOut.move(78,8);
btnOut.name = "btnIn";
btnOut.setStyle("textFormat",tf);
btnOut.addEventListener(MouseEvent.CLICK, zoomOut);
detailsWindow.addChild(btnOut);
//create close button and put it in the upper right corner of the detailsWindow
var btnCloseDetailsWindow = new Button();
btnCloseDetailsWindow.height = 40;
btnCloseDetailsWindow.width = 40;
btnCloseDetailsWindow.label = "X"
btnCloseDetailsWindow.move(detailsW - 50, 10);
btnCloseDetailsWindow.name = "btnCloseDetailsWindow";
btnCloseDetailsWindow.setStyle("textFormat",tf);
btnCloseDetailsWindow.addEventListener(MouseEvent.CLICK, closeDetailsWindow);
detailsWindow.addChild(btnCloseDetailsWindow);
detailsWindow.visible = false;
addChild(detailsWindow);
}
//////////////////////////////
//EVENT HANDLERS
//////////////////////////////
//scales detailsMap and centers it to the point that was clicke in the mainMap
//to be honest, the math doesn't make complete sense to me!!!!!! Just sort of stumbled into it
function setDetailsMap(evt:MouseEvent):void
{
detailsMap.scaleX = 2;
detailsMap.scaleY = 2;
detailsMap.x = -(evt.localX * detailsMap.scaleX)+detailsW/2;
detailsMap.y = -(evt.localY * detailsMap.scaleY)+detailsH/2;
detailsWindow.visible = true;
}
//allows zooming in for detailsWindow
//this math is a little fuzzy to me
//I played with it for a good while
function zoomIn(evt:MouseEvent):void
{
detailsMap.scaleX *= 1.5;
detailsMap.scaleY *= 1.5;
detailsMap.x = (detailsMap.x*1.5) - (detailsW/4);
detailsMap.y = (detailsMap.y*1.5) - (detailsH/4);
}
//allows zooming Out for detailsWindow
//once again, math is a little fuzzy to me
function zoomOut(evt:MouseEvent):void
{
detailsMap.scaleX *= .5;
detailsMap.scaleY *= .5;
detailsMap.x = (detailsMap.x*.5) + (detailsW/4);
detailsMap.y = (detailsMap.y*.5) + (detailsH/4);
}
//draggin functionality for detailsMap
function dragMap(evt:MouseEvent):void
{
detailsMap.startDrag();
}
function stopDragMap(evt:MouseEvent):void
{
detailsMap.stopDrag();
}
function closeDetailsWindow(evt:MouseEvent):void
{
detailsWindow.visible = false;
}
}
}
MapSprite.as
package niallkadermap
{
import flash.display.Sprite;
import flash.display.DisplayObject;
public class MapSprite extends Sprite
{
var popsAry:Array;
function MapSprite(bg:DisplayObject)
{
//add the backgroun img
//to this sprite
addChild(bg);
//create the 'pops'
popsAry = new Array(
new Pop(1,490,322,"POP 1"),
new Pop(2,505,222,"POP 2"),
new Pop(3,490,342,"POP 3"),
new Pop(4,318,331,"POP 4")
);
addPops();
}
public function addPops():void
{
//the pops will add themselves
//to this sprite
for(var i:int=0; i < popsAry.length; i++)
{
popsAry[i].addPop(this);
}
}
}
}
Pop.as
package niallkadermap
{
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
public class Pop extends Sprite
{
var pId:Number;
var pX:Number;
var pY:Number;
var popDetails:String;
public function Pop(pId:Number,pX:Number, pY:Number,popDetails:String)
{
this.pId = pId
this.pX = pX;
this.pY = pY;
this.popDetails = popDetails;
graphics.lineStyle(2,0x00FF00);
graphics.beginFill(0xFF0000);
graphics.drawCircle(this.pX,this.pY,4);
graphics.endFill();
}
function addPop(target:DisplayObjectContainer):void
{
//add this pop to the target...
target.addChild(this);
}
}
}
{
import fl.controls.Button;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.Shape;
import flash.display.DisplayObject;
import flash.text.TextFormat;
import niallkadermap.*;
public class Main extends Sprite
{
//the main map in the bg
private var mainMap:MapSprite;
//sets starting position of mainMap, can't assume 0,0
private var mainMapX:Number = 0;
private var mainMapY:Number = 0;
//create the sprite that contains the detailsMap
//note: it will be cropped when we set it's scrollRect properties
private var detailsWindow:Sprite;
//create the map that sits inside detailsWindow
private var detailsMap:MapSprite;
//set the scrollRect boundaries of the detailsWindow
//ALSO used for zoomIn() and zoomOut()
private var detailsH:Number = 320;
private var detailsW:Number = 320;
//set the position of detailsWindow
private var detailsX:Number = 515;
private var detailsY:Number = 370;
//cross hairs in details window
private var hLine:Shape;
private var vLine:Shape;
//these 2 vars are instances of the map from the library
//one will be used as the background for the main map
//the other will be the background of the details map
public var bgMap1:MapImg = new MapImg();
public var bgMap2:MapImg = new MapImg();
public function Main():void
{
createMainMap();
createDetailsMap();
}
function createMainMap():void
{
//create the main map and pass in the bg image
mainMap = new MapSprite(bgMap1);
//clicking on the main map will make the details
//map visible, so attach the event listener...
mainMap.addEventListener(MouseEvent.CLICK, setDetailsMap)
//set map starting position - can't assumme that it will be 0,0
mainMap.x = mainMapX;
mainMap.y = mainMapY;
addChild(mainMap);
}
function createDetailsMap()
{
//create the details window that will contain the details map
detailsWindow = new Sprite();
//create a window so that we don't show the entire contents of
//the details window,
//the rectangular window will clip everything outside of it
//sw the only part of the details window visible will be inside the rectangle
//make the rect the dimensions that detailsWindow is supposed to be
detailsWindow.scrollRect = new Rectangle(0,0,detailsW,detailsH);
//allows us to use the hand cursor on hover
detailsWindow.buttonMode = true;
detailsWindow.useHandCursor = true;
//create the details map
detailsMap = new MapSprite(bgMap2);
//setup detailsMap for drag functionality
detailsMap.addEventListener(MouseEvent.MOUSE_DOWN, dragMap);
detailsMap.addEventListener(MouseEvent.MOUSE_UP, stopDragMap);
detailsWindow.addChild(detailsMap);
//create an outline around the details window so it's easy to see
var rect = new Shape();
rect.graphics.lineStyle(4,0x000000);
rect.graphics.drawRect(0,0,detailsW,detailsH);
detailsWindow.addChild(rect);
//make cross hairs so it's easy to see the center point of the detailsWindow
hLine = new Shape();
hLine.graphics.lineStyle(2,0x000000);
hLine.graphics.moveTo(0,detailsH/2);
hLine.graphics.lineTo(detailsW,detailsH/2);
detailsWindow.addChild(hLine);
vLine = new Shape();
vLine.graphics.lineStyle(2,0x000000);
vLine.graphics.moveTo(detailsW/2,0);
vLine.graphics.lineTo(detailsW/2,detailsH);
detailsWindow.addChild(vLine);
//set location of detailsWindow on the stage
detailsWindow.x = detailsX;
detailsWindow.y = detailsY;
//create a TextFormat for the buttons that
//we'll make in the next step...
var tf:TextFormat = new TextFormat();
tf.font = "Verdana";
tf.size = 18;
tf.bold = true;
//create the zoom in and out buttons
var btnIn:Button = new Button();
btnIn.height = 50;
btnIn.width = 50;
btnIn.label = "In";
btnIn.move(10,8);
btnIn.name = "btnIn";
btnIn.setStyle("textFormat",tf);
btnIn.addEventListener(MouseEvent.CLICK, zoomIn);
detailsWindow.addChild(btnIn);
var btnOut:Button = new Button();
btnOut.height = 50;
btnOut.width = 50;
btnOut.label = "Out";
btnOut.move(78,8);
btnOut.name = "btnIn";
btnOut.setStyle("textFormat",tf);
btnOut.addEventListener(MouseEvent.CLICK, zoomOut);
detailsWindow.addChild(btnOut);
//create close button and put it in the upper right corner of the detailsWindow
var btnCloseDetailsWindow = new Button();
btnCloseDetailsWindow.height = 40;
btnCloseDetailsWindow.width = 40;
btnCloseDetailsWindow.label = "X"
btnCloseDetailsWindow.move(detailsW - 50, 10);
btnCloseDetailsWindow.name = "btnCloseDetailsWindow";
btnCloseDetailsWindow.setStyle("textFormat",tf);
btnCloseDetailsWindow.addEventListener(MouseEvent.CLICK, closeDetailsWindow);
detailsWindow.addChild(btnCloseDetailsWindow);
detailsWindow.visible = false;
addChild(detailsWindow);
}
//////////////////////////////
//EVENT HANDLERS
//////////////////////////////
//scales detailsMap and centers it to the point that was clicke in the mainMap
//to be honest, the math doesn't make complete sense to me!!!!!! Just sort of stumbled into it
function setDetailsMap(evt:MouseEvent):void
{
detailsMap.scaleX = 2;
detailsMap.scaleY = 2;
detailsMap.x = -(evt.localX * detailsMap.scaleX)+detailsW/2;
detailsMap.y = -(evt.localY * detailsMap.scaleY)+detailsH/2;
detailsWindow.visible = true;
}
//allows zooming in for detailsWindow
//this math is a little fuzzy to me
//I played with it for a good while
function zoomIn(evt:MouseEvent):void
{
detailsMap.scaleX *= 1.5;
detailsMap.scaleY *= 1.5;
detailsMap.x = (detailsMap.x*1.5) - (detailsW/4);
detailsMap.y = (detailsMap.y*1.5) - (detailsH/4);
}
//allows zooming Out for detailsWindow
//once again, math is a little fuzzy to me
function zoomOut(evt:MouseEvent):void
{
detailsMap.scaleX *= .5;
detailsMap.scaleY *= .5;
detailsMap.x = (detailsMap.x*.5) + (detailsW/4);
detailsMap.y = (detailsMap.y*.5) + (detailsH/4);
}
//draggin functionality for detailsMap
function dragMap(evt:MouseEvent):void
{
detailsMap.startDrag();
}
function stopDragMap(evt:MouseEvent):void
{
detailsMap.stopDrag();
}
function closeDetailsWindow(evt:MouseEvent):void
{
detailsWindow.visible = false;
}
}
}
MapSprite.as
package niallkadermap
{
import flash.display.Sprite;
import flash.display.DisplayObject;
public class MapSprite extends Sprite
{
var popsAry:Array;
function MapSprite(bg:DisplayObject)
{
//add the backgroun img
//to this sprite
addChild(bg);
//create the 'pops'
popsAry = new Array(
new Pop(1,490,322,"POP 1"),
new Pop(2,505,222,"POP 2"),
new Pop(3,490,342,"POP 3"),
new Pop(4,318,331,"POP 4")
);
addPops();
}
public function addPops():void
{
//the pops will add themselves
//to this sprite
for(var i:int=0; i < popsAry.length; i++)
{
popsAry[i].addPop(this);
}
}
}
}
Pop.as
package niallkadermap
{
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
public class Pop extends Sprite
{
var pId:Number;
var pX:Number;
var pY:Number;
var popDetails:String;
public function Pop(pId:Number,pX:Number, pY:Number,popDetails:String)
{
this.pId = pId
this.pX = pX;
this.pY = pY;
this.popDetails = popDetails;
graphics.lineStyle(2,0x00FF00);
graphics.beginFill(0xFF0000);
graphics.drawCircle(this.pX,this.pY,4);
graphics.endFill();
}
function addPop(target:DisplayObjectContainer):void
{
//add this pop to the target...
target.addChild(this);
}
}
}