地图拖动效果实现.docx
《地图拖动效果实现.docx》由会员分享,可在线阅读,更多相关《地图拖动效果实现.docx(26页珍藏版)》请在冰豆网上搜索。
地图拖动效果实现
一个实现思路
1.第一次显示,以某坐标为中心(例如玩家城池坐标),读取周围特定菱形范围内的坐标。
读取要求:
至少要超出屏幕范围
2.鼠标按下,记录拖动开始位置
3.鼠标移动,拖动整个地图
4.鼠标抬起,根据当前位置和拖动开始位置,计算拖动经过了多少个单元。
并查找当前显示的地图块中,符合这个条件的地图块,记录世界坐标和其XY坐标
5.以新的世界坐标为中心,重新获取地图数据,并把中心地图块显示到刚才的XY位置。
给玩家在视觉上是平滑拖动的效果
onMouseDown map.startDrag();
onMouseup map.stopDrag();
--
varp=newArray();
varspeed=0.05;//速度
varpicWidth=1280;//大图的宽高
varpicHeight=971;
varx,y//鼠标点下去时背景的坐标
varx_new,y_new//位移
varhaveclick=false;
functiongetmouseposition(event)
{
if(document.all)
{
x=document.body.scrollLeft+event.clientX;
y=document.body.scrollTop+event.clientY;
}else
{
x=event.layerX;
y=event.layerY;
}
haveclick=true;
}
functionmovestop()
{
haveclick=false;
}
functionmovestart(event)
{
if(haveclick)
{
if(document.getElementById('pic').style.backgroundPosition.length==0)
{document.getElementById('pic').style.backgroundPosition="0px0px";}
p=document.getElementById('pic').style.backgroundPosition.split("")
if(document.all)
{
x_new=document.body.scrollLeft+event.clientX;
y_new=document.body.scrollTop+event.clientY;
}else
{
x_new=event.layerX;
y_new=event.layerY;
}
x2=(speed*(x_new-x)+parseInt(p[0])).toString(10);//计算位移量
y2=(speed*(y_new-y)+parseInt(p[1])).toString(10);
if(x2<-picWidth+420)x2=-picWidth+420;
if(y2>0)y2=0;
if(x2>0)x2=0;
if(y2<-picHeight+300)y2=-picHeight+300;
document.getElementById('pic').style.backgroundPosition=x2+"px"+y2+"px";
}
}
-->
★天地培训★案例17——Flash地图应用
[i=s]本帖最后由S_eVent于2011-11-1700:
40编辑[/i]
列位仙家……许久不见,现在咱啥也别说了,眼泪唰啦唰啦地……不,哥没哭,哥这是高兴……(旁白:
“高兴你个头……兴你个头……你个头……个头……头……”)
我*,你***掺和什么掺和,哥跟仙家们见面关你*事,你再****我就*****,我*,怎么说出来的话都是星号?
!
还伴随着一声清脆的“滴……”声?
!
(旁白:
“神经病……”)切~说我发神经就是你没幽默感的表现了……
算了,不扯了,伤和气,这和气一伤,啪~容易扯着蛋~!
相信列位仙家都看过我的[url=
flash地图应用大致可分两分静态地图和动态地图两类,所谓静态地图就是地图始终是一张固定的图片,此图片会很大,长宽都可能是舞台的数倍,而动态地图就是一次只初始化用户可视范围内的地图数据,当拖动地图时会向服务器发起请求拿到新的一块地图区域的信息然后再渲染出来,像XX,谷歌,E都市的地图均是如此。
我们今天先讲静态地图。
看看我静态地图的最终结果先吧:
[url=
它的制作要点主要分两点:
一,拖拽移动视角;二,地图缩放。
第一点对于列位来说应该没有什么问题,若不明白再去翻翻案例五吧老湿……至于第二点呢,简单地说就是图片坐标以及scaleX,scaleY的改变,难点就在于保持以当前视野中心点为中心等比缩放上面。
我们知道,当你对一个Sprite或是其他显示对象通过改变scaleX,scaleY进行缩放的时候,显示对象会以注册点(坐标零点)为中心进行缩放,那么当你把一张图片addChild到一个Sprite中后,改变此Sprite的scaleX,scaleY进行缩放时会发现图片会向右下角延伸,要想让图片等比缩放必须把图片中心点放在Sprite原点才行。
不信你试试?
[flash]
测试过之后你一定能体会到,只有注册点在中心时才可能做到在改变scale时等比缩放。
不过我们的地图在按下鼠标后会被拖动,不停地在改变其坐标位置,所以你一开始把注册点设置在地图图片中心位置是不够的,注册点还得跟着鼠标的拖动而不停地改变。
有人就问了,啊?
注册点也能动态改变吗?
额~其实这个“注册点”得加引号啦,其实质还是x,y的变化,让我们看看它的原理所在。
首先,我们一个地图对象(可以为bitmap也可以把bitmap放至一个Sprite中)的默认原点位置(0,0)依然在左上角,若不更改地图addChild时的位置为负的一半地图长、宽,则对地图进行缩放时地图将会向右下方扩散。
我们看下图
[attach]59273[/attach]
假设黑色区域为地图的父容器(可以看作是舞台,这也是我们用户的可视区域),白色十字为原注册点,在父容器中心,当淡蓝色的原地图放大时,我们能够想象注册点将会向右下方跑。
此时用户会明显地看到刚才父容器中心点位置往右移动了,这自然不能达到我们想要的目的,我们是希望用户视野中心点位置保持不变然后等比缩放它的四周区域。
那怎么办呢?
自然是把随着scale的放大而往右下方偏移的注册点移动回来。
那么到底需要移动多少呢?
从图上我们其实可以看出,注册点在地图放大过程中右移的位置为它缩放前后与父容器(黑色区域)左边缘的距离之差,同理,我们也可以得到下移了的位置:
offsetX=X0-X0*scaleX;
offsetY=Y0-Y0*scaleY;
上面的公式中的X0,Y0是注册点相对于父容器的横纵坐标,而不是相对于地图本身,因为地图本身会进行缩放,在不停地变动,那么就不能以它自身作为参照物,就像你想测一测你跑步有多快,你肯定是以一样静止的物体,比如一棵大树为参照物,不可能以一个运动的物体,如一个和你并行奔跑的人为参照物,若你以运动的物体作为参照物,那你只会感觉你跑的很慢甚至没有移动过,但实际上你已经跑得跟狗一样快了。
要计算地图相对于父容器的横纵坐标,使用localToGlobal以及globalToLocal方法是再简单不过的了:
varA:
Point=mc.parent.globalToLocal(mc.localToGlobal(regpoint)) //先把注册点转换为全局坐标再转换为它父容器的坐标
mc.scaleX=mc.scaleY=value;
//执行放大后,再重新计算全局坐标
varB:
Point=mc.parent.globalToLocal(mc.localToGlobal(regpoint))
varoffsetX:
Number=A.x-B.x;
varoffsetY:
Number=A.y-B.y;
如此便可求得我们注册点在放大/缩小后移动了的距离。
为了让缩放后改变了的注册点回到父容器中心,我们把地图的横纵坐标进行如下调整:
mc.x+=offsetX;
mc.y+=offsetY
验证一下有没有错误。
若地图放大,则offsetX,offsetY将是负值,若缩小,则为正值,地图放大后注册点往右下方偏离(见上图所示),所以为了让注册点回到父容器中心,需把地图往左上移动,那么让地图的横纵坐标加上一个负值就等于让地图的横纵坐标进行了减小,确实能够使地图往左上移动。
OK,证明这样做没错后就可以把这个原理用到我们的实际开发中去了。
需要注意的是,我们的这个所谓的“注册点”不过只是一个参照点而已,它的用途就是在缩放前后计算用户所关注的点偏移了的位置,然后用这个位置来让地图x,y进行移动以保证用户关注点保持在原来的“位置”。
不过如果我们要对地图进行拖动的话就必须改变地图的x,y坐标,当地图坐标改变时我们的“注册点”位置也必须跟着移动以保证它在地图中所处的位置不变才行。
不然当你拖动了地图,注册点还留在原地的话它在地图中所处位置就会变掉了,缩放后计算出来的偏移位置就会出现错误。
看看源码:
假设我们在水平以及竖直方向拖动地图的距离分别为distX和distY,那么拖动后地图坐标为
map.x+=distX;
map.y+=distY;
假设我们起初设置的地图位置为
map.x=0;
map.y=0;
注册点位置为舞台中心点regPoint=newPoint(stage.stageWidth/2,stage.stageHeight/2);
那么移动了地图后它的位置依然要和map的左上角保持距离:
regpoint.x=-map.x+stage.stageWidth/2;
regpoint.y=-map.y+stage.stageHeight/2;
若地图进行过缩放,那么就需要把地图坐标换算为1倍大小时候的坐标再进行计算:
regpoint.x=-map.x/map.scaleX+stage.stageWidth/2;
regpoint.y=-map.y/map.scaleY+stage.stageHeight/2;
切记,每当地图坐标改变时,注册点坐标也得跟着改变。
阅读完了理论知识的一些要点之后看看全部源码,其中还不乏一些需要注意的地方:
[code]
[SWF(width="600",height="500")]
publicclassASMap2extendsSprite
{
[Embed(source="assets/wowmap.jpg")]
privatevarMapResource:
Class;
privatevarmap:
Sprite=newSprite();//地图
privatevaroldX:
Number;//拖动前X
privatevaroldY:
Number;//拖动后Y
privatevarcurrentMapZoom:
Number=1;//当前地图缩放比例
privatevarmaxZoomLevel:
int=5;//最大可放大次数
privatevarminZoomLevel:
int=-3;//最大可缩小次数
privatevarcurrentZoomLevel:
int=1;//当前缩放次数
privatevarzoomValue:
Number=0.1;//每次放大或缩小的比例
privatevarbmp:
Bitmap;
privatevarregpoint:
Point//地图注册点
publicfunctionASMap2()
{
initView();
initBtn();
}
privatefunctioninitView():
void{
bmp=newMapResource();
map.addChild(bmp);
addChild(map);
map.x=0;//地图真实注册点在0,0位置
map.y=0;
regpoint=newPoint(stage.stageWidth/2,stage.stageHeight/2);//虚拟注册点位置为舞台中心
map.doubleClickEnabled=true;//设置地图容器能接受双击事件
map.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
map.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
map.addEventListener(MouseEvent.DOUBLE_CLICK,onDoubleClick);
map.cacheAsBitmap=true;//缓存为bitmap,提高拖动效率
}
privatefunctioninitBtn():
void{
varzoomIn:
MyButton=newMyButton("放大",50,25);
varzoomOut:
MyButton=newMyButton("缩小",50,25);
zoomIn.x=240;
zoomOut.x=300;
zoomIn.y=zoomOut.y=15;
addChild(zoomIn);
addChild(zoomOut);
zoomIn.addEventListener(MouseEvent.CLICK,zoomInHandler);
zoomOut.addEventListener(MouseEvent.CLICK,zoomOutHandler);
}
/**
*设置镜头
*
*/
privatefunctionsetCamera(x:
Number,y:
Number):
void{
vardesX:
Number= map.x+stage.stageWidth/2-x;
vardesY:
Number= map.y+stage.stageHeight/2-y;
//使用TweenLite缓动改变地图x,y值时,x,y是缓缓改变而不是立马改变的(切记),所以需要在缓动结束后再改变注册点
TweenLite.to(map,0.6,{x:
Math.min(Math.max(desX,stage.stageWidth-map.width),0),
y:
Math.min(Math.max(desY,stage.stageHeight-map.height),0),
onComplete:
offsetRegpoint});
// offsetRegpoint(); //注意不能在这里改变注册点,因为此时map的x,y还未变到预期值
}
privatefunctiononMouseDown(event:
MouseEvent):
void{
addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
stage.addEventListener(Event.MOUSE_LEAVE,onMouseUp);//防止按住鼠标拖动过程中鼠标滑出flashplayer窗口后再回来造成的鼠标跟随现象
oldX=stage.mouseX;
oldY=stage.mouseY;
}
privatefunctiononMouseMove(event:
MouseEvent):
void{
//地图拖动(视角移动)
vardistX:
Number=stage.mouseX-oldX;
vardistY:
Number=stage.mouseY-oldY;
map.x+=distX;
map.y+=distY;
//限制地图移动范围,防止露出舞台背景
map.x=Math.min(0,map.x);
map.x=Math.max((stage.stageWidth-map.width),map.x);
map.y=Math.min(0,map.y);
map.y=Math.max((stage.stageHeight-map.height),map.y);
//地图位置一变,注册点必须跟着变
offsetRegpoint();
oldX=stage.mouseX;
oldY=stage.mouseY;
}
privatefunctiononMouseUp(event:
Event):
void{
removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove);
}
privatefunctiononDoubleClick(event:
MouseEvent):
void{
setCamera(event.stageX,event.stageY);
}
privatefunctionzoomInHandler(event:
MouseEvent):
void{
if(currentZoomLevel+1<=maxZoomLevel){
zoom(0);
}
}
privatefunctionzoomOutHandler(event:
MouseEvent):
void{
if(currentZoomLevel-1>=minZoomLevel){
zoom
(1);
}
}
/**
*根据地图位置移动注册点
*
*/
privatefunctionoffsetRegpoint():
void{
regpoint.x=-map.x/map.scaleX+stage.stageWidth/2;
regpoint.y=-map.y/map.scaleY+stage.stageHeight/2;
}
/**
*缩放
*@paramaction 操作方式:
0为放大,1为缩小
*
*/
privatefunctionzoom(action:
int):
void{
if(action==0){