正如很多人所知,HTML5 Canvas lineTo()
会在每个角落给你一条非常锯齿状的线条。此时,更优选的解决方案是实现quadraticCurveTo()
,这是生成平滑绘图的一种非常好的方法。但是,我希望在画布HTML5上创建一个平滑但准确的画面。二次曲线方法可以很好地平滑绘图,但不会遍历所有采样点。换句话说,当我尝试使用二次曲线绘制快速曲线时,有时曲线似乎被应用程序“校正”。不是跟随我的绘制路径,而是一些段从其原始路径弯曲以遵循二次曲线。
我的应用程序用于HTML5画布上的专业绘图,因此绘图既平滑又精确非常重要。我不确定我是否通过尝试将HTML5画布放在与photoshop或任何其他画家应用程序(SAI,painterX等)相同的级别来要求不可能的事情。
由于
答案 0 :(得分:2)
你想要的是Cardinal spline,因为基数样条曲线会经过你绘制的实际点。
注意:要获得专业的结果,您还需要实现短阈值的移动平均值,同时使用基数样条线来获得更大的阈值,并使用拐点值来破坏锐角处的线条,这样就不会使整条线条平滑。我不会在这里讨论移动平均线或膝盖(也不是锥形),因为它们超出了范围,但显示了使用基数样条的方法。
侧面注释 - 应用似乎修改线条的效果是不可避免的,因为平滑发生在帖子后面。在绘制时存在平滑的算法,但它们不保留膝盖值,并且在绘制时线条似乎“摇摆”。我想这是一个偏好的问题。
这是一个小提琴,用于演示以下内容:
的 ONLINE DEMO 强>
首先是一些先决条件(我正在使用我的easyCanvas库来设置演示中的环境,因为它为我节省了大量工作,但这不是此解决方案工作的必要条件):
当您按照X / Y(即[x1, y1, x2, y2, ... xn, yn]
)得到数组顺序中的点数时,您可以使用此函数来平滑它:
张力值(ts
,默认为0.5)是曲线的平滑。数字越大,曲线越圆。你可以超出正常的间隔[0,1]来制作卷发。
段(nos
或段数)是每个点之间的分辨率。在大多数情况下,您可能不需要高于9-10。但是在速度较慢的计算机上或者你需要快速绘制更高值的地方。
功能(优化):
/// cardinal spline by Ken Fyrstenberg, CC-attribute
function smoothCurve(pts, ts, nos) {
// use input value if provided, or use a default value
ts = (typeof ts === 'undefined') ? 0.5 : ts;
nos = (typeof nos === '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, r = 0,
len = 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[len - 2]); //copy last point and append
_pts.push(pts[len - 1]);
for (i = 2; i < len; 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++) {
// 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
return res;
}
然后只需在存储积分后从mouseup
事件中调用它:
stroke = smoothCurve(stroke, 0.5, 16);
strokes.push(stroke);
对膝盖值的简短评论:
此上下文中的拐点值是线中点(作为线段的一部分)之间的角度大于某个阈值(通常在45-60度之间)的位置。当膝盖出现时,线条被分成一条新线,这样只使用由角度小于它们之间的角度的点组成的线(由于不使用膝盖,你会看到演示中的小卷发)。 p>
对移动平均线的简短评论:
Moving average通常用于统计目的,但对绘制应用程序也非常有用。当你有一个由许多点组成的簇,它们之间的距离很短时,样条曲线不能很好地工作。所以在这里你可以使用MA来平滑点。
还可以使用点减少算法,例如Ramer/Douglas/Peucker,但它更多地用于存储以减少数据量。