效果图
前提:引入百度地图
index.html文件中 引入
<script src="https://api.map.baidu.com/api?v=3.0&ak=***"></script>
注意:ak 为百度地图的秘钥,必须是v=3.0
一、CanvasLayer.js
function CanvasLayer(options) {
this.options = options || {};
this.paneName = this.options.paneName || 'labelPane';
this.zIndex = this.options.zIndex || 0;
this._map = options.map;
this._lastDrawTime = null;
this.show();
}
CanvasLayer.prototype = new BMap.Overlay();
CanvasLayer.prototype.initialize = function (map) {
this._map = map;
let canvas = this.canvas = document.createElement("canvas");
let ctx = this.ctx = this.canvas.getContext('2d');
canvas.style.cssText = "position:absolute;" +
"left:0;" +
"top:0;" +
"z-index:" + this.zIndex + ";";
this.adjustSize();
this.adjustRatio(ctx);
map.getPanes()[this.paneName].appendChild(canvas);
let that = this;
map.addEventListener('resize', function () {
that.adjustSize();
that.adjustRatio(ctx);
that._draw();
});
return this.canvas;
}
CanvasLayer.prototype.adjustSize = function () {
let size = this._map.getSize();
let canvas = this.canvas;
canvas.width = size.width;
canvas.height = size.height;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
}
CanvasLayer.prototype.adjustRatio = function (ctx) {
let backingStore = ctx.backingStorePixelRatio ||
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
let pixelRatio = (window.devicePixelRatio || 1) / backingStore;
let canvasWidth = ctx.canvas.width;
let canvasHeight = ctx.canvas.height;
ctx.canvas.width = canvasWidth * pixelRatio;
ctx.canvas.height = canvasHeight * pixelRatio;
ctx.canvas.style.width = canvasWidth + 'px';
ctx.canvas.style.height = canvasHeight + 'px';
ctx.scale(pixelRatio, pixelRatio);
};
CanvasLayer.prototype.draw = function () {
let self = this;
let args = arguments;
clearTimeout(self.timeoutID);
self.timeoutID = setTimeout(function () {
self._draw.apply(self, args);
}, 15);
}
CanvasLayer.prototype._draw = function () {
let map = this._map;
this.canvas.style.left = -map.offsetX + 'px';
this.canvas.style.top = -map.offsetY + 'px';
this.dispatchEvent('draw');
this.options.update && this.options.update.apply(this, arguments);
}
CanvasLayer.prototype.getContainer = function () {
return this.canvas;
}
CanvasLayer.prototype.show = function () {
if (!this.canvas) {
this._map.addOverlay(this);
}
this.canvas.style.display = "block";
}
CanvasLayer.prototype.hide = function () {
this.canvas.style.display = "none";
this._map.removeOverlay(this);
}
CanvasLayer.prototype.setZIndex = function (zIndex) {
this.canvas.style.zIndex = zIndex;
}
CanvasLayer.prototype.getZIndex = function () {
return this.zIndex;
}
export {CanvasLayer};
二、引入CanvasLayer.js
1.将CanvasLayer.js放在src/assets/js目录下
2.在使用的页面引入
import {CanvasLayer} from '@/assets/js/CanvasLayer';
3.使用CanvasLayer.js绘制渐变色及箭头
<template>
<div class="map-box">
<baidu-map
id="baiduMap"
style="width: 100%;height: 100%;z-index:0;"
:center="center"
:zoom="zoom"
@ready="handler"
:min-zoom="5"
:max-zoom="15"
:scroll-wheel-zoom="true"
:mapClick="false" >
</baidu-map>
</div>
</template>
<script>
import {CanvasLayer} from '@/assets/js/CanvasLayer';
export default {
data(){
return{
map:'',
center:{
lng:114.52785300795644,
lat:38.14758574827796,
},
zoom:13,
canvasLayer:null,
canvasLayerPointer:null,
dataArr:[
{
center:{
lng:114.52785300795644,
lat:38.14758574827796,
},
num:1,
},
{
center:{
lng:114.54050114953694,
lat:38.13759635572114
},
num:10,
},
{
center:{
lng:114.56579743269792,
lat:38.12419932394176
},
num:25,
},
{
center:{
lng:114.5908781225365,
lat:38.12056580319661
},
num:51,
},
{
center:{
lng:114.6115750814864,
lat:38.11613720325717
},
num:12,
},
]
}
},
methods:{
//加载地图以及初始化数据
handler({BMap,map}) {
this.zoom = 13
this.map = map //百度地图实例化对象
this.drawRiver()
},
drawRiver(){
var that = this
// CanvasLayer构造函数需要两个参数: map为实例化后的地图对象 update:绘制的方法
if (this.canvasLayer == null) {
this.canvasLayer = new CanvasLayer({
map: that.map,
update: mapLine,
});
}
if(this.canvasLayerPointer==null){
this.canvasLayerPointer = new CanvasLayer({
map: that.map,
update: updatePointer,
});
}
this.canvasLayer.draw()
this.canvasLayerPointer.draw()
// 渐变河流的绘制
function mapLine() {
// getContext() 方法返回一个用于在画布上绘图的环境。(创建画布环境,用于图案绘制)
const ctx = that.canvasLayer.canvas.getContext('2d');
if (!ctx) {
return;
}
// clearRect() 方法擦除了指定的矩形,并且用一个透明的颜色填充它。(清除画布)
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// 绘制带速度颜色的轨迹
for (let i = 0, len = that.dataArr.length; i < len - 1; i += 1) {
//pointToPixel({lng,lat})将经纬度转化为坐标
const pixel = that.map.pointToPixel(that.dataArr[i].center);
const nextPixel = that.map.pointToPixel(that.dataArr[i + 1].center);
//beginPath() 方法开始一条路径,或重置当前的路径。
ctx.beginPath();
//moveTo开始一条路径,移动初始位置 aPixel.x, aPixel.y。
//lineTo创建到达位置 midPixel.x, midPixel.y 的一条线:
ctx.moveTo(pixel.x, pixel.y);
ctx.lineTo(nextPixel.x, nextPixel.y);
ctx.lineCap = 'round'; 绘制圆形的结束线帽 (线类型)例:'square'、'round'
ctx.lineWidth = 16; //绘制宽度(可自定义)
//createLinearGradient() 方法创建线性的渐变对象。需要四个参数 起点坐标和终点坐标进行渐变绘制
const grd = ctx.createLinearGradient(pixel.x, pixel.y, nextPixel.x,nextPixel.y);
//水质类型
const nowValue = that.dataArr[i].num
const nextValue = that.dataArr[i + 1].num
//gradient.addColorStop(stop,color);
//stop 介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。
//color 在结束位置显示的 CSS 颜色值
grd.addColorStop(0, getColorByValue(nowValue));
grd.addColorStop(1, getColorByValue(nextValue));
//strokeStyle 设置渐变颜色。
ctx.strokeStyle = grd;
ctx.stroke();stroke() 方法会实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。
}
}
// 箭头的绘制
function updatePointer() {
// getContext() 方法返回一个用于在画布上绘图的环境。
//当前唯一的合法值是 "2d",它指定了二维绘图,并且导致这个方法返回一个环境对象,该对象导出一个二维绘图。
const ctx = that.canvasLayerPointer.canvas.getContext("2d");
if (!ctx) {
return;
}
// clearRect() 方法擦除了指定的矩形,并且用一个透明的颜色填充它。
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
if (that.dataArr.length !== 0) {
for (let i = 0, len = that.dataArr.length; i < len - 1; i += 1) {
//百度地图 将经纬度转为坐标值
const pixel = that.map.pointToPixel(that.dataArr[i].center);//前一个坐标
const nextPixel = that.map.pointToPixel(that.dataArr[i + 1].center);//后一个坐标
//beginPath() 方法开始一条路径,或重置当前的路径。
ctx.beginPath();
// 根据渲染像素距离渲染箭头
// Math.abs() 获取绝对值的方格
if (
Math.abs(nextPixel.x - pixel.x) > 10 ||
Math.abs(nextPixel.y - pixel.y) > 10
) {
/**
* @method 获取以下五个点坐标
* @param {Object} 起点 pixel
* @param {Object} 终点 nextPixel
* @param {Object} 中心点 midPixel
* @param {Object} 箭头端点1 startPoint
* @param {Object} 箭头端点2 endPoint
*/
// 计算中心点
const midPixel = new BMap.Pixel(
(pixel.x + nextPixel.x) / 2,
(pixel.y + nextPixel.y) / 2
);
// 起点终点距离 运用了数学中的勾股定理
const distance = ((nextPixel.x - pixel.x) ** 2 + (nextPixel.y - pixel.y) ** 2) ** 0.5;
// 箭头长度
const pointerLong = 6;
// 箭头端点1 --- 起点坐标
const startPoint = arrowPoint(pointerLong,midPixel,distance,nextPixel,pixel).aPixel
// 箭头端点2 --- 终点坐标
const endPoint = arrowPoint(pointerLong,midPixel,distance,nextPixel,pixel).bPixel
//moveTo开始一条路径,移动初始位置 startPoint.x, startPoint.y。
//lineTo创建到达位置 midPixel.x, midPixel.y 的一条线:
ctx.moveTo(startPoint.x, startPoint.y); //从端点1开始移动
ctx.lineTo(midPixel.x, midPixel.y);//端点1画到中心点
ctx.lineTo(endPoint.x, endPoint.y);//中心点画到端点2
ctx.lineWidth = 3;//设置线对的宽度
ctx.strokeStyle = "#eee";//设置线的颜色
ctx.lineCap = "round";//绘制圆形的结束线帽
ctx.stroke();//stroke() 方法会实际地绘制出通过 moveTo() 和 lineTo() 方法定义的路径。
}
}
}
}
/**
* @method getColorByValue 获取颜色
* @param value 判断标准
*/
function getColorByValue(value) {
if (value <= 10) {
return '#409eff'
} else if (value > 10 && value <= 50) {
return '#fcff40'
} else {
return '#f56c6c'
}
}
/**
* @method arrowPoint 箭头起点和终点坐标计算规则
* @param pointerLong 箭头长度
* @param midPixel 中心点
* @param distance 两点之间的距离
* @param nextPixel 终端
* @param pixel 起点
*/
function arrowPoint(pointerLong,midPixel,distance,nextPixel,pixel){
// 声明箭头端点1的坐标
const aPixel = {};
// 声明箭头端点2的坐标
const bPixel = {};
// 箭头端点1、箭头端点2坐标计算规则
if (nextPixel.x - pixel.x === 0) {// 下一个X轴坐标等于上一个坐标
if (nextPixel.y - pixel.y > 0) {//下一个Y轴坐标大于上一个坐标
aPixel.x = midPixel.x - pointerLong * 0.5 ** 0.5;
aPixel.y = midPixel.y - pointerLong * 0.5 ** 0.5;
bPixel.x = midPixel.x + pointerLong * 0.5 ** 0.5;
bPixel.y = midPixel.y - pointerLong * 0.5 ** 0.5;
} else if (nextPixel.y - pixel.y < 0) {//下一个Y轴坐标小于上一个坐标
aPixel.x = midPixel.x - pointerLong * 0.5 ** 0.5;
aPixel.y = midPixel.y + pointerLong * 0.5 ** 0.5;
bPixel.x = midPixel.x + pointerLong * 0.5 ** 0.5;
bPixel.y = midPixel.y + pointerLong * 0.5 ** 0.5;
}
} else { // 下一个X轴坐标不等于上一个坐标
const k0 =
(-(2 ** 0.5) * distance * pointerLong +
2 * (nextPixel.y - pixel.y) * midPixel.y) /
(2 * (nextPixel.x - pixel.x)) + midPixel.x;
const k1 = -((nextPixel.y - pixel.y) / (nextPixel.x - pixel.x));
const a = k1 ** 2 + 1;
const b = 2 * k1 * (k0 - midPixel.x) - 2 * midPixel.y;
const c =
(k0 - midPixel.x) ** 2 + midPixel.y ** 2 - pointerLong ** 2;
aPixel.y = (-b + (b * b - 4 * a * c) ** 0.5) / (2 * a);
bPixel.y = (-b - (b * b - 4 * a * c) ** 0.5) / (2 * a);
aPixel.x = k1 * aPixel.y + k0;
bPixel.x = k1 * bPixel.y + k0;
}
return {aPixel,bPixel}
}
}
}
}
</script>
<style scoped>
.map-box{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
</style>
注:本文基于此链接文章基础上进行整理,可查看 https://blog.csdn.net/Tron_future/article/details/112832289.
如若引用本文内容,请注明出处