vue2+canvas 根据坐标实现小车移动
canvas小车移动
·
20221112_220533
vue2+canvas 根据坐标实现小车移动
最近接手了一个大屏项目,负责部分技术支持,其中包括本次小车移动动画。在思考后共有两种方案,第一种是使用position:absolute,加上top,left和transform:rotate()属性控制小车移动,第二种使用canvas中的定位加旋转画布实现。本文主要介绍使用canvas实现。
小车的移动轨迹绘画
demo中使用了两层canvas,小车的轨迹使用lineTo与moveTo绘画较为简单,以下先给出轨迹坐标及小车移动轨迹的函数。
轨迹html
<div class="map">
<canvas id="canvasBG" width="1920" height="750"></canvas>
</div>
移动坐标
mapCoordinateList: [ { x: 10, y: 10 }, { x: 60, y: 40 }, { x: 645, y: 45, }, { x: 650, y: 50, }, { x: 650, y: 300 }, { x: 300, y: 350 }, { x: 250, y: 400 }, { x: 250, y: 600, }, { x: 300, y: 650 }, { x: 600, y: 650 }, { x: 600, y: 700 }, { x: 10, y: 700 }, ],
轨迹函数
handleCanvasBG() {
const myContext = document.getElementById('canvasBG')
const ctx = myContext.getContext('2d')
ctx.beginPath()
this.mapCoordinateList.forEach((item, index) => {
if(index+1 < this.mapCoordinateList.length) {
ctx.moveTo(item.x, item.y)
ctx.lineTo(this.mapCoordinateList[index+1].x, this.mapCoordinateList[index+1].y)
} else {
ctx.moveTo(item.x, item.y)
ctx.lineTo(this.mapCoordinateList[0].x, this.mapCoordinateList[0].y)
}
})
ctx.stroke()
}
小车canvas绘画
在编写小车的移动动画时,主要控制原点及画布,我使用过三种方法进行控制,但是最终选择了一下方法。
第一步:将小车的中心移动至坐标的初始位置(画布translate),保证在下一个拐点是以下一个坐标为初始位置。(使用js创建)
// 创建小车画布
handleCreateCanvas() {
this.moveLen = this.mapCoordinateList.length
const map = document.querySelector(".map")
const myCanvas = document.createElement('canvas')
myCanvas.className = 'canvas'
myCanvas.width = 1920
myCanvas.height = 750
myCanvas.style.position = 'absolute'
myCanvas.style.left = 0
myCanvas.style.top = 0
myCanvas.style.backgroundColor = 'rgba(0,0,0,0)'
myCanvas.style.Zindex = 2
map.appendChild(myCanvas)
const ctx = myCanvas.getContext('2d')
ctx.beginPath()
this.handleMove(ctx, map, myCanvas)
},
// 画布移动原点判断
handleMove(ctx) { // 小车的移动函数
if(this.mapIndex) {
const valx = this.mapCoordinateList[this.mapIndex].x - this.mapCoordinateList[this.mapIndex-1].x
const valy = this.mapCoordinateList[this.mapIndex].y - this.mapCoordinateList[this.mapIndex-1].y
const len = Math.sqrt(valx*valx+valy*valy)
ctx.translate(len, 0)
} else {
ctx.translate(this.mapCoordinateList[this.mapIndex].x, this.mapCoordinateList[this.mapIndex].y)
}
第二步:因为小车当前坐标与下一个坐标有角度,所以需要先计算角度并将车头移动至平行角度(画布rotate)。
逻辑想法:画布被旋转了,没有做恢复设置,如果恢复原始角度将会出现闪屏的现象,因此当前计算的与x轴角度需要减去上一次旋转的角度。
const valx = this.mapCoordinateList[this.mapIndex+1].x - this.mapCoordinateList[this.mapIndex].x
const valy = this.mapCoordinateList[this.mapIndex+1].y - this.mapCoordinateList[this.mapIndex].y
let laugl = this.handleGetGugle(valx, valy) // 计算当前坐标与x轴正方向的角度
let moveIndex = 0 // 当前移动了多少
const len = Math.sqrt(valx*valx+valy*valy) // 移动距离计算
const lauglLen = Math.floor((laugl - this.afterLaugl) * 100) // 需要移动的角度,角度被放大,每次动画执行0.01角度
const surplus = laugl - this.afterLaugl - lauglLen / 100
let index = 0
const lauglTime = setInterval(() => {
ctx.clearRect(-11, -6, 1920, 750)
if(index < Math.abs(lauglLen)) {
ctx.strokeRect( - 10, -5, 20, 10) // 是一个矩形仿小车
if(lauglLen > 0) {
ctx.rotate(0.01)
} else {
ctx.rotate(-0.01)
}
index++ //当前旋转了多少
} else {
ctx.rotate(surplus) // 若角度不足0.01将会在小车移动时自动旋转
计算当前坐标与x轴正方的角度
handleGetGugle(valx, valy) {
let laugl = 0
if(valx > 0) {
if(valy >= 0 ) {
laugl = Math.atan(valy / valx)
} else {
laugl = Math.atan(Math.abs(valx / valy)) + 1.5*Math.PI
}
} else if(valx < 0) {
if(valy > 0) {
laugl = Math.atan(Math.abs(valx / valy)) + 0.5*Math.PI
} else if(valy < 0) {
laugl = Math.atan(valy / valx) + Math.PI
} else {
laugl = Math.PI
}
} else {
if (valy > 0) {
laugl = Math.PI*0.5
} else {
laugl = Math.PI*1.5
}
}
return laugl
}
第三步:让小车匀速移动至下一个坐标。
const timer = setInterval(() => {
ctx.clearRect(-11, -6, 1920, 750)
ctx.strokeRect(moveIndex - 10, -5, 20, 10)
ctx.stroke()
if(moveIndex < len) {
moveIndex += 1
} else {
clearInterval(timer)
if(this.mapIndex > this.moveLen ) {
this.mapIndex = 1
this.mapCoordinateList.splice(0, this.moveLen)
}
if(this.mapIndex < this.mapCoordinateList.length - 2){
this.mapIndex++
this.handleMove(ctx)
} else {
this.mapCoordinateList.push(...this.mapCoordinateList)
this.mapIndex++
this.handleMove(ctx)
}
}
}, 10)
moveIndex++
第四步:在到达下一个坐标时,需要将原点移动至下一个坐标在当前画布的位置,循环执行以上操作。
重新执行handleMove函数。
完整代码奉上
<template>
<div class="map">
<canvas id="canvasBG" width="1920" height="750"></canvas>
</div>
</template>
<script>
export default {
name: '',
created() {
},
mounted() {
this.handleCanvasBG()
this.handleCreateCanvas()
},
data() {
return {
mapCoordinateList: [ { x: 10, y: 10 }, { x: 60, y: 40 }, { x: 645, y: 45, }, { x: 650, y: 50, }, { x: 650, y: 300 }, { x: 300, y: 350 }, { x: 250, y: 400 }, { x: 250, y: 600, }, { x: 300, y: 650 }, { x: 600, y: 650 }, { x: 600, y: 700 }, { x: 10, y: 700 }, ],
mapIndex: 0,
afterLaugl: 0,
moveLen: 0,
}
},
methods: {
handleCreateCanvas() {
this.moveLen = this.mapCoordinateList.length
const map = document.querySelector(".map")
const myCanvas = document.createElement('canvas')
myCanvas.className = 'canvas'
myCanvas.width = 1920
myCanvas.height = 750
myCanvas.style.position = 'absolute'
myCanvas.style.left = 0
myCanvas.style.top = 0
myCanvas.style.backgroundColor = 'rgba(0,0,0,0)'
myCanvas.style.Zindex = 2
map.appendChild(myCanvas)
const ctx = myCanvas.getContext('2d')
ctx.beginPath()
this.handleMove(ctx, map, myCanvas)
},
handleMove(ctx) {
if(this.mapIndex) {
const valx = this.mapCoordinateList[this.mapIndex].x - this.mapCoordinateList[this.mapIndex-1].x
const valy = this.mapCoordinateList[this.mapIndex].y - this.mapCoordinateList[this.mapIndex-1].y
const len = Math.sqrt(valx*valx+valy*valy)
ctx.translate(len, 0)
} else {
ctx.translate(this.mapCoordinateList[this.mapIndex].x, this.mapCoordinateList[this.mapIndex].y)
}
const valx = this.mapCoordinateList[this.mapIndex+1].x - this.mapCoordinateList[this.mapIndex].x
const valy = this.mapCoordinateList[this.mapIndex+1].y - this.mapCoordinateList[this.mapIndex].y
let laugl = this.handleGetGugle(valx, valy)
let moveIndex = 0
const len = Math.sqrt(valx*valx+valy*valy)
const lauglLen = Math.floor((laugl - this.afterLaugl) * 100)
const surplus = laugl - this.afterLaugl - lauglLen / 100
let index = 0
const lauglTime = setInterval(() => {
ctx.clearRect(-11, -6, 1920, 750)
if(index < Math.abs(lauglLen)) {
ctx.strokeRect( - 10, -5, 20, 10)
if(lauglLen > 0) {
ctx.rotate(0.01)
} else {
ctx.rotate(-0.01)
}
index++
} else {
ctx.rotate(surplus)
clearInterval(lauglTime)
const timer = setInterval(() => {
ctx.clearRect(-11, -6, 1920, 750)
ctx.strokeRect(moveIndex - 10, -5, 20, 10)
ctx.stroke()
if(moveIndex < len) {
moveIndex += 1
} else {
clearInterval(timer)
if(this.mapIndex > this.moveLen ) {
this.mapIndex = 1
this.mapCoordinateList.splice(0, this.moveLen)
}
if(this.mapIndex < this.mapCoordinateList.length - 2){
this.mapIndex++
this.handleMove(ctx)
} else {
this.mapCoordinateList.push(...this.mapCoordinateList)
this.mapIndex++
this.handleMove(ctx)
}
}
}, 10)
moveIndex++
}
}, 10)
this.afterLaugl = laugl
},
handleCanvasBG() {
const myContext = document.getElementById('canvasBG')
const ctx = myContext.getContext('2d')
ctx.beginPath()
this.mapCoordinateList.forEach((item, index) => {
if(index+1 < this.mapCoordinateList.length) {
ctx.moveTo(item.x, item.y)
ctx.lineTo(this.mapCoordinateList[index+1].x, this.mapCoordinateList[index+1].y)
} else {
ctx.moveTo(item.x, item.y)
ctx.lineTo(this.mapCoordinateList[0].x, this.mapCoordinateList[0].y)
}
})
ctx.stroke()
},
handleGetGugle(valx, valy) {
let laugl = 0
if(valx > 0) {
if(valy >= 0 ) {
laugl = Math.atan(valy / valx)
} else {
laugl = Math.atan(Math.abs(valx / valy)) + 1.5*Math.PI
}
} else if(valx < 0) {
if(valy > 0) {
laugl = Math.atan(Math.abs(valx / valy)) + 0.5*Math.PI
} else if(valy < 0) {
laugl = Math.atan(valy / valx) + Math.PI
} else {
laugl = Math.PI
}
} else {
if (valy > 0) {
laugl = Math.PI*0.5
} else {
laugl = Math.PI*1.5
}
}
return laugl
}
}
}
</script>
<style lang="scss" scoped>
.map{
position: relative;
.canvasBG{
z-index: 1;
}
}
</style>
有兴趣可以直接拉代码,打开就可以看见。欢迎赐教。
更多推荐
已为社区贡献2条内容
所有评论(0)