6.2 Canvas 路径与形状
6.2 Canvas 路径与形状
Canvas路径API提供了创建复杂形状和轮廓的强大工具集。本节将深入探讨路径操作、贝塞尔曲线以及各种高级形状绘制技术,帮助您掌握专业级的Canvas绘图能力。
路径基础与高级操作
1. 路径生命周期管理
// 标准路径绘制流程
ctx.beginPath(); // 开始新路径
ctx.moveTo(50, 50); // 设置起点
ctx.lineTo(150, 50); // 添加线段
ctx.closePath(); // 闭合路径(可选)
ctx.stroke(); // 描边路径
// 重要特性说明:
// - 路径未闭合时,fill()会自动闭合路径
// - 每次beginPath()都会重置当前路径
// - 不调用beginPath()会累积路径命令
2. 复杂路径组合示例
// 绘制对话气泡
ctx.beginPath();
ctx.moveTo(75, 25);
ctx.quadraticCurveTo(25, 25, 25, 62.5);
ctx.quadraticCurveTo(25, 100, 50, 100);
ctx.lineTo(100, 125);
ctx.lineTo(150, 100);
ctx.quadraticCurveTo(175, 100, 175, 62.5);
ctx.quadraticCurveTo(175, 25, 125, 25);
ctx.closePath();
ctx.fillStyle = 'lightblue';
ctx.fill();
ctx.stroke();
贝塞尔曲线详解
1. 二次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(50, 150);
// quadraticCurveTo(cpX, cpY, endX, endY)
ctx.quadraticCurveTo(150, 50, 250, 150);
ctx.strokeStyle = 'red';
ctx.lineWidth = 3;
ctx.stroke();
// 绘制控制点(可视化)
ctx.fillStyle = 'blue';
ctx.fillRect(150-3, 50-3, 6, 6); // 控制点
2. 三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(50, 200);
// bezierCurveTo(cp1X, cp1Y, cp2X, cp2Y, endX, endY)
ctx.bezierCurveTo(100, 100, 200, 300, 250, 200);
ctx.strokeStyle = 'green';
ctx.stroke();
// 绘制控制点
ctx.fillStyle = 'purple';
ctx.fillRect(100-3, 100-3, 6, 6); // 控制点1
ctx.fillRect(200-3, 300-3, 6, 6); // 控制点2
高级形状绘制技术
1. 多边形生成函数
function drawPolygon(ctx, x, y, radius, sides, startAngle=0) {
ctx.beginPath();
for(let i = 0; i <= sides; i++) {
const angle = startAngle + i * 2 * Math.PI / sides;
ctx.lineTo(
x + radius * Math.cos(angle),
y + radius * Math.sin(angle)
);
}
ctx.closePath();
ctx.stroke();
}
// 绘制五边形
drawPolygon(ctx, 150, 150, 50, 5);
2. 圆角矩形实现
function roundedRect(ctx, x, y, width, height, radius) {
ctx.beginPath();
ctx.moveTo(x + radius, y);
ctx.lineTo(x + width - radius, y);
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
ctx.lineTo(x + width, y + height - radius);
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
ctx.lineTo(x + radius, y + height);
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
ctx.lineTo(x, y + radius);
ctx.quadraticCurveTo(x, y, x + radius, y);
ctx.closePath();
}
// 使用示例
roundedRect(ctx, 50, 50, 200, 100, 20);
ctx.fillStyle = 'rgba(200, 100, 0, 0.5)';
ctx.fill();
路径操作API进阶
1. 路径裁剪区域
// 创建星形裁剪区域
ctx.beginPath();
for(let i = 0; i < 5; i++) {
const angle = i * 2 * Math.PI / 5 - Math.PI/2;
const innerAngle = angle + Math.PI / 5;
ctx.lineTo(
150 + 50 * Math.cos(angle),
150 + 50 * Math.sin(angle)
);
ctx.lineTo(
150 + 20 * Math.cos(innerAngle),
150 + 20 * Math.sin(innerAngle)
);
}
ctx.closePath();
ctx.clip(); // 设置裁剪区域
// 在裁剪区域内绘制渐变
const gradient = ctx.createRadialGradient(150, 150, 10, 150, 150, 70);
gradient.addColorStop(0, 'gold');
gradient.addColorStop(1, 'red');
ctx.fillStyle = gradient;
ctx.fillRect(100, 100, 100, 100);
2. 路径测量与虚线设置
// 测量文本宽度
const text = 'Hello Path';
ctx.font = '30px Arial';
const metrics = ctx.measureText(text);
console.log(metrics.width); // 文本宽度
// 虚线绘制
ctx.setLineDash([10, 5]); // 10px线段,5px间隔
ctx.lineDashOffset = 0; // 虚线起始偏移
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.lineTo(250, 200);
ctx.stroke();
// 动画虚线偏移
let offset = 0;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setLineDash([10, 5]);
ctx.lineDashOffset = -offset;
ctx.beginPath();
ctx.moveTo(50, 200);
ctx.lineTo(250, 200);
ctx.stroke();
offset++;
requestAnimationFrame(animate);
}
animate();
复杂形状实战:中国结绘制
function drawChineseKnot(ctx, x, y, size) {
const unit = size / 20;
ctx.save();
ctx.translate(x, y);
// 中心十字结
ctx.beginPath();
ctx.moveTo(0, -10*unit);
ctx.bezierCurveTo(5*unit, -8*unit, 8*unit, -5*unit, 10*unit, 0);
ctx.bezierCurveTo(8*unit, 5*unit, 5*unit, 8*unit, 0, 10*unit);
ctx.bezierCurveTo(-5*unit, 8*unit, -8*unit, 5*unit, -10*unit, 0);
ctx.bezierCurveTo(-8*unit, -5*unit, -5*unit, -8*unit, 0, -10*unit);
ctx.closePath();
// 四角装饰
for(let i = 0; i < 4; i++) {
ctx.rotate(Math.PI/2);
ctx.moveTo(14*unit, -4*unit);
ctx.lineTo(18*unit, -4*unit);
ctx.quadraticCurveTo(20*unit, 0, 18*unit, 4*unit);
ctx.lineTo(14*unit, 4*unit);
}
ctx.fillStyle = 'red';
ctx.fill();
ctx.strokeStyle = 'gold';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
// 使用示例
drawChineseKnot(ctx, 150, 150, 200);
性能优化技巧
-
路径复用:
// 预定义路径 const prebuiltPath = new Path2D(); prebuiltPath.arc(100, 100, 50, 0, Math.PI*2); // 重复使用 ctx.fill(prebuiltPath); ctx.translate(200, 0); ctx.fill(prebuiltPath); -
批量绘制:
// 使用Path2D对象批量绘制 const shapes = []; for(let i = 0; i < 100; i++) { const path = new Path2D(); path.arc(Math.random()*500, Math.random()*300, 5, 0, Math.PI*2); shapes.push(path); } ctx.fillStyle = 'blue'; shapes.forEach(path => ctx.fill(path)); -
分层渲染:
<!-- 使用多个Canvas叠加 --> <canvas id="bgCanvas" class="canvas-layer"></canvas> <canvas id="mainCanvas" class="canvas-layer"></canvas> <canvas id="uiCanvas" class="canvas-layer"></canvas> <style> .canvas-layer { position: absolute; left: 0; top: 0; } </style>
调试工具与技巧
-
路径可视化调试:
function debugPath(ctx, path) { const tempCtx = document.createElement('canvas').getContext('2d'); tempCtx.stroke(path); console.log(tempCtx.canvas.toDataURL()); } -
控制点辅助显示:
function drawControlPoints(ctx, points) { ctx.save(); ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; points.forEach(p => { ctx.beginPath(); ctx.arc(p.x, p.y, 3, 0, Math.PI*2); ctx.fill(); }); ctx.restore(); }
通过本节的深入学习,您已经掌握了Canvas路径系统的核心功能。这些技术为创建复杂图形、数据可视化和交互式图形应用奠定了坚实基础。接下来我们将探索Canvas的图像处理能力,进一步扩展您的图形编程技能。
#前端开发
分享于 2025-05-20
上一篇:6.1 Canvas 绘图基础
下一篇:6.3 图像处理与像素操作