1 回答
TA贡献1851条经验 获得超3个赞
您可以使用矩阵DOMMatrix
或DOMMatrixReadOnly使用 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。
添加回答
举报