algorithm - 沿 SVG 路径绘制
问题描述
我有一个使用贝塞尔曲线的 SVG 路径,例如:
m 776,2226 c 0,0 258.61385,-173.7593 289.34025,-325.8576 57.158,-282.9367 15.5277,-622.2212 50.8732,-933.13714 12.8345,-112.89946 104.2775,-278.6582 22.2568,-340.66923 -50.5144,-38.19103 -158.97817,99.97904 -158.97817,99.97904
在我的应用程序中,我想从初始点(x=776,y=2226)开始,慢慢绘制路径。例如,当用户按下按钮时,路径会显示更多。
我想使用 HTML 画布来做到这一点。
请注意,此路径不是封闭路径。
想过用Canvas的isPointInPath()
功能,从初始点开始,逐个绘制像素。但是,如何找到路径中的所有点?
什么是替代方法?
解决方案
就像在 SVG 中一样,Canvas2D API 具有 dash-offset 和 stroke-dasharray 选项,分别通过lineDashOffset
property 和setLineDash方法。
然而,这个 API 仍然缺乏测量路径长度的正确方法(早期有关于扩展 Path2D API 的讨论,但还没有具体的内容)。
您可以自己计算该长度,但是路径越复杂,您出错的可能性就越大……所以实际上最简单的可能是使用 SVGGeometry 元素,它确实公开了一个.getTotalLength()
方法。
const declaration = `M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z`;
const geom = document.createElementNS( "http://www.w3.org/2000/svg", "path" );
geom.setAttribute( "d", declaration );
const length = geom.getTotalLength();
const path = new Path2D( declaration );
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext( "2d" );
// [ dash - hole ]
ctx.setLineDash( [ length, length ] );
const duration = 2000;
const start = performance.now();
requestAnimationFrame( draw );
ctx.fillStyle = "green";
ctx.strokeStyle = "red";
function draw(now) {
ctx.clearRect( 0, 0, canvas.width, canvas.height );
const delta = (now % duration) / duration;
ctx.lineDashOffset = length - (length * delta);
ctx.stroke( path );
requestAnimationFrame( draw );
}
<canvas width="100" height="100"></canvas>
对于那些为了标题而来到这里并想要沿着路径移动形状的人,我们可以继续使用我们的 SVGGeometryElement 及其getPointAtLength方法:
const declaration = `M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z`;
const geom = document.createElementNS( "http://www.w3.org/2000/svg", "path" );
geom.setAttribute( "d", declaration );
const length = geom.getTotalLength();
const path = new Path2D( declaration );
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext( "2d" );
const duration = 2000;
const start = performance.now();
requestAnimationFrame( draw );
ctx.fillStyle = "green";
ctx.strokeStyle = "red";
function draw(now) {
ctx.clearRect( 0, 0, canvas.width, canvas.height );
const delta = (now % duration) / duration;
const point = geom.getPointAtLength( length * delta );
ctx.fillRect( point.x - 10, point.y -10, 20, 20 );
ctx.stroke( path );
requestAnimationFrame( draw );
}
<canvas width="100" height="100"></canvas>
现在,在您的情况下,我想您仍然需要做一些工作才能正确缩放返回的值,就像您在画布上绘制时可能缩放路径一样,但我将把它作为练习(不太复杂) .