首先在页面中放上地图图片,并建立三个canvas标签,分别用于点、迁徙线、动态效果
<div class="mapBox">
<div class="map">
<img src="@/assets/shanxi.svg" alt="">
</div>
<!-- 线 -->
<canvas id="canvas" class="canvas"></canvas>
<!-- 点 -->
<canvas id="canvasPoint" class="canvas"></canvas>
<!-- 动态效果 -->
<canvas id="canvasMove" class="canvas"></canvas>
</div>
初始化canvas
初始化时,需要给各个canvas画布一个确定的宽高,否则画布会使用固定的宽300高150,这时如果另外使用的css给canvas画布制定宽高样式,会导致画布被拉伸,里面内容也会跟着被拉伸
data() {
return {
canvas: null,
canvasPoint: null,
canvasMove: null,
center: {
}, // 迁徙线起点位置
directionArr: [], // 迁徙线终点位置
endKeep: [], // 保存一下各个迁徙线起点
end: [], // 运动中的各迁徙线时间p时所在位置
p: 0, // 时间记录,每到1时变为0
step: 0.005, // 时间每次递增量
animationSpeed: 0.03, // 点动画效果圆圈每次增加量
dotNumber: 25, // 动画迁徙线 动态的线的部分由多少个点组成
rate: 1.053, // 1.033 贝塞尔曲线计算时用到的参数
requestAnimationFrameName: '',
compareData: [ // 用于临时计算各终点位置的参数
{
x: 0.65, y: 0.89 },
{
x: 0.094, y: 0.76 },
{
x: 0.95, y: 0.28 },
{
x: 0.19, y: 0.19 },
{
x: 0.49, y: 0.08 }
]
};
},
mounted() {
this.init();
},
methods: {
init() {
// 获取需要画布达到的宽高数据
const mapBox = document.getElementsByClassName('mapBox')[0];
const width = mapBox.offsetWidth;
const height = mapBox.offsetHeight;
// 拿到三个画布,给定宽高
const canvas = document.getElementById('canvas');
const canvasPoint = document.getElementById('canvasPoint');
const canvasMove = document.getElementById('canvasMove');
canvas.width = width;
canvas.height = height;
canvasPoint.width = width;
canvasPoint.height = height;
canvasMove.width = width;
canvasMove.height = height;
this.canvas = canvas.getContext('2d');
this.canvasPoint = canvasPoint.getContext('2d');
this.canvasMove = canvasMove.getContext('2d');
// 找到所有迁徙线起点,项目中我的起点是太原,所以大概找到一下
this.center = {
x: Math.ceil(width * 0.52),
y: Math.ceil(height * 0.48)
};
// 各线终点 以下仅为参考,具体以项目要求为准
for (let i = 0; i<= 4; i++) {
this.directionArr[i] = {
x: Math.ceil(width * compareData[index].x),
y: Math.ceil(height * compareData[index].y)
}
this.endKeep[index] = {
x: this.center.x,
y: this.center.y
};
}
this.end = JSON.parse(JSON.stringify(this.endKeep));
},
画布一:固定的迁徙线画布
drawAllLine() {
// 根据每个点分别画线
this.directionArr.forEach(item => {
this.drawLine(item);
});
},
drawLine({
x, y }) {
this.canvas.beginPath();
this.canvas.moveTo(this.center.x, this.center.y); // 起始点(x,y)
// 计算贝塞尔曲线控制点位置
const coord = this.calcCp([x, y], [this.center.x, this.center.y]);
this.canvas.quadraticCurveTo(coord.x, coord.y, x, y); //创建二次贝塞尔曲线
// 线宽1
this.canvas.lineWidth = 1;
// 线颜色
this.canvas.strokeStyle = '#5cb85c';
this.canvas.stroke();
this.canvas.closePath();
},
/*
* num: 要被转换的数字
* exnum: 当前中心坐标 不一定是x还是y
*/
calcCp(start, end) {
let middleX = 0;
let middleY = 0;
if (start[0] > end[0] && start[1] > end[1]) {
middleX = ((start[0] + end[0]) / 2) * this.rate;
middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
}
if (start[0] > end[0] && start[1] < end[1]) {
middleX = ((start[0] + end[0]) / 2) * this.rate;
middleY = ((start[1] + end[1]) / 2) * this.rate;
}
if (start[0] < end[0] && start[1] > end[1]) {
middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);
}
if (start[0] < end[0] && start[1] < end[1]) {
middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);
middleY = ((start[1] + end[1]) / 2) * this.rate;
}
return {
x: middleX,
y: middleY
};
},
画布二:各终点效果
data中增加关于点的一些参数
radius: 1, // 航路点半径
radiusRing: 1,
radiusRingMin: 1,
radiusRingMax: 25, // 最大设为25时,涟漪消失的不会很突兀
dotColor: '243,254,193',
ringColor: 'rgba(236,210,32,0.5)'
还需要增加控制动画执行的方法
drawPoint(x1, y1) {
// 最里圈小圆
this.canvasPoint.fillStyle = `rgba(${
this.dotColor}, 1)`;
this.canvasPoint.beginPath();
this.canvasPoint.arc(x1, y1, this.radius, 0, 2 * Math.PI);
this.canvasPoint.closePath();
this.canvasPoint.fill();
// 外层小圆
this.canvasPoint.fillStyle = `rgba(${
this.dotColor}, 0.3)`;
this.canvasPoint.beginPath();
this