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);

性能优化技巧

  1. 路径复用

    // 预定义路径
    const prebuiltPath = new Path2D();
    prebuiltPath.arc(100, 100, 50, 0, Math.PI*2);
    
    // 重复使用
    ctx.fill(prebuiltPath);
    ctx.translate(200, 0);
    ctx.fill(prebuiltPath);
    
  2. 批量绘制

    // 使用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));
    
  3. 分层渲染

    <!-- 使用多个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>
    

调试工具与技巧

  1. 路径可视化调试

    function debugPath(ctx, path) {
      const tempCtx = document.createElement('canvas').getContext('2d');
      tempCtx.stroke(path);
      console.log(tempCtx.canvas.toDataURL());
    }
    
  2. 控制点辅助显示

    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

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】