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
相关产品推荐
相关产品推荐

