皈依舞
您可以使用矩阵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。