Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Canvas杂谈:第一季】我有菱角,请让我变得圆滑 #19

Closed
hongru opened this issue Nov 21, 2013 · 2 comments
Closed

【Canvas杂谈:第一季】我有菱角,请让我变得圆滑 #19

hongru opened this issue Nov 21, 2013 · 2 comments

Comments

@hongru
Copy link
Owner

hongru commented Nov 21, 2013

在游戏化的场景里,其实有很多需要折线变圆滑曲线的的需求。说到这里,相信很多同学一定第一时间想到的是贝塞尔曲线。而且在canvas底层的api里,其实也提供了绘制“二次贝塞尔曲线” 和 “三次贝塞尔曲线” 的方法。
quadraticCurveTo()bezierCurveTo()

没错,我们今天要说这两个api,但是本篇的重点却不在它们那儿。


quadraticCurveTo()

二次贝塞尔曲线,构成有3个点,一个起始点,一个控制点,一个结束点。通常使用方式:

ctx.beginPath();
ctx.moveTo(20,20);
ctx.quadraticCurveTo(20,100,200,20);
ctx.stroke();

比如上面的代码意思就是

“画笔”移动到[20, 20]的位置,以[20, 100]为控制点,[200, 20]为终点绘制一条二次贝塞尔曲线。

详细可见【w3schools的Demo】 ,我就不多说了。

bezierCurveTo

三次贝塞尔曲线,一个起始点,两个控制点,一个结束点。4个点构成。简要的使用方式如下:

ctx.beginPath();
ctx.moveTo(20,20);
ctx.bezierCurveTo(20,100,200,100,200,20);
ctx.stroke();

上面代码的大致意思是:

"画笔"移动到[20, 20]的位置,然后以[20, 100], [200, 100]这两个点为控制点,最后以[200, 20]为结束点,绘制一条3次贝塞尔曲线。

依旧详细可见【W3Schools的Demo】


开头就说过了,本篇的重点不是单纯的讲这两个api。回到我们的主题:

怎么利用canvas提供的这两个贝塞尔曲线的api,将一条随机的折线,拟合成一条平滑的曲线??

在小编说自己的解决方案之前,大家如果有空可以先想一下。在一条折线上,开始点,结束点,控制点改怎么取,才能很好的利用已知的二次和三次贝塞尔曲线的api,拟合出没有拐点,平滑的曲线??

请先拿出纸笔先大概画一画,就会发现,要想没有拐点的拟合出平滑的曲线,怎么样取点?没点讲究是做不到的。

小编这里会和大家分享自己的实现方案。有两个,一个是使用贝塞尔曲线的,一个是不使用贝塞尔曲线的方式。

使用二次贝塞尔曲线来拟合任意折线

【Demo】
bezier
如上图所示,一条顶点大于等于3个的折线,最终要拟合成一条平滑的曲线。怎么选取控制点,选用二次曲线还是三次贝塞尔曲线?这是个问题。

我相信凭借大家的聪明才智,一眼就可以看破了天机。

上面一条随机折线 ABCDEF ,最终拟合成那条红色的曲线。看图就能够知道是怎么做到的:

折线组成中的5条线段 AB, BC, CD, DE, EF, 除去最开始和最后的线段,其余的线段分别取它们的中点,得到C0, C1, C3 三个点。

  1. 以A为起始点,C0为结束点,B为控制点,利用quadraticCurveTo做一条二次曲线.
  2. 以C0为起始点,C1为结束点,C为控制点,再做一条二次曲线
  3. 以此类推,直到完成C2-E-F

当然,上面的做法是假设折线由n条线段组成,取第2条到n-1条线段的中点,然后进行二次曲线拟合。其实也可以不取中点,取线段上其他比例的点也可以,最终结果只是平滑度的问题。

比如我们取1/3 比例的点。通常为了对称,就需要多取一个点,也就是一条线段这一端的1/3取个点,另一端的1/3也取个点。这时候,我们再每3个点进行二次曲线的拟合。
3
看起来就会是这个样子,这个时候除了只是把折线的菱角磨成圆滑之外,每条线段就会有一段仍然是直的。

这种情况算不算拟合成功呢... 其实在实际的应用中,这种情况通常也是满足要求的,关键是看你的需求是什么样的。

假如不用贝塞尔曲线,该怎么做呢...

既然叫拟合,就代表是“无限趋近”的意思。那么我们按这个思路,让一条折线无限趋近于曲线,有什么做法呢?

小编这里给大家介绍一个更直接的折线插值的办法。怎么做呢? 先看这个**【Demo】**

小编依旧截个图跟大家简要说明是怎么回事,其实原理很简单,一说就明:
2013-11-22 10 20 31

比如上图折线ABCDEF, 从B点开始,距离顶点1/3 的长度位置 分别取一个点,比如B点向两边各取1/3 得到C1, C2 点。 C点向两边各1/3 得到C3, C4 点。以此类推,得到一条新的折线 A-C1-C2-C3-C4-C5-C6-C7-C8-F 。拿到这条新的折线再重复上面的做法。
重复3~4次,你就会发现,折线从视觉上看,已经慢慢平滑了起来。

我们把这个方式,叫它 Chaikin Curve - 插值曲线。它实际上不是真正的曲线,而是折线模拟。


  • 本期【Canvas杂谈】就到此为止。按照惯例,小编依旧尽量只讲思路,不贴代码,需要代码的同学可自行右键 :)
  • 本期介绍了两个api quadraticCurveTobezierCurveTo
  • 着重说明了两种折线拟合曲线的思路,一种是用二次曲线,一种是用折线插值
  • 但是这两种方式都只是在符合折线的方向趋势上来平滑过度,除了首尾两点之外,中间的点都没经过。如果我们要拟合出一条经过折线所有顶点的平滑曲线,有可能么?改怎么做? 有思路的同学给小编信哦,非常期待。
@hongru hongru closed this as completed Nov 29, 2013
@liushaoyu
Copy link

给力,不过好奇你从哪得知这些思路?自己想的?搜索的(关键字是啥,在你不知道关键字的时候怎么办,难道学习还是看运气的吗,哈哈)?还是看源码的?

@Slept-Warrior
Copy link

但是这两种方式都只是在符合折线的方向趋势上来平滑过度,除了首尾两点之外,中间的点都没经过。如果我们要拟合出一条经过折线所有顶点的平滑曲线,有可能么?改怎么做? 有思路的同学给小编信哦,非常期待。

起点终点不变,折线顶点生成一条直线垂直于角中线,组成线段,做贝塞尔曲线?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants