文章更新于 2021-03-03
实现过程主要用到的Canvas API
绘制图像: drawImage()
获取图像数据: getImageData()
重写图像数据: putImageData()
导出图像: toDataURL()
1.创建主画布,确定大小,位置等
2.创建与主画布相同大小的虚拟图层画布,绘制内容,返回toDataURL()
3.虚拟图层合并渲染到主画布,将图像以此渲染到主画布drawImage()
4.通过鼠标位置,获取虚拟图层像素值,存在像素则进行下一步
5.通过鼠标位置与图层数据源计算,获取目标数据
6.通过鼠标位置+地图div到窗口的顶部(左部)位置,绝对定位绘制提示信息框
7.动画展示信息框
1、获取目标数据的另一种思路是,在构建点、线、面entity数据时,利用gco获取单独entity虚拟图层数据与属性一起归类保存。
2、利用鼠标坐标点数据,索引entity虚拟图层数据,如果存在像素值,则说明命中此数据。
//主函数
drawCanvas() {
let that = this;
let canvas = document.getElementById('map');
canvas.setAttribute('width', canvas.offsetWidth);
canvas.setAttribute('height', canvas.offsetHeight);
canvas.className = 'scanning-dashed-circle';
let context = canvas.getContext('2d');
await this.drawImage(require('../img/base.png'), canvas);
let map = {//base地图原始大小
width: 662,
height: 366
};
let points = this.basePool;//点数据源
let dataUrl = await this.drawPointLayer(require('../img/location.svg'), canvas, points, map);
await this.drawImage(dataUrl, canvas);
let getEntity = this.getLayerEntity(canvas, map, points);
let getColor = this.getLayerPixel(canvas, dataUrl);
let interPlay = this.setIntervalShow(points,getEntity);//初始化动画事件
interPlay.play();//首次自动播放
canvas.onclick = function(e) {
let x = e.offsetX === undefined ? e.layerX : e.offsetX;
let y = e.offsetY === undefined ? e.layerY : e.offsetY;
if (getColor2(x, y) === '暂无数据') {
that.mapOverview = false;
interPlay.stop();//暂停播放
} else {
getEntity(x, y);
setTimeout(interPlay.play(),3000);//延迟启动播放
}
};
}
//往canvas中绘制图片
drawImage(src, canvas, x = 0, y = 0, width = canvas.width, height = canvas.height) {
return new Promise(resolve => {
let context = canvas.getContext('2d');
let img = new Image();
img.onload = function() {
resolve(context.drawImage(this, x, y, width, height));
};
img.src = src;
});
}
// 绘制点图层,返回图层图像数据
async drawPointLayer(src, canvas, ps = [], map) {
let layer = document.createElement("canvas");
layer.width = canvas.width;
layer.height = canvas.height;
let ctx = layer.getContext("2d");
let lineWidth = 5;
for (const item of ps) {
let x = Math.abs(canvas.width / map.width * item.x);
let y = Math.abs(canvas.height / map.height * item.y);
item.x_ = x;
item.y_ = y;
await this.drawImage(require('../img/location.svg'), layer, x - 16, y - 32, 32, 40).then(res => {
//画笔绘制其他信息
// ctx.lineWidth = 1;
// ctx.strokeStyle = 'rgba(1,70,194,.5)';
// ctx.beginPath();
// ctx.arc(x, y, lineWidth + 1, 0, Math.PI * 2);
// ctx.closePath();
// ctx.stroke();
// ctx.fillStyle = 'rgba(30,199,230,.8)';
// ctx.beginPath();
// ctx.arc(x, y, lineWidth, 0, Math.PI * 2);
// ctx.closePath();
// ctx.fill();
});
}
return layer.toDataURL();
}
//通过距离计算,获取点数据
getLayerEntity(canvas, map, data) {
let that = this;
let wScale = canvas.width / map.width;
let hScale = canvas.height / map.height;
return function(x, y) {
// 求最短距离
let dis = 9999999;
let target = null;
data.forEach(item => {
let x_ = Math.abs(wScale * item.x);
let y_ = Math.abs(hScale * item.y);
let {x_, y_} = item;
let dis_ = Math.sqrt(Math.pow((x_ - x), 2) + Math.pow((y_ - y), 2));
if (dis > dis_ && dis_ < 50) {
dis = dis_;
target = item;
}
});
if (target) {
that.mapOverview = true;//显示信息框
that.clickPoint = target;//复制点数据
let map = document.getElementsByClassName('map-container')[0];
let node = document.getElementsByClassName('map-overview')[0];
let {t, l} = that.getOffset(map);//计算map div距离浏览器的像素数
if (node) {
node.style.top = (t + target.y_ - 40*4) + 'px';
node.style.left = (l + target.x_ + 32) + 'px';
}
} else {
that.mapOverview = false;//隐藏信息框
}
return target;
};
}
//虚拟图层中获取像素值
getLayerPixel(canvas, imgsrc, target) {
target = target || {
'rgba(0, 0, 0, 0)': '暂无数据'
};
let layer = document.createElement("canvas");
layer.width = canvas.width;
layer.height = canvas.height;
let ctx = layer.getContext("2d");
let img = new Image();
let pixelDataSet = [];
img.onload = function() {
ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
pixelDataSet = ctx.getImageData(0, 0, layer.width, layer.height).data;
};
img.src = imgsrc;
return function(x, y) {
let start = 4 * (x + (layer.width * y));
let output = target[`rgba(${pixelDataSet[start]}, ${pixelDataSet[start + 1]}, ${pixelDataSet[start + 2]}, ${pixelDataSet[start + 3]})`];
return output || [pixelDataSet[start], pixelDataSet[start + 1], pixelDataSet[start + 2], pixelDataSet[start + 3]].join(',');
};
}
//计算div距离浏览器的像素数
getOffset(obj) {
let t = obj.offsetTop;
let l = obj.offsetLeft;
// 判断是否有父容器,如果存在则累加其边距
while (obj = obj.offsetParent) {
t += obj.offsetTop;
l += obj.offsetLeft;
}
return { t, l};
}
//设置动画,轮播信息框
setIntervalShow(obj, fun) {
let i = 1;
let inter = null;
let play = function () {
inter = setInterval(() => {
if (i === obj.length) {
i = 0;
}
let {x_, y_} = obj[i];
fun.call({}, x_, y_);
i++;
}, 3000);
};
let stop = function () {
clearInterval(inter);
};
return {play, stop};
}