canvas軌跡運動, 利用向量實現點勻速運動

最近做了一個人員軌跡運動的需求, 來分享下代碼. 

<!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>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章