bezierCurveTo 三次贝塞尔曲线

三次贝塞尔曲线比二次贝塞尔曲线更灵活,使用两个控制点来定义曲线形状。它可以绑制S形曲线和更复杂的曲线。

方法语法

ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
参数类型说明
cp1x, cp1ynumber第一个控制点坐标
cp2x, cp2ynumber第二个控制点坐标
x, ynumber终点坐标

曲线原理

三次贝塞尔曲线由四个点定义:

起点 ────┐
         │
      控制点1
         │
         │
      控制点2
         │
         │
      终点

曲线从起点出发,受两个控制点影响,最终到达终点。

基本用法

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

ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(150, 50, 250, 250, 350, 150)
ctx.strokeStyle = '#3498db'
ctx.lineWidth = 3
ctx.stroke()

三次贝塞尔曲线演示

三次贝塞尔曲线

起点: (50, 180)

控制点1: (120, 30)

控制点2: (330, 250)

终点: (400, 180)

控制点的影响

两个控制点共同决定曲线形状:

// S形曲线
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(150, 50, 250, 250, 350, 150)
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 3
ctx.stroke()

// 平滑曲线
ctx.beginPath()
ctx.moveTo(50, 200)
ctx.bezierCurveTo(150, 100, 250, 100, 350, 200)
ctx.strokeStyle = '#2ecc71'
ctx.lineWidth = 3
ctx.stroke()

S形曲线演示

S形曲线(三次贝塞尔曲线特色)

绘制心形

使用三次贝塞尔曲线绑制心形:

function drawHeart(ctx, x, y, size) {
  ctx.beginPath()
  ctx.moveTo(x, y + size * 0.3)
  ctx.bezierCurveTo(
    x, y - size * 0.5,
    x - size, y - size * 0.5,
    x - size, y + size * 0.1
  )
  ctx.bezierCurveTo(
    x - size, y + size * 0.8,
    x, y + size * 1.2,
    x, y + size * 1.5
  )
  ctx.bezierCurveTo(
    x, y + size * 1.2,
    x + size, y + size * 0.8,
    x + size, y + size * 0.1
  )
  ctx.bezierCurveTo(
    x + size, y - size * 0.5,
    x, y - size * 0.5,
    x, y + size * 0.3
  )
  ctx.closePath()
}

drawHeart(ctx, 200, 100, 50)
ctx.fillStyle = '#e74c3c'
ctx.fill()

心形演示

使用三次贝塞尔曲线绘制心形

绘制平滑路径

将任意点集转换为平滑曲线:

const points = [
  { x: 50, y: 200 },
  { x: 120, y: 100 },
  { x: 200, y: 180 },
  { x: 280, y: 80 },
  { x: 350, y: 150 },
  { x: 450, y: 120 }
]

function drawSmoothPath(ctx, points) {
  if (points.length < 2) return
  
  ctx.beginPath()
  ctx.moveTo(points[0].x, points[0].y)
  
  for (let i = 1; i < points.length - 1; i++) {
    const prev = points[i - 1]
    const curr = points[i]
    const next = points[i + 1]
    
    const cp1x = prev.x + (curr.x - prev.x) / 3
    const cp1y = prev.y + (curr.y - prev.y) / 3
    const cp2x = curr.x - (next.x - prev.x) / 3
    const cp2y = curr.y - (next.y - prev.y) / 3
    
    ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, curr.x, curr.y)
  }
  
  ctx.lineTo(points[points.length - 1].x, points[points.length - 1].y)
  ctx.stroke()
}

drawSmoothPath(ctx, points)
ctx.strokeStyle = '#9b59b6'
ctx.lineWidth = 3
ctx.stroke()

平滑路径演示

平滑路径曲线

二次与三次贝塞尔曲线对比

二次贝塞尔曲线

1个控制点

三次贝塞尔曲线

2个控制点

注意事项

控制点不在曲线上

// 两个控制点都不在曲线上
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(150, 50, 250, 250, 350, 150)
// 曲线不会经过控制点
ctx.stroke()

起点很重要

// 必须先moveTo
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(150, 50, 250, 250, 350, 150)
ctx.stroke()

// 如果没有moveTo,从(0,0)开始
ctx.beginPath()
ctx.bezierCurveTo(150, 50, 250, 250, 350, 150)
ctx.stroke()

控制点重合时

// 当两个控制点重合时,效果类似二次贝塞尔曲线
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(200, 50, 200, 50, 350, 150)
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 3
ctx.stroke()

连续曲线

// 多个曲线可以连续
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.bezierCurveTo(100, 50, 150, 250, 200, 150)
ctx.bezierCurveTo(250, 50, 300, 250, 350, 150)
ctx.strokeStyle = '#2ecc71'
ctx.lineWidth = 3
ctx.stroke()

连续曲线演示

连续的三次贝塞尔曲线