Skip to content

Instantly share code, notes, and snippets.

@wxzen
Created May 23, 2020 03:09
Show Gist options
  • Save wxzen/9deb922f29ba464fb64fd23efdabf45d to your computer and use it in GitHub Desktop.
Save wxzen/9deb922f29ba464fb64fd23efdabf45d to your computer and use it in GitHub Desktop.
用高德地图自定义canvas图层绘制带渐变效果的点标记,支持鼠标交互事件/弹跳动画,适合地图渲染大量点标记
/*
* Created: 2020-04-25 15:14:01
* Author : xuwei
* Email : [email protected]
* -----
* Description: 渐变点标记插件
*/
/* eslint-disable guard-for-in */
/* eslint-disable prefer-destructuring */
import TWEEN from '@tweenjs/tween.js'
const {
AMap
} = window;
class GradientMarkerManager {
constructor(map, colorsMapping, radius) {
this.map = map;
this.canvas = document.createElement('canvas');
this.canvas.id = 'stations-canvas';
this.colorsMapping = colorsMapping;
this.radius = AMap.Browser.retina ? radius * 2 : radius;
}
setMousemoveEventCallback(cbFunc) {
this.mousemoveCbFunc = cbFunc;
}
setMouseOverEventCallback(cbFunc) {
this.mouseOverCbFunc = cbFunc;
}
setMouseOutEventCallback(cbFunc) {
this.mouseOutCbFunc = cbFunc;
}
setMouseClickEventCallback(cbFunc) {
this.clickCbFunc = cbFunc;
}
setData(data) {
this.data = data;
}
addLayer() {
const {
map,
canvas,
data
} = this;
AMap.plugin('AMap.CustomLayer', () => {
canvas.id = 'stations-canvas';
this.layer = new AMap.CustomLayer(canvas, {
zooms: [3, 12],
alwaysRender: true, //缩放过程中是否重绘,复杂绘制建议设为false
zIndex: 110
});
const ctx = canvas.getContext("2d");
const onRender = () => {
const size = map.getSize(); //resize
let {
width,
height
} = size;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
if (AMap.Browser.retina) { //高清适配
width *= 2;
height *= 2;
}
canvas.width = width;
canvas.height = height; //清除画布
this.drawCanvas(data);
};
//events
map.on('click', e => {
const {
pixel
} = e;
for (const d in data) {
if (Object.prototype.hasOwnProperty.call(data, d)) {
for (let i = 0, len = data[d].length; i < len; i++) {
this.buildPath(map, data[d][i], ctx);
if (ctx.isPointInPath(pixel.x, pixel.y)) {
if (this.clickCbFunc) {
this.clickCbFunc(data[d][i]);
}
break;
}
}
}
}
});
map.on('mousemove', e => {
const {
pixel
} = e;
let isHover = false;
let point = null;
for (const k in data) {
if (Object.prototype.hasOwnProperty.call(data, k)) {
for (let i = 0, len = data[k].length; i < len; i++) {
this.buildPath(map, data[k][i], ctx);
if (ctx.isPointInPath(pixel.x, pixel.y)) {
isHover = true;
point = data[k][i];
break;
}
}
}
canvas.style.cursor = isHover ? 'pointer' : '';
if (isHover) {
if (this.mouseOverCbFunc) {
this.mouseOverCbFunc(point, pixel);
}
} else if (this.mouseOutCbFunc) {
this.mouseOutCbFunc();
}
}
});
this.layer.render = onRender;
this.layer.setMap(map);
});
}
buildPath(map, d, ctx) {
const pos = new AMap.LngLat(d.longitude, d.latitude);
const pixel = map.lngLatToContainer(pos);
ctx.beginPath();
ctx.moveTo(pixel.x + this.radius, pixel.y);
ctx.arc(pixel.x, pixel.y, this.radius, 0, Math.PI * 2);
ctx.closePath();
}
drawCanvas(data) {
const ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
for (const d in data) {
if (Object.prototype.hasOwnProperty.call(data, d)) {
data[d].forEach(e => {
this.drawPoint(e);
});
}
}
return ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
}
drawPoint(p, offsetY) {
const ctx = this.canvas.getContext('2d');
const {
longitude,
latitude,
color = 'green'
} = p;
const pos = new AMap.LngLat(longitude, offsetY !== undefined ? latitude + offsetY :
latitude);
let pixel = this.map.lngLatToContainer(pos);
if (AMap.Browser.retina) {
pixel = pixel.multiplyBy(2);
}
ctx.beginPath();
ctx.moveTo(pixel.x + this.radius, pixel.y);
ctx.arc(pixel.x, pixel.y, this.radius, 0, Math.PI * 2);
ctx.closePath();
const gradient = ctx.createRadialGradient(pixel.x, pixel.y, this.radius / 10, pixel.x,
pixel
.y, this.radius);
const colorList = this.colorsMapping[color];
gradient.addColorStop(0, colorList[0]);
gradient.addColorStop(0.8, colorList[1]);
gradient.addColorStop(1, colorList[2]);
ctx.lineWidth = 0.5;
ctx.fillStyle = gradient;
ctx.fill();
}
draw() {
const data = this.data;
if (!this.layer) {
this.addLayer();
} else {
this.drawCanvas(data);
}
}
clear() {
// const ctx = this.canvas.getContext('2d');
// ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
if (this.layer) {
this.map.remove(this.layer);
}
this.layer = null;
this.data = null;
}
clearByType(type) {
const ctx = this.canvas.getContext('2d');
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
const data = this.data;
for (const k in data) {
if (Object.prototype.hasOwnProperty.call(data, k)) {
if (k === type) {
delete data[k];
break;
}
}
}
if (JSON.stringify(data) !== '{}') {
this.drawCanvas(data);
}
}
animatePoint(stationInfo) {
let animatingFlag = true;
const data = this.data;
let point;
let index;
let stationType;
for (const k in data) {
if (Object.prototype.hasOwnProperty.call(data, k)) {
point = data[k].filter(e => e.stationCode === stationInfo.stationCode).pop();
if (point !== undefined) {
stationType = k;
index = data[k].indexOf(point);
break;
}
}
}
const canvas = this.canvas;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
const _data = (function () {
const list = {};
for (const k in data) {
if (Object.prototype.hasOwnProperty.call(data, k)) {
if (k !== stationType) {
list[k] = data[k].slice();
} else {
list[k] = data[k].slice(0, index).concat(data.slice(index + 1));
}
}
};
return list;
})();
// console.log('size', _data.length);
const backgroundImgdata = this.drawCanvas(_data);
//start an animation
const params = {
offset: 0
};
const tween = new TWEEN.Tween(params)
.to({
offset: 1 / 2 ** (this.map.getZoom() - 5)
})
.repeat(1)
.easing(TWEEN.Easing.Quadratic.Out)
.onUpdate(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(backgroundImgdata, 0, 0);
this.drawPoint(point, params.offset);
})
.onComplete(() => {
animatingFlag = false;
})
.yoyo(true);
function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time);
}
tween.start();
requestAnimationFrame(animate);
}
}
export default GradientMarkerManager;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment