如何使用javascript HTML5 canvas通过N个点绘制平滑曲线?
对于绘图应用程序,我将鼠标移动坐标保存到数组中,然后使用 lineTo 绘制它们。生成的线条并不平滑。如何在所有收集的点之间生成一条曲线?
我用 google 搜索过,但只找到了 3 个绘制线条的函数:对于 2 个样本点,只需使用 lineTo
即可。对于 3 个采样点,quadraticCurveTo
,对于 4 个采样点,bezierCurveTo
。
(我尝试为数组中的每 4 个点绘制一个 bezierCurveTo ,但这会导致每 4 个样本点扭结,而不是连续的平滑曲线。)
如何编写一个函数来绘制平滑曲线具有 5 个或更多采样点?
For a drawing application, I'm saving the mouse movement coordinates to an array then drawing them with lineTo. The resulting line is not smooth. How can I produce a single curve between all the gathered points?
I've googled but I have only found 3 functions for drawing lines: For 2 sample points, simply use lineTo
. For 3 sample points quadraticCurveTo
, for 4 sample points, bezierCurveTo
.
(I tried drawing a bezierCurveTo
for every 4 points in the array, but this leads to kinks every 4 sample points, instead of a continuous smooth curve.)
How do I write a function to draw a smooth curve with 5 sample points and beyond?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
将后续样本点与不相交的“curveTo”类型函数连接在一起的问题是曲线相交的地方不平滑。这是因为两条曲线共享一个端点,但受到完全不相交的控制点的影响。一种解决方案是“曲线到”接下来的 2 个后续采样点之间的中点。使用这些新的插值点连接曲线可以在端点处实现平滑过渡(一次迭代的终点将成为下一次迭代的控制点。)换句话说,两条不相交的曲线有现在有更多共同点。
该解决方案摘自《Foundation ActionScript 3.0 Animation: Making things move》一书。 p.95 - 渲染技术:创建多条曲线。
注意:此解决方案实际上并没有绘制每个点,这是我的问题的标题(而是通过样本点近似曲线,但从未经过样本点),但出于我的目的(绘图应用程序),这对我来说已经足够好了,从视觉上你无法分辨出区别。有一个解决方案可以遍历所有样本点,但它要复杂得多(请参阅http://www.cartogrammar.com/blog/actionscript-curves-update/)
这是近似方法的绘图代码:
作为可运行的代码片段:
The problem with joining subsequent sample points together with disjoint "curveTo" type functions, is that where the curves meet is not smooth. This is because the two curves share an end point but are influenced by completely disjoint control points. One solution is to "curve to" the midpoints between the next 2 subsequent sample points. Joining the curves using these new interpolated points gives a smooth transition at the end points (what is an end point for one iteration becomes a control point for the next iteration.) In other words the two disjointed curves have much more in common now.
This solution was extracted out of the book "Foundation ActionScript 3.0 Animation: Making things move". p.95 - rendering techniques: creating multiple curves.
Note: this solution does not actually draw through each of the points, which was the title of my question (rather it approximates the curve through the sample points but never goes through the sample points), but for my purposes (a drawing application), it's good enough for me and visually you can't tell the difference. There is a solution to go through all the sample points, but it is much more complicated (see http://www.cartogrammar.com/blog/actionscript-curves-update/)
Here is the the drawing code for the approximation method:
As a runnable snippet:
有点晚了,但为了记录。
您可以通过使用基数样条(又名规范样条)绘制平滑曲线来实现平滑线条这贯穿了要点。
我为画布制作了这个功能 - 它分为三个功能以增加多功能性。主要包装函数如下所示:
要绘制曲线,请使用一个包含 x、y 点的数组,顺序为:
x1,y1, x2,y2, ...xn,yn
。像这样使用它:
上面的函数调用两个子函数,一个用于计算平滑点。这将返回一个包含新点的数组 - 这是计算平滑点的核心函数:
并实际将点绘制为平滑曲线(或任何其他分段线,只要您有 x,y 数组):
结果如下:
您可以轻松扩展画布,这样您就可以像这样调用它:
将以下内容添加到javascript:
您可以在 NPM (
npm i cardinal-spline-js
) 或 GitLab。A bit late, but for the record.
You can achieve smooth lines by using cardinal splines (aka canonical spline) to draw smooth curves that goes through the points.
I made this function for canvas - it's split into three function to increase versatility. The main wrapper function looks like this:
To draw a curve have an array with x, y points in the order:
x1,y1, x2,y2, ...xn,yn
.Use it like this:
The function above calls two sub-functions, one to calculate the smoothed points. This returns an array with new points - this is the core function which calculates the smoothed points:
And to actually draw the points as a smoothed curve (or any other segmented lines as long as you have an x,y array):
This results in this:
You can easily extend the canvas so you can call it like this instead:
Add the following to the javascript:
You can find a more optimized version of this on NPM (
npm i cardinal-spline-js
) or on GitLab.第一个答案不会贯穿所有要点。该图将精确地通过所有点,并且将是一条完美的曲线,其中点为 [{x:,y:}] n 个这样的点。
The first answer will not pass through all the points. This graph will exactly pass through all the points and will be a perfect curve with the points as [{x:,y:}] n such points.
我决定添加,而不是将我的解决方案发布到另一篇文章中。
以下是我构建的解决方案,可能并不完美,但到目前为止输出还不错。
重要:它将穿过所有点!
如果您有任何想法,让它变得更好,请分享给我。谢谢。
以下是前后对比:
将此代码保存到 HTML 以进行测试。
I decide to add on, rather than posting my solution to another post.
Below are the solution that I build, may not be perfect, but so far the output are good.
Important: it will pass through all the points!
If you have any idea, to make it better, please share to me. Thanks.
Here are the comparison of before after:
Save this code to HTML to test it out.
正如 Daniel Howard 指出,Rob Spencer 在 http://scaledinnovation.com/analytics/splines/aboutSplines.html。
这是一个交互式演示: http://jsbin.com/ApitIxo/2/
这里它是以防 jsbin 关闭的代码片段。
As Daniel Howard points out, Rob Spencer describes what you want at http://scaledinnovation.com/analytics/splines/aboutSplines.html.
Here's an interactive demo: http://jsbin.com/ApitIxo/2/
Here it is as a snippet in case jsbin is down.
我发现这个效果很好
I found this to work nicely
尝试一下 KineticJS - 您可以使用点数组定义样条线。这是一个示例:
旧网址:http://www.html5canvastutorials。 com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
查看存档网址:https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
Give KineticJS a try - you can define a Spline with an array of points. Here's an example:
Old url: http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
See archive url: https://web.archive.org/web/20141204030628/http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-spline-tutorial/
Bonjour
我很欣赏 user1693593 的解决方案:Hermite 多项式似乎是控制绘制内容的最佳方法,从数学的角度来看也是最令人满意的。
这个话题似乎已经被关闭了很长时间,但可能像我这样的后来者仍然对此感兴趣。
我一直在寻找一个免费的交互式绘图生成器,它可以让我存储曲线并在其他地方重复使用它,但在网络上没有找到这种东西:所以我用自己的方式从维基百科制作了它用户1693593提到的来源。
很难解释它是如何在这里工作的,了解它是否值得的最好方法是查看 https://sites.google.com/view/divertissements/accueil/splines。
Bonjour
I appreciate the solution of user1693593 : Hermite polynomials seems the best way to control what will be drawn, and the most satisfying from a mathematical point of view.
The subject seems to be closed for a long time but may be some latecomers like me are still interested in it.
I've looked for a free interactive plot builder which could allow me to store the curve and reuse it anywhere else, but didn't find this kind of thing on the web : so I made it on my own way, from the wikipedia source mentionned by user1693593.
It's difficult to explain how it works here, and the best way to know if it is worth while is to look at https://sites.google.com/view/divertissements/accueil/splines.
令人难以置信的迟到,但受到 Homan 极其简单的答案的启发,请允许我发布一个更通用的解决方案(通用是指 Homan 的解决方案在少于 3 个顶点的点数组上崩溃):
Incredibly late but inspired by Homan's brilliantly simple answer, allow me to post a more general solution (general in the sense that Homan's solution crashes on arrays of points with less than 3 vertices):
这段代码对我来说是完美的:
你有正确的平滑线和正确的端点
注意! (y = "画布高度" - y);
This code is perfect for me:
you have correct smooth line and correct endPoints
NOTICE! (y = "canvas height" - y);
与原始问题的答案略有不同;
如果有人想要绘制一个形状:
那么希望我的以下功能可以有所帮助
A slightly different answer to the original question;
If anyone is desiring to draw a shape:
Then hopefully the below function of mine could help
我不知何故需要一种仅使用二次贝塞尔曲线的方法。这是我的方法,可以扩展到 3d:
四边形贝塞尔曲线的公式是
当 t = 0 或 1 时,曲线可以通过 A 点或 C 点,但不保证通过 B 点。
其一阶导数为
构造曲线经过点 P0,P1,P2 有两条四边贝塞尔曲线,两条贝塞尔曲线在 p1 处的斜率应相等
这给出
因此,可以像这样绘制经过 3 个点的曲线
其中
m1p1 = p1m2
。m1m2
的方向无关紧要,可以通过p2 - p1
求得。对于通过 4 个或更多点的曲线
,其中
m1p1 = p1m2
和m3p2 = p2m4
。I somehow need a way that uses only quadratic bezier. This is my method and can be extended to 3d:
The formula for the quad bezier curve is
When t = 0 or 1, the curve can pass through point A or C but is not guaranteed to pass through B.
Its first-order derivative is
To construct a curve passing through points P0,P1,P2 with two quad bezier curves, the slopes of the two bezier curves at p1 should be equal
This gives
So a curve through 3 points can be drawn like this
Where
m1p1 = p1m2
. The direction ofm1m2
is not matter, can be found byp2 - p1
.For curves passing through 4 or more points
Where
m1p1 = p1m2
andm3p2 = p2m4
.为了添加到 K3N 的基数样条方法,并可能解决 TJ Crowder 对曲线“倾斜”到误导性位置的担忧,我在
getCurvePoints()
函数中插入了以下代码,就在res.push( x);
这有效地在每对连续点之间创建一个(不可见的)边界框,并确保曲线保持在该边界框内 - 即。如果曲线上的一个点位于两个点的上方/下方/左侧/右侧,则会将其位置更改为位于框内。这里使用中点,但这可以改进,也许使用线性插值。
To add to K3N's cardinal splines method and perhaps address T. J. Crowder's concerns about curves 'dipping' in misleading places, I inserted the following code in the
getCurvePoints()
function, just beforeres.push(x);
This effectively creates a (invisible) bounding box between each pair of successive points and ensures the curve stays within this bounding box - ie. if a point on the curve is above/below/left/right of both points, it alters its position to be within the box. Here the midpoint is used, but this could be improved upon, perhaps using linear interpolation.
如果您想通过 n 个点确定曲线方程,则以下代码将为您提供 n-1 次多项式的系数,并将这些系数保存到 coefficients[] 数组(从常数项)。 x 坐标不必按顺序排列。这是拉格朗日多项式的示例。
If you want to determine the equation of the curve through n points then the following code will give you the coefficients of the polynomial of degree n-1 and save these coefficients to the
coefficients[]
array (starting from the constant term). The x coordinates do not have to be in order. This is an example of a Lagrange polynomial.