最近做了一個人員軌跡運動的需求, 來分享下代碼.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>點軌跡運動</title>
<style>
.canvas-box {
width: 1400px;
height: 800px;
background: url("http://b-ssl.duitang.com/uploads/item/201202/14/20120214144725_HQ5mX.jpg") 0 0 no-repeat;
background-size: 100% 100%;
}
</style>
</head>
<body>
<div>
<div class="canvas-box">
<canvas id="canvas" width="1400" height="800">不支持canvas</canvas>
</div>
</div>
<script type="text/javascript">
let timer = 0
let cvs = document.getElementById('canvas')
let ctx = cvs.getContext('2d')
let points = [] //已經運動過的數據
let animatePoint = { x: 0, y: 0 } //當前運動點位置
let nextPointIndex = 1 //下一個運動點的index
//源數據
let routes = [
{ x: 12, y: 666 },
{ x: 190, y: 120 },
{ x: 354, y: 456 },
{ x: 269, y: 85 },
{ x: 456, y: 41 },
{ x: 789, y: 120 },
{ x: 876, y: 354 },
]
//開始運動
init(routes)
function init (routes) {
animatePoint = { x: 0, y: 0 }
nextPointIndex = 1
ctx.clearRect(0, 0, cvs.clientWidth, cvs.clientHeight)
if (routes.length > 0) {
points.push({
x: routes[0].x,
y: routes[0].y,
})
animatePoint = {
x: routes[0].x,
y: routes[0].y,
}
drawPoint(routes[0].x, routes[0].y)
}
if (routes.length > 1) {
this.startTimer()
}
}
function startTimer () {
startTime = new Date().getTime()
if (routes.length > 1) {
this.clearTimer()
this.animate()
}
}
function clearTimer () {
window.cancelAnimationFrame(timer)
}
function animate () {
timer = window.requestAnimationFrame(animate)
startMove()
}
function startMove () {
let targetDistance = Math.sqrt(
Math.pow(routes[nextPointIndex - 1].x - routes[nextPointIndex].x, 2) +
Math.pow(routes[nextPointIndex - 1].y - routes[nextPointIndex].y, 2),
)
let currentDistance = Math.sqrt(
Math.pow(routes[nextPointIndex - 1].x - animatePoint.x, 2) +
Math.pow(routes[nextPointIndex - 1].y - animatePoint.y, 2),
)
if (currentDistance >= targetDistance) {
//利用運動距離與目標距離, 判斷運動的點是否超過下一個目標點, 超過了就重置下一個點
startTime = new Date().getTime()
points[nextPointIndex] = {
x: routes[nextPointIndex].x,
y: routes[nextPointIndex].y,
}
animatePoint = {
x: routes[nextPointIndex].x,
y: routes[nextPointIndex].y,
}
nextPointIndex++
clearTimer()
if (nextPointIndex <= routes.length - 1) {
setTimeout(() => {
startTimer()
}, 500)
}
//重繪
ctx.clearRect(0, 0, cvs.clientWidth, cvs.clientHeight)
drawPolygon(points)
drawPoint(animatePoint.x, animatePoint.y, 'yellow')
return
}
if (nextPointIndex > routes.length - 1) {
//軌跡運動結束後, 關閉timer
clearTimer()
animatePoint = {
x: routes[routes.length - 1].x,
y: routes[routes.length - 1].y,
}
} else {
let speed = 0.25
let deltaTime = new Date().getTime() - startTime
let deltaDistance = deltaTime * speed
let rate = deltaDistance / targetDistance
let x =
routes[nextPointIndex - 1].x +
(routes[nextPointIndex].x - routes[nextPointIndex - 1].x) * rate
let y =
routes[nextPointIndex - 1].y +
(routes[nextPointIndex].y - routes[nextPointIndex - 1].y) * rate
animatePoint.x = x
animatePoint.y = y
//重繪, 將animatePoint設爲軌跡的下一個點, 以達到動態的效果
points[nextPointIndex] = {
x: animatePoint.x,
y: animatePoint.y,
}
ctx.clearRect(0, 0, cvs.clientWidth, cvs.clientHeight)
drawPolygon(points)
drawPoint(animatePoint.x, animatePoint.y, 'yellow')
}
}
function drawPoint (x, y, color) {
//繪製點
ctx.fillStyle = color || '#1DEFFF'
ctx.strokeStyle = '#fff'
if (!color) {
ctx.shadowColor = '#FFF'
ctx.shadowBlur = 10
}
ctx.beginPath()
ctx.arc(x, y, 5, Math.PI * 2, 0, true)
ctx.closePath()
ctx.stroke()
ctx.fill()
}
function drawPolygon (points) {
//繪製軌跡
ctx.clearRect(0, 0, cvs.clientWidth, cvs.clientHeight)
ctx.strokeStyle = '#1DEFFF'
ctx.shadowColor = '#1DEFFF'
ctx.shadowBlur = 10
ctx.lineWidth = 4
ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)
let i = 1,
len = points.length
for (; i < len; i++) {
ctx.lineTo(points[i].x, points[i].y)
}
ctx.stroke()
let j = 0
for (; j < len - 1; j++) {
drawPoint(points[j].x, points[j].y)
}
}
</script>
</body>
</html>