二次贝塞尔曲线是Canvas中绑制平滑曲线的重要方法。它使用一个控制点来定义曲线形状。
ctx.quadraticCurveTo(cpx, cpy, x, y)
| 参数 | 类型 | 说明 |
|---|---|---|
| cpx, cpy | number | 控制点坐标 |
| x, y | number | 终点坐标 |
二次贝塞尔曲线由三个点定义:
起点 ────┐
│
│ 控制点
│
终点
曲线从起点出发,受控制点牵引,最终到达终点。
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()