地图拖动实现原理相对比较简单,就是地图面板容器随着鼠标移动的过程;地图图层及其他要素监听移地图动事件,进行更新状态操作。例如地图移动过程中瓦片图层需要加载当前视图范围未加载的瓦片,需要销毁超出当前范围的瓦片。
1 Event事件类设计
Event事件类通常会使用观察者设计模式,观察者模式定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。这种设计模式应用非常广泛,大家熟悉的Vue、leaflet、tomcat、Netty等等都有观察者模式的身影。js-event-bus是javascript开源的事件总线,大家可以拿来直接用,本文给大家简单写一个。
//事件类
class Event{
constructor() {
this.listener = [];
}
//注册事件
on(type, fn, ctx) {
this.listener.push({
type,
fn,
ctx
});
}
//触发事件
fire(type, object) {
this.listener.filter(item => {
return item.type === type;
}).forEach(item => {
let {type, fn, ctx} = item;
ctx = ctx || this;
fn && fn.call(ctx, object);
});
}
}
2 让地图类、图层类继承Event
使地图和图层等实例具有发布、订阅事件的能力,之后的几乎所有功能都离不开事件。鼠标拖动地图是触发move事件,瓦片、marker等图层及其他叠加物监听地图move事件,做出相应的更新。
//地图类
class Map extends Event{
...
}
//图层类
class Layer extends Event{
...
}
3 MouseHandler类,处理地图容器鼠标事件
在地图初始化的时候创建该实例,mouseHandler监听地图面板容器的鼠标事件,封装实现鼠标拖动、鼠标滚轮缩放等事件。
//处理鼠标事件类
class MouseHandler extends Event{
constructor(el) {
super();
this.el = el;
this.lastX = void 0;
this.lastY = void 0;
}
isPc() {
let userAgentInfo = navigator.userAgent;
let Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPod", "iPad"];
let flag = true;
for (let i = 0; i < Agents.length; i++) {
if (userAgentInfo.indexOf(Agents[i]) > 0) {
flag = false;
break;
}
}
return flag;
}
handlerDrag() {
let el = this.el;
let me = this;
if(this.isPc()) {
el.onmousedown = function(event) {
me.fire("movestart", event);
el.onmousemove = function(event1) {
me.fire("move", event1);
event1.preventDefault();
}
}
el.onmouseup = function(event) {
el.onmousemove = function(event1) {
return false;
}
me.fire("moveend", event);
}
}else {
el.ontouchstart = function(event) {
let thevent = event.touches[0];
me.lastX = thevent.pageX;
me.lastY = thevent.pageY;
me.fire("movestart", thevent);
el.ontouchmove = function(event1) {
let thevent1 = event1.touches[0];
thevent1.movementX = thevent1.pageX - me.lastX;
thevent1.movementY = thevent1.pageY - me.lastY;
me.lastX = thevent1.pageX;
me.lastY = thevent1.pageY;
me.fire("move", thevent1);
event1.preventDefault();
}
}
el.ontouchend = function(event) {
el.ontouchmove = function(event1) {
return false;
}
me.lastX = void 0;
me.lastY = void 0;
me.fire("moveend", event);
}
}
return me;
}
handlerWheel() {
let el = this.el;
let me = this;
el.onwheel = function(event) {
me.fire("wheel", event);
}
return me;
}
}
4 实现地图移动方法move(event)
class Map extends Event{
//…
move(event) {//地图移动方法
let {movementX, movementY} = event;
let {zoom, center, resolutions, project} = this;
//获取地图面板位置
let offset = this.getMapPos();
//计算并设定地图移动后位置
let offsetPoint = new Point(offset[0] + movementX, offset[1] + movementY).round();
this.mapPane.style["transform"] = `translate(${offsetPoint.x}px, ${offsetPoint.y}px)`;
//更新地图中心点经纬度
let resolution = resolutions[zoom];
let centerMeter = project.project(center);
let lastCenterMeter = centerMeter.add(-movementX * resolution, movementY * resolution);
this.center = project.unproject(lastCenterMeter);
//触发地图移动事件
this.fire("move", event);
}
}
5 Tile类监听地图move事件,更新瓦片
class Tile extends Layer{
onAdd(map) {
this.map = map;
this.tiles = [];
this.render();
this.map.on("move", this.onUpdate, this);
}
onUpdate() {
//更新瓦片方法总体思路是
//1 加载地图移动后需要加载的瓦片,
//2 移除超出地图范围的瓦片,
//这部分代码在历史文章中已经写过了,
//这里只做了一些修改,如需要请到github上自行查看。
//Github地址在本文底部。
}
}
6 初始化地图
let map = new Map({
center: new LonLat(
116.3,
39.85
),
zoom: 11
});
map.addTileLayer({
id: "tile",
url: "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png"
});
let arr = [ [116.42,39.93], [116.37,39.92], [116.43,39.88], [116.35,39.87],
[116.43,39.92], [116.28,39.85], [116.22,39.9], [116.3,39.95],
[116.1,39.93], [116.13,39.75], [116.65,39.92], [116.65,40.13],
[116.23,40.22], [116.33,39.73], [116.63,40.32], [117.12,40.13],
[116.83,40.37], [115.97,40.45]
];
let markers = [];
arr.forEach(i => {
let marker = {
lonlat: new LonLat(i[0], i[1]),
iconWidth: 40,
iconHeight: 40,
offset:[0, -20],
iconUrl: '../img/marker.png'
};
markers.push(marker);
})
let markerLayer = new MarkerLayer({
markers: markers
});
map.addLayer(markerLayer);
7 效果图
http://180.76.171.45:8080/gisdaily/page/pan.html
欢迎大家关注我的技术公众号:
文艺公众号: