1 回答

TA貢獻1851條經驗 獲得超3個贊
您可以使用矩陣DOMMatrix
或DOMMatrixReadOnly使用 patterns 函數(shù)來定位模式setTransform
。
更新
如何查看代碼。
模式在代碼中創(chuàng)建,但可以是圖像。只需創(chuàng)建模式并傳遞給以下函數(shù)。
請注意,第二個函數(shù)是創(chuàng)建圖案的圖像。該示例使用方形模式,因此第二個參數(shù)只是大小
請注意,該模式已縮放以適應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 筆劃函數(shù),就沒有簡單的方法來解決這個問題。這會對渲染這種類型的路徑造成嚴重的性能影響。
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。
添加回答
舉報