beginPath 与 closePath

Canvas beginPath与closePath详解,掌握路径的开始和闭合操作。beginPath和closePath是路径操作中最基础也最重要的两个方法。理解它们的作用是正确绑制图形的前提。

beginPath 开始新路径

beginPath创建一个新的路径,并清除之前的路径数据:

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

ctx.beginPath()
ctx.moveTo(50, 50)
ctx.lineTo(150, 50)
ctx.stroke()

为什么需要beginPath

看一个常见错误:

// 错误示例
ctx.moveTo(50, 50)
ctx.lineTo(100, 100)
ctx.stroke()

ctx.lineTo(200, 50)
ctx.stroke()

结果会从(100,100)画线到(200,50),因为路径没有重置。

正确做法:

ctx.beginPath()
ctx.moveTo(50, 50)
ctx.lineTo(100, 100)
ctx.stroke()

ctx.beginPath()  // 重新开始
ctx.moveTo(100, 100)
ctx.lineTo(200, 50)
ctx.stroke()

beginPath对比演示

❌ 没有使用beginPath

✓ 正确使用beginPath

closePath 闭合路径

closePath将路径的终点连接到起点,形成封闭图形:

ctx.beginPath()
ctx.moveTo(100, 50)
ctx.lineTo(50, 150)
ctx.lineTo(150, 150)
ctx.closePath()  // 自动连接到(100, 50)
ctx.stroke()

closePath vs 手动闭合

// 方式1:手动闭合
ctx.beginPath()
ctx.moveTo(100, 50)
ctx.lineTo(50, 150)
ctx.lineTo(150, 150)
ctx.lineTo(100, 50)  // 手动回到起点
ctx.stroke()

// 方式2:使用closePath
ctx.beginPath()
ctx.moveTo(100, 50)
ctx.lineTo(50, 150)
ctx.lineTo(150, 150)
ctx.closePath()  // 自动闭合
ctx.stroke()

两种方式视觉效果相同,但closePath更简洁。

closePath效果演示

不闭合

使用closePath

填充效果

closePath与fill的关系

fill会自动闭合路径:

ctx.beginPath()
ctx.moveTo(100, 50)
ctx.lineTo(50, 150)
ctx.lineTo(150, 150)
// 没有closePath
ctx.fill()  // 自动闭合并填充

但stroke不会自动闭合:

ctx.beginPath()
ctx.moveTo(100, 50)
ctx.lineTo(50, 150)
ctx.lineTo(150, 150)
// 没有closePath
ctx.stroke()  // 不会闭合,显示开口三角形

fill自动闭合演示

stroke不自动闭合

fill自动闭合

最佳实践

// 推荐的路径绑制模式
function drawShape(ctx) {
  ctx.beginPath()
  
  // 定义路径
  ctx.moveTo(x1, y1)
  ctx.lineTo(x2, y2)
  ctx.lineTo(x3, y3)
  ctx.closePath()
  
  // 设置样式
  ctx.fillStyle = '#3498db'
  ctx.strokeStyle = '#2980b9'
  ctx.lineWidth = 2
  
  // 渲染
  ctx.fill()
  ctx.stroke()
}

注意事项

beginPath不会清除样式

ctx.strokeStyle = '#e74c3c'

ctx.beginPath()
ctx.moveTo(50, 50)
ctx.lineTo(100, 100)
ctx.stroke()

ctx.beginPath()  // 只清除路径,不清除样式
ctx.moveTo(100, 50)
ctx.lineTo(150, 100)
ctx.stroke()  // 仍然是红色

closePath不是必须的

// 绑制开放路径(如折线图)
ctx.beginPath()
ctx.moveTo(50, 50)
ctx.lineTo(100, 80)
ctx.lineTo(150, 60)
ctx.lineTo(200, 90)
ctx.stroke()  // 不需要closePath

样式不受beginPath影响

beginPath只清除路径,不清除样式

两条线使用相同的样式,因为样式在beginPath之前设置