画布清除与重置

Canvas动画和交互中,经常需要清除画布内容重新绑制。掌握正确的清除方法很重要。

清除画布的方法

方法1:clearRect

最常用的清除方法:

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

// 清除整个画布
ctx.clearRect(0, 0, canvas.width, canvas.height)

方法2:重设宽度

// 重设宽度会清空画布
canvas.width = canvas.width

这种方法会清空画布,但也会重置所有状态(变换、样式等)。

方法3:重设高度

canvas.height = canvas.height

效果同上。

clearRect详解

clearRect(x, y, width, height) 清除指定矩形区域:

// 清除整个画布
ctx.clearRect(0, 0, canvas.width, canvas.height)

// 清除部分区域
ctx.clearRect(100, 100, 200, 150)

// 清除特定形状(通过路径)
ctx.beginPath()
ctx.arc(200, 200, 50, 0, Math.PI * 2)
ctx.clip()  // 设置裁剪区域
ctx.clearRect(0, 0, canvas.width, canvas.height)  // 只清除圆形区域

动画中的清除

在动画循环中,每帧都需要清除画布:

function animate() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  // 更新状态
  update()
  
  // 重新绑制
  draw()
  
  requestAnimationFrame(animate)
}

优化:只清除变化区域

// 如果知道物体之前的位置,只清除那个区域
function clearObject(obj) {
  ctx.clearRect(
    obj.x - obj.size,
    obj.y - obj.size,
    obj.size * 2,
    obj.size * 2
  )
}

状态保存与恢复

清除画布不会影响绑制状态,但重设尺寸会:

// 设置样式
ctx.fillStyle = 'red'
ctx.translate(100, 100)

// clearRect 不影响状态
ctx.clearRect(0, 0, canvas.width, canvas.height)
console.log(ctx.fillStyle)  // 'red' 保持不变

// 重设尺寸会重置状态
canvas.width = canvas.width
console.log(ctx.fillStyle)  // '#000000' 恢复默认

使用save和restore

// 保存状态
ctx.save()

// 设置变换和样式
ctx.translate(100, 100)
ctx.fillStyle = 'red'

// 恢复状态
ctx.restore()

重置画布到初始状态

function resetCanvas(canvas) {
  const ctx = canvas.getContext('2d')
  
  // 重设尺寸(清空内容并重置状态)
  canvas.width = canvas.width
  
  // 或者手动重置状态
  ctx.setTransform(1, 0, 0, 1, 0, 0)  // 重置变换
  ctx.globalAlpha = 1
  ctx.globalCompositeOperation = 'source-over'
  ctx.fillStyle = '#000000'
  ctx.strokeStyle = '#000000'
  ctx.lineWidth = 1
  ctx.lineCap = 'butt'
  ctx.lineJoin = 'miter'
  ctx.lineDashOffset = 0
  ctx.shadowColor = 'rgba(0, 0, 0, 0)'
  ctx.shadowBlur = 0
  ctx.shadowOffsetX = 0
  ctx.shadowOffsetY = 0
  ctx.font = '10px sans-serif'
  ctx.textAlign = 'start'
  ctx.textBaseline = 'alphabetic'
  
  return ctx
}

清除带背景色的画布

如果画布有背景色,清除后需要重新填充:

function clearWithBackground(canvas, bgColor = '#fff') {
  const ctx = canvas.getContext('2d')
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.fillStyle = bgColor
  ctx.fillRect(0, 0, canvas.width, canvas.height)
}

实际应用:绘制移动的物体

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')

const ball = {
  x: 50,
  y: 150,
  radius: 20,
  speed: 3
}

function draw() {
  // 清除画布
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  // 绘制背景
  ctx.fillStyle = '#f0f0f0'
  ctx.fillRect(0, 0, canvas.width, canvas.height)
  
  // 更新位置
  ball.x += ball.speed
  if (ball.x > canvas.width + ball.radius) {
    ball.x = -ball.radius
  }
  
  // 绘制小球
  ctx.beginPath()
  ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2)
  ctx.fillStyle = '#3498db'
  ctx.fill()
  
  requestAnimationFrame(draw)
}

draw()

常见问题

问题1:清除后内容还在

// 错误:坐标超出范围
ctx.clearRect(0, 0, 100, 100)  // 只清除了部分

// 正确:清除整个画布
ctx.clearRect(0, 0, canvas.width, canvas.height)

问题2:清除后样式丢失

// 如果用重设尺寸的方法
canvas.width = canvas.width  // 样式被重置

// 需要重新设置样式
ctx.fillStyle = 'red'

问题3:高清屏清除问题

// 如果使用了 scale(dpr, dpr)
// 清除时需要考虑缩放

const dpr = window.devicePixelRatio || 1

// 方法1:清除时考虑缩放
ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr)

// 方法2:先恢复缩放再清除
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.scale(dpr, dpr)