您可以使用基数样条来平滑类似这样的线条:
![enter image description here](https://istack.dev59.com/3BXms.webp)
由于浏览器能够快速响应事件(mousemove),所以原因如@Pointy已经解释过了。有一个名为
Pointer Lock API的API可能会在未来帮助解决这个问题,因为它更低级,但现在我们需要使用算法来平滑线条,以防止出现分段。
除了平滑外,还可以应用细节平滑、点减少、锥度和其他方法来改善结果。
但在这种特定情况下,您可以使用我制作的扩展到画布的以下函数。只需调用它即可:
ctx.curve(myPointArray, tension, segments);
ctx.stroke();
该数组包含您的x和y点,按如下顺序排序:
[x1, y1, x2, y2, ... xn, yn]
。
tension
的典型值为0.5。
segments
(默认为16)是可选的。
张力越大,曲线看起来就越圆滑。分段是数组中每个点之间的分辨率。对于绘图应用程序,值为5可能效果良好(产生较少的结果点)。
要使其更好地工作,您可以在单独的画布上注册您的点,在那里绘制原始线条。在鼠标抬起时使用此函数处理该行,并将其绘制到主画布上,然后清除绘图画布。
此函数高度优化 - 它还返回处理后的点,因此您可以存储结果而不是每次重新处理。
CanvasRenderingContext2D.prototype.curve = function(pts, ts, nos) {
nos = (typeof numOfSegments === 'undefined') ? 16 : nos;
var _pts = [], res = [],
x, y,
t1x, t2x, t1y, t2y,
c1, c2, c3, c4,
st, st2, st3, st23, st32,
t, i, l = pts.length,
pt1, pt2, pt3, pt4;
_pts.push(pts[0]);
_pts.push(pts[1]);
_pts = _pts.concat(pts);
_pts.push(pts[l - 2]);
_pts.push(pts[l - 1]);
this.moveTo(pts[0], pts[1])
for (i = 2; i < l; i+=2) {
pt1 = _pts[i];
pt2 = _pts[i+1];
pt3 = _pts[i+2];
pt4 = _pts[i+3];
t1x = (pt3 - _pts[i-2]) * ts;
t2x = (_pts[i+4] - pt1) * ts;
t1y = (pt4 - _pts[i-1]) * ts;
t2y = (_pts[i+5] - pt2) * ts;
for (t = 0; t <= nos; t++) {
st = t / nos;
st2 = st * st;
st3 = st2 * st;
st23 = st3 * 2;
st32 = st2 * 3;
c1 = st23 - st32 + 1;
c2 = st32 - st23;
c3 = st3 - 2 * st2 + st;
c4 = st3 - st2;
res.push(c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x);
res.push(c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y);
}
}
l = res.length;
for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);
return res;
}
请参考此答案,了解基数样条的实现。