You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HTML Canvas中计算动画方向 实现矩形沿直线路径移动到点击位置

问题根源
  • 你当前设置的deltaX和deltaY固定为±1,x和y方向每帧移动步长完全相同,当水平位移差和垂直位移差不一致时,会先走完较短的边,再单独走较长的边,最终形成折线轨迹,无法沿直线移动
  • 原有动画逻辑还存在两个附加问题:没有每帧清空画布会产生拖影,cancelAnimationFrame未传入动画id无法正确终止动画
修正思路
  1. 每次触发点击时,先计算当前矩形位置到目标点的水平差dx、垂直差dy,以及两点的直线距离
  2. 按位移比例分配x、y方向的每帧步长:deltaX = 速度 * dx / 总距离,deltaY = 速度 * dy / 总距离,保证两个方向同时到达目标点
  3. 每帧绘制前清空画布,同时增加精度判断,避免浮点数误差导致无法完全匹配目标坐标的问题
  4. 正确管理动画帧id,到达目标后终止动画
修正后的完整代码

HTML部分

<canvas id="canvas"></canvas>

JS部分

'use strict'

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const ratio = Math.ceil(window.devicePixelRatio)
let height = window.innerHeight
let width = window.innerWidth
// 动画帧id,用于终止动画
let animationId = null
// 移动速度,可自行调整数值控制快慢
const MOVE_SPEED = 3

canvas.height = height * ratio
canvas.width = width * ratio
canvas.style.height = `${height}px`
canvas.style.width = `${width}px`
ctx.setTransform(ratio, 0, 0, ratio, 0, 0)

let position = {
  x: 0,
  y: 0,
  deltaX: 0,
  deltaY: 0,
  size: 20
}

let move = {
  x: 0,
  y: 0,
}

function animate() {
  // 计算当前位置到目标的剩余距离
  const dx = move.x - position.x
  const dy = move.y - position.y
  const distance = Math.sqrt(dx * dx + dy * dy)
  
  // 剩余距离小于步长时直接定位到目标点,终止动画
  if (distance < MOVE_SPEED) {
    position.x = move.x
    position.y = move.y
    cancelAnimationFrame(animationId)
    animationId = null
    // 绘制最终位置的矩形
    ctx.clearRect(0, 0, width, height)
    ctx.fillRect(position.x, position.y, position.size, position.size)
    return
  }

  // 清空画布,绘制当前帧矩形
  ctx.clearRect(0, 0, width, height)
  position.x += position.deltaX
  position.y += position.deltaY
  ctx.fillRect(position.x, position.y, position.size, position.size)

  animationId = requestAnimationFrame(animate)
}

function moveTo(x, y) {
  // 如果有正在运行的动画,先终止
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
  move.x = x
  move.y = y
  const dx = move.x - position.x
  const dy = move.y - position.y
  const distance = Math.sqrt(dx * dx + dy * dy)
  // 按比例分配步长
  position.deltaX = MOVE_SPEED * dx / distance
  position.deltaY = MOVE_SPEED * dy / distance
  animate()
}

canvas.addEventListener('click', (event) => {
  moveTo(event.clientX, event.clientY)
})

// 初始绘制矩形
ctx.fillRect(position.x, position.y, position.size, position.size)

内容的提问来源于stack exchange,提问作者Mr. lindroid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 03:36:09