为了账号安全,请及时绑定邮箱和手机立即绑定

将用作画布中的strokeStyle的Image定位定向

将用作画布中的strokeStyle的Image定位定向

料青山看我应如是 2022-10-27 16:04:16
我正在尝试将图像用作笔触样式,但在如何指导图案(箭头图像)的放置方式方面存在问题。例如,我使用箭头作为 strokeStyle 的图案。我希望箭头指向顶部矩形,向下指向右侧,向后指向底部,然后向上指向矩形的右侧。更像图像应该遵循矩形的形状使用正常笔划只需将箭头放置在直线图案中,如图所示。 在此处输入图像描述这是我的代码  function drawPattern(img, size) {  const canvas = document.getElementById("canvas");  const tempCanvas = document.createElement("canvas");  const tCtx = tempCanvas.getContext("2d");  tempCanvas.width = size;  tempCanvas.height = size;  tCtx.drawImage(img, 0, 0, img.width, img.height, 0, 0, size, size);  const ctx = canvas.getContext("2d");  ctx.clearRect(0, 0, canvas.width, canvas.height);  const pat = ctx.createPattern(tempCanvas, "repeat");  ctx.strokeStyle = pat;  ctx.lineWidth = 100;  ctx.strokeRect(0, 0, canvas.width, canvas.height);}const img = new Image();img.src = "http://freundbild.com/arrow.png";img.onload = function () {  drawPattern(this, 100);};
查看完整描述

1 回答

?
皈依舞

TA贡献1851条经验 获得超3个赞

您可以使用矩阵DOMMatrixDOMMatrixReadOnly使用 patterns 函数来定位模式setTransform

更新

如何查看代码。

模式在代码中创建,但可以是图像。只需创建模式并传递给以下函数。

请注意,第二个函数是创建图案的图像。该示例使用方形模式,因此第二个参数只是大小

请注意,该模式已缩放以适应lineWidth因此不会保持其外观。

function orientPattern(ctx, image, pattern, dist, lineWidth, p1, p2) {

    const dx = p2.x - p1.x;

    const dy = p2.y - p1.y;

    const d = (dx * dx + dy * dy) ** 0.5;

    const nx = dx / d;

    const ny = dy / d;

    const w = image.width;

    const h = image.height;

    const yScale = h / lineWidth;

    const mat = new DOMMatrixReadOnly([

        nx, ny, 

        -ny / yScale, nx / yScale,

        p1.x - ((ny * h * 0.5) % h) / yScale - (nx * (dist % w)) , 

        p1.y + ((nx * h * 0.5) % h) / yScale - (ny * (dist % w))

    ]);

    pattern.setTransform(mat);

    return pattern;

}

注意您需要一次绘制要绘制的形状的每个线段。您不能将其拟合到矩形弧或不是直线的路径。


注意线连接将有孔或覆盖(取决于线帽设置) 如果不编写完全替换 2D 笔划函数,就没有简单的方法来解决这个问题。这会对渲染这种类型的路径造成严重的性能影响。


const ctx = canvas.getContext("2d");


function createArrowPattern(size, bgCol, col) {

    const c = document.createElement("canvas");

    c.width = c.height = size;

    const ctx = c.getContext("2d");

    ctx.fillStyle = bgCol;

    ctx.fillRect(0,0, size, size);

    ctx.fillStyle = col;

    const u = size / 5;

    ctx.setTransform(size, 0, 0, size, size / 2, size / 2);

    ctx.beginPath();

    ctx.lineTo(-0.4, -0.2);

    ctx.lineTo( 0.1, -0.2);

    ctx.lineTo( 0.1, -0.5);

    ctx.lineTo( 0.4,  0);

    ctx.lineTo( 0.1,  0.5);

    ctx.lineTo( 0.1,  0.2);

    ctx.lineTo(-0.4,  0.2);

    ctx.fill();

    return ctx.createPattern(c, "repeat");

}

    

function orientPattern(ctx, size, pattern, dist, lineWidth, p1, p2) {

    const dx = p2.x - p1.x;

    const dy = p2.y - p1.y;

    const d = (dx * dx + dy * dy) ** 0.5;

    const nx = dx / d;

    const ny = dy / d;

    const yScale = size / lineWidth;

    const mat = new DOMMatrixReadOnly([

        nx, ny, 

        -ny / yScale, nx / yScale,

        p1.x - ((ny * size * 0.5) % size) / yScale - (nx * (dist % size)) , 

        p1.y + ((nx * size * 0.5) % size) / yScale - (ny * (dist % size))

    ]);

    pattern.setTransform(mat);

    return pattern;

}



function drawPatternPath(ctx, size, pattern, lineWidth, start, ...points) {

    var i = 0;

    ctx.lineWidth = lineWidth;

    ctx.lineCap = "round";

    var dist = 0;

    var p1 = points[i++]

    while (i < points.length) {

        const p2 = points[i++];

        ctx.strokeStyle = orientPattern(ctx, size, pattern, -start + dist, lineWidth, p1, p2);

        ctx.beginPath();

        ctx.lineTo(p1.x, p1.y);

        ctx.lineTo(p2.x, p2.y);

        ctx.stroke();

       // dist += 10

        dist += ((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2) ** 0.5;

        p1 = p2;

    }

    


}

const P = (x, y) => ({x,y});

const ARROW_SIZE = 64;

const LINE_WIDTH = 22;

const arrow = createArrowPattern(ARROW_SIZE, "white", "red");

var pos = 0;

animate() 

function animate() {

    ctx.clearRect(0,0,400,300);

    drawPatternPath(

        ctx, ARROW_SIZE, arrow, LINE_WIDTH, pos, 

        P(30,30), 

        P(370,30), 

        P(370, 200),

        P(350, 250),

        P(300, 270),

        P(30,270),

        P(30,30)

    );

    pos += 1;

    requestAnimationFrame(animate);

}

<canvas id="canvas" width ="400" height="300"></canvas>


我认为这种方法是一种 hack,并且需要将图像、图案、渐变和文本映射到 2D API 中的笔画(作为路径),因为目前在质量和性能方面都没有可接受的解决方法。


另一种选择是 WebGL,它可以轻松地以极快的速度和卓越的质量绘制此类图案化路径,但是将其与 2D API 集成并非易事,一旦沿着这条路径,为什么还要使用 2D API。


查看完整回答
反对 回复 2022-10-27
  • 1 回答
  • 0 关注
  • 79 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信