You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas绘制图形顶点圆角平滑处理技术求助

Canvas图形顶点平滑解决方案

针对你用直线连接点绘制图形时出现的尖锐角问题,这里提供三种可行的修复方案,解决你之前贝塞尔曲线无效的问题:

方案1:快速实现顶点圆润(适合简单需求)

利用Canvas的lineJoin属性设置顶点圆角,先描边再填充覆盖尖角:

window.onload = function () {
  let drawingCanvas = document.getElementById("smile");
  if (drawingCanvas && drawingCanvas.getContext) {
    let cx = drawingCanvas.getContext("2d");

    const dots = [
      { id: 0, x: 0, y: 645 },
      { id: 1, x: 42, y: 686 },
      { id: 2, x: 37, y: 849 },
      { id: 3, x: 263, y: 849 },
      { id: 4, x: 299, y: 707 },
      { id: 5, x: 268, y: 404 },
      { id: 6, x: 241, y: 115 },
      { id: 7, x: 63, y: 115 },
      { id: 8, x: 42, y: 404 },
    ];

    cx.beginPath();
    cx.moveTo(dots[0].x, dots[0].y);
    dots.forEach((dot) => cx.lineTo(dot.x, dot.y));
    cx.closePath(); // 闭合路径保证图形完整
    cx.lineJoin = 'round'; // 设置顶点为圆角
    cx.lineWidth = 20; // 线宽决定圆角大小,数值越大圆角越明显
    cx.stroke(); // 先描边生成圆角
    cx.fill(); // 填充覆盖内部
  }
};

方案2:二次贝塞尔曲线拟合(精准控制平滑度)

你之前用贝塞尔没效果是因为没计算合适的控制点,这里通过相邻点的中点作为控制点,让路径平滑过渡:

window.onload = function () {
  let drawingCanvas = document.getElementById("smile");
  if (drawingCanvas && drawingCanvas.getContext) {
    let cx = drawingCanvas.getContext("2d");

    const dots = [
      { id: 0, x: 0, y: 645 },
      { id: 1, x: 42, y: 686 },
      { id: 2, x: 37, y: 849 },
      { id: 3, x: 263, y: 849 },
      { id: 4, x: 299, y: 707 },
      { id: 5, x: 268, y: 404 },
      { id: 6, x: 241, y: 115 },
      { id: 7, x: 63, y: 115 },
      { id: 8, x: 42, y: 404 },
    ];

    // 复制第一个点到末尾,保证闭合路径时平滑过渡
    const closedDots = [...dots, dots[0]];

    cx.beginPath();
    cx.moveTo(closedDots[0].x, closedDots[0].y);

    for (let i = 0; i < closedDots.length - 1; i++) {
      const current = closedDots[i];
      const next = closedDots[i + 1];
      // 计算当前点与下一个点的中点作为二次贝塞尔控制点
      const midX = (current.x + next.x) / 2;
      const midY = (current.y + next.y) / 2;

      if (i < closedDots.length - 2) {
        // 非最后一段:贝塞尔曲线连接到中点
        cx.quadraticCurveTo(current.x, current.y, midX, midY);
      } else {
        // 最后一段:连接到起点完成闭合
        cx.quadraticCurveTo(current.x, current.y, next.x, next.y);
      }
    }

    cx.fill();
  }
};

方案3:Catmull-Rom样条曲线(最自然的平滑效果)

如果需要更流畅的曲线过渡,使用Catmull-Rom样条生成自然的平滑路径:

window.onload = function () {
  let drawingCanvas = document.getElementById("smile");
  if (drawingCanvas && drawingCanvas.getContext) {
    let cx = drawingCanvas.getContext("2d");

    const dots = [
      { id: 0, x: 0, y: 645 },
      { id: 1, x: 42, y: 686 },
      { id: 2, x: 37, y: 849 },
      { id: 3, x: 263, y: 849 },
      { id: 4, x: 299, y: 707 },
      { id: 5, x: 268, y: 404 },
      { id: 6, x: 241, y: 115 },
      { id: 7, x: 63, y: 115 },
      { id: 8, x: 42, y: 404 },
    ];

    // 扩展数组:复制首尾点到两端,保证首尾平滑过渡
    const extendedDots = [dots[dots.length - 1], ...dots, dots[0]];

    cx.beginPath();
    cx.moveTo(dots[0].x, dots[0].y);

    for (let i = 1; i < extendedDots.length - 2; i++) {
      const prev = extendedDots[i - 1];
      const curr = extendedDots[i];
      const next = extendedDots[i + 1];
      const nextNext = extendedDots[i + 2];

      // 计算Catmull-Rom样条的控制点
      const cp1x = curr.x + (next.x - prev.x) / 6;
      const cp1y = curr.y + (next.y - prev.y) / 6;
      const cp2x = next.x - (nextNext.x - curr.x) / 6;
      const cp2y = next.y - (nextNext.y - curr.y) / 6;

      // 用三次贝塞尔曲线连接路径
      cx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, next.x, next.y);
    }

    cx.closePath();
    cx.fill();
  }
};

内容的提问来源于stack exchange,提问作者coremraz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:27:02