画布二次曲线到

时间:2021-03-22 13:50:25

标签: javascript html5-canvas

我发现了这段代码,它生成了一个带圆角的矩形,但我希望能够根据需要增加矩形的大小(高度和宽度)。

 var canvas = document.getElementById('newCanvas');
 var ctx = canvas.getContext('2d');
 ctx.beginPath();
 ctx.moveTo(20, 10);
 ctx.lineTo(80, 10);
 ctx.quadraticCurveTo(90, 10, 90, 20);
 ctx.lineTo(90, 80);
 ctx.quadraticCurveTo(90, 90, 80, 90);
 ctx.lineTo(20, 90);
 ctx.quadraticCurveTo(10, 90, 10, 80);
 ctx.lineTo(10, 20);
 ctx.quadraticCurveTo(10, 10, 20, 10);
 ctx.stroke();

2 个答案:

答案 0 :(得分:1)

不要忘记加入路径末端

我注意到你忘记关闭路径了。根据 ctx.lineJoin 设置,这可能会导致路径开始/结束处出现轻微的接缝或凹凸。

ctx.closePath 的调用用一行将结尾连接到开头

视觉设计

要使用的曲线类型的视觉设计规则。

  • 曲线的贝塞尔曲线是快速移动的事物的一部分
  • 圈出静止或缓慢移动的事物

贝塞尔曲线永远无法完全拟合圆。二次贝塞尔曲线非常不适合。如果您必须使用贝塞尔曲线,请使用三次贝塞尔曲线以获得更好的拟合。

使用三次贝塞尔曲线的最佳 approximation of a circle 是通过 c = 0.55191502449 作为半径的一部分插入控制点。这将导致 0.019608% 的最小可能径向误差

示例显示了三次(黑色)和二次(红色)曲线之间的区别。

const ctx = canvas.getContext('2d');
ctx.strokeStyle = 'red';
drawRoundedRectQuad(ctx, 10, 10, 180, 180, 70);
ctx.strokeStyle = 'black';
drawRoundedRect(ctx, 10, 10, 180, 180, 70);

function drawRoundedRect(ctx, x, y, w, h, r) {
    const c = 0.55191502449;
    const cP = r * (1 - c);
    const right = x + w;
    const bottom = y + h;
    ctx.beginPath();
    ctx.lineTo(right - r, y);
    ctx.bezierCurveTo(right - cP, y, right, y + cP, right, y + r);
    ctx.lineTo(right, bottom - r);
    ctx.bezierCurveTo(right, bottom - cP, right - cP, bottom, right - r, bottom);
    ctx.lineTo(x + r, bottom);
    ctx.bezierCurveTo(x + cP, bottom, x, bottom - cP, x,  bottom - r);
    ctx.lineTo(x, y + r);
    ctx.bezierCurveTo(x, y + cP , x + cP, y, x + r, y);
    ctx.closePath();
    ctx.stroke();
}
function drawRoundedRectQuad(ctx, x, y, w, h, r){
    ctx.beginPath();
    ctx.lineTo(x + w- r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h- r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h- r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
    ctx.stroke();
};
<canvas id="canvas" width ="200" height="200"></canvas>

圆角以匹配 CSS border-radius

要获得真正的圆角矩形(圆形而不是近似曲线),请使用 ctx.arc 创建圆角。

使用 roundedRect 扩展 2D API

下面的代码通过将函数 strokeRoundedRect(x, y, w, [h, [r]])fillRoundedRect(x, y, w, [h, [r]])roundedRect(x, y, w, [h, [r]]) 添加到 2D 上下文原型来绘制一个圆角矩形。

参数

x, y, w, [h, [r]]

  • x, y 圆角矩形的左上角
  • w,圆角矩形的宽度
  • h,矩形的可选高度。默认为宽度值(创建圆角正方形)
  • r 可选半径或角。默认值为 0(无圆角)。如果值为负,则使用半径 0。如果 r > 宽度或高度的一半,则 r 更改为 Math.min(w * 0.5, h * 0.5)

示例

包括圆角矩形扩展的实现。

function Extend2DRoundedRect() {
    const p90  = Math.PI * 0.5;
    const p180 = Math.PI;
    const p270 = Math.PI * 1.5;
    const p360 = Math.PI * 2;
    function roundedRect(x, y, w, h = w, r = 0) {
        const ctx = this;
        if (r < 0) { r = 0 }
        if (r === 0) {
            ctx.rect(x, y, w, h);
            return;
        }
        r = Math.min(r, w * 0.5, h * 0.5)
        ctx.moveTo(x, y + r);   
        ctx.arc(x + r    , y + r    , r, p180, p270);
        ctx.arc(x + w - r, y + r    , r, p270, p360);
        ctx.arc(x + w - r, y + h - r, r, 0   , p90);
        ctx.arc(x + r    , y + h - r, r, p90 , p180);
        ctx.closePath();
    }
    function strokeRoundedRect(...args) {
        const ctx = this;
        ctx.beginPath();
        ctx.roundedRect(...args);
        ctx.stroke();
    }
    function fillRoundedRect(...args) {
        const ctx = this;        
        ctx.beginPath();
        ctx.roundedRect(...args);
        ctx.fill();
    }
    CanvasRenderingContext2D.prototype.roundedRect = roundedRect;
    CanvasRenderingContext2D.prototype.strokeRoundedRect = strokeRoundedRect;
    CanvasRenderingContext2D.prototype.fillRoundedRect = fillRoundedRect;
}
Extend2DRoundedRect();


// Using rounded rectangle extended 2D context
const ctx = canvas.getContext('2d');
ctx.strokeStyle = "#000";
ctx.strokeRoundedRect(10.5, 10.5, 180, 180);      // no radius render rectangle
ctx.strokeRoundedRect(210.5, 10.5, 180, 180, 20); // Draw 1px line along center of pixels
ctx.strokeRoundedRect(20, 20, 160, 160, 30);  
ctx.fillRoundedRect(30, 30, 140, 140, 20);  

ctx.fillRoundedRect(230, 30, 140, 40, 20);  // Circle ends
ctx.fillRoundedRect(230, 80, 140, 20, 20);  // Auto circle ends
ctx.fillRoundedRect(280, 120, 40, 40, 120); // circle all sides

var inset = 0;
ctx.beginPath();
while (inset < 80) {
    ctx.roundedRect(
        10 + inset, 210 + inset, 
        380 - inset * 2, 180 - inset * 2, 
        50 - inset
     );
     inset += 8;
}
ctx.fill("evenodd");
<canvas id="canvas" width="400" height="400"></canvas>

答案 1 :(得分:0)

您需要将静态值转换为 (x, y) 坐标和 [width × height] 维度变量。

我采用了您所拥有的,并对公式进行了逆向工程以计算您的静态绘图。将您现有的变量更改为 xy,并在其中添加 widthheight,并在必要时添加或减去 radius。< /p>

const drawRoundedRect = (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.stroke();
};

const canvas = document.getElementById('new-canvas');
const ctx = canvas.getContext('2d');

ctx.strokeStyle = 'black';
ctx.strokeRect(10, 10, 80, 80);

ctx.strokeStyle = 'red';
drawRoundedRect(ctx, 10, 10, 80, 80, 10);

ctx.strokeStyle = 'green';
drawRoundedRect(ctx, 20, 20, 60, 60, 14);
<canvas id="new-canvas"></canvas>

相关问题