轨迹回放有几个方案:
方案一、调用arcgis自己的api,设置定时器,一段时间清空当前点,绘制下一个点。
缺点:点的移动不是连续的,一闪一闪的,而且速度不均匀
方案二、利用HTML5的canvas,原理和上面一样
缺点:同上
方案三、利用SVG制作动画
没有缺点,非常完美
通过比较,最后使用SVG进行制作轨迹回放。
这里有几个难点:
1、屏幕坐标和经纬度的同步
2、SVG动画层会遮盖地图层的事件
3、由于是连续动画,移动到某时刻并不一定在路径数组的点上,暂停和漫游需要知道回放到的路基数组的具体区间
实现的思路:
1、启动的时候,动态把SVG的路径解析出来
2、设置定时器,频率要比播放速度快,尽量快就好了。
定时器里面做两个事情:a、判断是否到达下一个点,把数组偏移量记录下来。b、判断是否碰到屏幕边界,屏幕边界需要漫游。
当然,这个定时器只有在播放的时候触发逻辑。
3、zoomin和zoomout,漫游的时候删除原来SVG动画,通过第二步的数组偏移量,把后续的路径按照新的屏幕坐标解析出来。当前轨迹点也同理重新绘制
4、屏幕坐标和经纬度的同步可以通过arcgis api里面的方法进行
5、事件被遮盖的问题,SVG提供了一种方式,通过embed 的wmode="opaque"设置可以解决。但是我这里的SVG如果潜入到embed中,则不方便动态获取DOM,也就不好实现SVG的重绘。因此排除掉此方法。
考虑到被遮盖的事件只有鼠标点击和滚轮的zoomin和zoomout,鼠标拖动的漫游。而且希望在播放状态的漫游中会自动适配到轨迹,暂停和停止不做限制,因此考虑自己来实现arcgis的以上事件。
6、鼠标点击和滚轮的zoomin和zoomout的事件通过SVG的鼠标点击事件,事件中直接调用arcgis的api即可。由于SVG没有鼠标拖拽事件,则需要独立实现,把移动的方向获取,然后调用arcgis的api即可。
下面看一下关键代码
核心对象:
//track用来存储轨迹状态,动态获得播放当前状态
var track={
//轨迹的初始经纬度,一旦获得则不能被改变
originalLon:[],
originalLat:[],
//未播放的路径经纬度
lon:[],
lat:[],
//屏幕坐标
x:[],
y:[],
//当前播放点的经纬度
now:[],
//当前播放点的屏幕坐标
nowXY:[],
//轨迹长度
length:0,
//当前数组偏移量
i:0,
//屏幕边界点坐标
limitXY:[],
//设置当前数组偏移量和判断是否碰壁,碰壁则漫游
nowTimer:setInterval(function(){
if(!document)
return;
//设置当前数组偏移量
if(track.lon[track.i]-track.now()<0.0001 && track.lat[track.i]-track.now[1]<0.0001){
if(processManager.isStart)
track.i++;
//判断是否碰壁,碰壁则漫游
if(track.nowXY[0]>=track.limitXY[0] || track.nowXY[1]>=track.limitXY[1] ||
track.nowXY[0]<=0 || track.nowXY[1]<=0){
if(processManager.isStart){
var point=new esri.geometry.Point({
x:track.now[0],
y:track.now[1]
});
map.centerAt(point);
}
}
},50)
)};
//processManager对象用来存储播放状态
var processManager={isStart:false};
//SVGEvent类用来实现SVG的扩展事件
var SVGEvent=function(){
...
}
//用静态方法来实现事件绑定
SVGEvent.bindEvent=function(element,eventName,fn){
if(!element || element=="svg")
element=window;
//鼠标滚动事件
if(eventName=="mousewheel"){
window.onmousewheel=document.onmousewheel=fn;
}
//鼠标拖动事件
if(eventName=="mouseDrag"){
var state=false,original_x,original_y;
element.οnmοusedοwn=function(event){
original_x=e.clientX;
original_y=e.clientY;
state=true;
};
element.οnmοusemοve=function(event){
if(state){
var e=event ? event : window.event;
}
};
element.οnmοuseup=function(event){
if(state){
state=false;
var diffX=parseInt(event.clientX-original_x);
var diffY=parseInt(event.clientY-original_y);
var dx="center";
var dy="center";
var d="center";
if(Math.abs(diffX)>80)
dx=diffX>0 ? "right" : "left";
if(Math.abs(diffY)>80)
dy=diffY>0 ? "down" : "up";
if(dx!="center" && dy!="center")
d=dx+dy;
if(dx!="center" && dy=="center")
d=dx;
if(dx=="center" && dy!="center")
d=dy;
fn.call(element,event,dx,dy,d,[event,clientX,event.clientY],[original_x,original_y]);
original_x=0;
original_y=0;
original_left=0;
original_top=0;
}
}
}
}
function init(){
//地图的初始化省略
...
//zoomin、zoomout后重新同步坐标,并且重绘动画
dojo.connect(map,'onZoomEnd',function(){
zoomSVG();
});
//鼠标滚动
SVGEvent.bindEvent("svg","mousewheel",wheel);
//鼠标拖动
SVGEvent.bindEvent("svg","mouseDrag",drag); //初始化边界坐标
track.limitXY=[document.body.clientWidth,docment.body.clientHeight];
//初始化屏幕坐标
initScreenXY();
//初始化svg
initSVG(track);
//加载轨迹线
addTrack(track);
}
function initScreenXY(){
track.x=[];
track.y=[];
for(var i=0;i
动画控制的核心代码如下:
function play(){
if(!processManager.isStart){
document.getElementById("animateMotion").beginElement();
processManager.isStart=true;
}else{
document.getElementById("svg").unpauseAnimations();
}
}
}
function pause(){
document.getElementById("svg").pauseAnimations();
processManager.isStart=false;
}
function stop(){
track.i=0;
processManager.isStart=false;
pause();
clearSVG();
initScreenXY();
initSVG(track);
}
function clearSVG(){
var circle=document.getElementById("circle");
circle.parentNode.removeChild(circle);
var motionPath=document.getElementById("motionPath");
motionPath.parentNode.removeChild(motionPath);
}
function zoomSVG(){
document.getElementById("svg").pausseAnimations();
clearSVG();
reDrawSVG();
if(processManager.isStart)
play();
}
function panSVG(){
document.getElementById("svg").pausseAnimations();
clearSVG();
reDrawSVG();
if(processManager.isStart)
play();
}
function reDrawSVG(){
//初始化剩余路线的坐标信息
var lon=[];
var lat=[];
var x=[];
var y=[];
//是否播放到最后一段,最后一段特殊处理
if(track.i
获得当前点的经纬度,屏幕坐标核心代码如下:
function now(){
var circle=document.getElementById("circle");
if(circle){
//当前的屏幕坐标
var ctm=circle.getCTM();
var x=circle.getAttribute("cx");
var y=circle.getAttribute("cy");
track.nowXY[0]=x*ctm.a+y*ctm.c+ctm.e;
track.nowXY[1]=x*ctm.b+y*ctm.d+ctm.f;
//当前的经纬度坐标
var screenPoint=new esri.geometry.ScreenPoint({
x:track.nowXY[0],
y:track.nowXY[1]
});
var point=map.toMap(screenPoint);
track.now[0]=point.x;
track.now[1]=point.y;
}
}
//事件的绑定在init中,这里是绑定的function
function wheel(evt){
if(evt.deltaY>0){
zoomIn();
}
else{
zoomOut();
}
}
function zoomIn(){
var extent=map.extent;
map.setExtent(extent.expand(0.5));
}
function zoomOut(){
var extent=map.extent;
map.setExtent(extent.expand(2));
}
function drag(event,dx,dy,d,point,originalPoint){
if(d=="right")
map.panLeft();
if(d=="left")
map.panRight();
if(d=="up")
map.panDown();
if(d=="down")
map.panUp();
if(d=="leftup")
map.panLowerRight();
if(d=="leftdown")
map.panUpperRight();
if(d=="rightup")
map.panLowerLeft();
if(d=="rightdown")
map.panUpperLeft();
}