您可以使用基数样条来平滑线,如下所示:
原因正如@Pointy 已经解释的那样,因为浏览器能够以多快的速度响应事件(mousemove
)。有一个名为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 可能工作正常(较少的结果点)。
为了使它更好地工作,您可以在绘制原始线的单独画布上注册您的点。鼠标向上时,使用此功能处理线条并将其绘制到主画布上,然后清除绘图画布。
此函数经过高度优化 - 它还返回处理后的点,因此您可以存储结果而不是每次都重新处理。
/**
* curve() by Ken Fyrstenberg (c) 2013 Epistemex
* See Code Project for full source:
* http://www.codeproject.com/Tips/562175/Draw-Smooth-Lines-on-HTML5-Canvas
*/
CanvasRenderingContext2D.prototype.curve = function(pts, ts, nos) {
nos = (typeof numOfSegments === 'undefined') ? 16 : nos;
var _pts = [], res = [], // clone array
x, y, // our x,y coords
t1x, t2x, t1y, t2y, // tension vectors
c1, c2, c3, c4, // cardinal points
st, st2, st3, st23, st32, // steps
t, i, l = pts.length,
pt1, pt2, pt3, pt4;
_pts.push(pts[0]); //copy 1. point and insert at beginning
_pts.push(pts[1]);
_pts = _pts.concat(pts);
_pts.push(pts[l - 2]); //copy last point and append
_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];
// calc tension vectors
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++) {
// pre-calc steps
st = t / nos;
st2 = st * st;
st3 = st2 * st;
st23 = st3 * 2;
st32 = st2 * 3;
// calc cardinals
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);
} //for t
} //for i
l = res.length;
for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);
return res;
} //func ext
有关基数样条的实现,请参见此答案。