quadraticCurveTo 二次贝塞尔曲线

二次贝塞尔曲线是Canvas中绑制平滑曲线的重要方法。它使用一个控制点来定义曲线形状。

方法语法

ctx.quadraticCurveTo(cpx, cpy, x, y)
参数类型说明
cpx, cpynumber控制点坐标
x, ynumber终点坐标

曲线原理

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

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

曲线从起点出发,受控制点牵引,最终到达终点。

基本用法

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

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

二次贝塞尔曲线演示

二次贝塞尔曲线

起点: (50, 180)

控制点: (225, 30)

终点: (400, 180)

控制点的影响

控制点位置决定曲线形状:

// 向上弯曲
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.quadraticCurveTo(200, 50, 350, 150)
ctx.stroke()

// 向下弯曲
ctx.beginPath()
ctx.moveTo(50, 200)
ctx.quadraticCurveTo(200, 300, 350, 200)
ctx.stroke()

// S形(需要多个曲线)
ctx.beginPath()
ctx.moveTo(50, 250)
ctx.quadraticCurveTo(150, 200, 200, 250)
ctx.quadraticCurveTo(250, 300, 350, 250)
ctx.stroke()

控制点影响演示

不同控制点位置的曲线

绘制波浪线

使用多个二次贝塞尔曲线创建波浪:

function drawWave(ctx, x, y, width, height, cycles) {
  const cycleWidth = width / cycles
  
  ctx.beginPath()
  ctx.moveTo(x, y + height / 2)
  
  for (let i = 0; i < cycles; i++) {
    const startX = x + i * cycleWidth
    const controlX = startX + cycleWidth / 2
    const controlY = y
    const endX = startX + cycleWidth
    const endY = y + height / 2
    
    ctx.quadraticCurveTo(controlX, controlY, endX, endY)
  }
  
  ctx.stroke()
}

drawWave(ctx, 50, 100, 400, 100, 4)

波浪线演示

波浪线

绘制平滑折线

将折线转换为平滑曲线:

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

ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)

for (let i = 1; i < points.length; i++) {
  const prev = points[i - 1]
  const curr = points[i]
  const midX = (prev.x + curr.x) / 2
  const midY = (prev.y + curr.y) / 2
  
  ctx.quadraticCurveTo(prev.x, prev.y, midX, midY)
}

ctx.lineTo(points[points.length - 1].x, points[points.length - 1].y)
ctx.strokeStyle = '#9b59b6'
ctx.lineWidth = 3
ctx.stroke()

平滑折线演示

原始折线

平滑曲线

绘制抛物线

二次贝塞尔曲线可以绑制抛物线:

// 向上抛物线
ctx.beginPath()
ctx.moveTo(50, 250)
ctx.quadraticCurveTo(200, 50, 350, 250)
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 3
ctx.stroke()

// 向下抛物线
ctx.beginPath()
ctx.moveTo(50, 50)
ctx.quadraticCurveTo(200, 250, 350, 50)
ctx.strokeStyle = '#2ecc71'
ctx.lineWidth = 3
ctx.stroke()

抛物线演示

抛物线

注意事项

控制点不在曲线上

// 控制点只是牵引点,曲线不会经过它
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.quadraticCurveTo(200, 50, 350, 150)
// 曲线不会经过(200, 50)
ctx.stroke()

起点很重要

// 必须先moveTo
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.quadraticCurveTo(200, 50, 350, 150)
ctx.stroke()

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

连续曲线

// 多个曲线可以连续
ctx.beginPath()
ctx.moveTo(50, 150)
ctx.quadraticCurveTo(150, 50, 250, 150)
ctx.quadraticCurveTo(350, 250, 450, 150)
ctx.stroke()

连续曲线演示

连续的二次贝塞尔曲线