关于贝塞尔曲线在前端路径动画与动画函数上的高级运用

看这篇文章之前,请确保你有基本的前端知识,知道Canvas最基本的使用方法,知道贝塞尔曲线在CSS3中的使用。本文章忽略了贝塞尔曲线的历史,不了解可自行谷歌百度。

三次贝塞尔曲线方程

推导过程

先看一下贝塞尔曲线的生成方法

二次贝塞尔曲线

Quadratic Bezier Curve
Quadratic Bezier Curve

三次贝塞尔曲线

Cubic Bezier Curve
Cubic Bezier Curve

四次贝塞尔曲线

Cubic Bezier Curve
Cubic Bezier Curve

这是二次贝塞尔曲线、三次贝塞尔曲线、以及四次贝塞尔曲线的生成示意图,我们可以用平实的文字总结一下这些图形表达的含义:对于给定的曲线起点与终点以及一系列控制点,分别将起点与第一控制点,第一控制点与第二控制点,以此类推直至最后一个控制点与终点相连,然后分别将这些线段对应的相同比例部分的点相连,记此比例为t,再将这些二次连线上t位置的点相连,依次类推直到不存在第二条线段,最后这条线段t位置上的点就在贝塞尔曲线上,而当t∈[0,1]时这些点的集合就是贝塞尔曲线本身。

三次贝塞尔曲线就是如此计算的:

  Q0:P0 + (P1-P0)t
  Q1:P1 + (P2-P1)t
  Q2:P2 + (P3-P2)t
  A0:Q0 + (Q1-Q0)t
  A1:Q1 + (Q2-Q1)t
  B(t) = A0 + (A1-A0)t

将所有点换成使用P0P1P2P3的表达式进行化简,即可化简为

B(t) = P0(1-t)³ + 3P1t(1-t)² + 3P2t²(1-t) + P3

化简过程涉及到换元,目的是抽象贝塞尔曲线的通用表达式,所以结果不是那么直观,不过这些都是数学细节,感兴趣可以去维基百科,平常接触最多的就是三次贝塞尔曲线,所以我们这里着重探讨的就是特殊的三次贝塞尔曲线而非普遍结论。

这一表达式中的变量t就是我们说的“各条线段上的比例”,在这里我们直接使用了一个字面量(如P0)来表示一个点,实际上编程时我们会将点分成xy坐标分别进行计算。

贝塞尔曲线动态生成截图

这里我将贝塞尔曲线的生成方法实现成了一个使用canvas绘制的过程例子,你可以直接拖动滑块改变描线速度。

运动位置计算

给定t拿到运动位置并非难事,因为我们已经有了曲线对应的函数表达式,直接代入参数t即可求得x,y坐标。

比如目前我们希望让物体位于某已知贝塞尔曲线的t位置,则我们需要的位置为

var x = P0x(1-t)³ + 3P1xt(1-t)² + 3P2xt²(1-t) + P3xt³;
var y = P0y(1-t)³ + 3P1yt(1-t)² + 3P2yt²(1-t) + P3yt³;

// position handler , e.g. 
// oBox.style.transform = `translate(${x}px,${y}px)`;

沿贝塞尔曲线运动截图

这里是Canvas实现的沿着曲线路径运动的圆

当然,这里我们要注意的是:我们并没有在时间维度控制动画,或者说目前这个沿曲线运动的圆目前能做到的也仅仅是“沿着曲线运动”,我们目前还没有控制它的匀速、加速等速度模式。

Canvas任意曲线描线动画

想要实现canvas描线动画,首先我们应该知道canvas动画的实现方式:不断的绘制与擦除。既然我们需要的是一个描线动画,那么我们就可以将研究目标转向另一个问题:如何绘制从起始点开始到达三次贝塞尔曲线上给定一点的曲线线段?

贝塞尔曲线的截取方法

实际上,我们已经知道了如何绘制三次贝塞尔曲线:不断运动的点所构成的线,那我们已经知道了t的位置,那实际上就是已经知道了绘制到给定点时对应的Q0,Q1,A0,而Q0A0就是这段贝塞尔曲线的控制点,而B2便是终点值。

上文中演示贝塞尔曲线的生成就使用了这一方法,点击查看例子源码

CSS3属性的转化

想知道如何将CSS3中的属性值转化为JS中的描述逻辑,首先应该搞清楚CSS3中的贝塞尔曲线表达式到底是什么。

  1. 属性值的含义

我们看到的CSS3中无论是 transition-timing-function 还是 animation-timing-function都是可以支持三次贝塞尔曲线表达式的,形如cubic-bezier(0.2,0.3,0.71,0.43),括号内有四个值,我们之前了解到的三次贝塞尔曲线相关的点需要四个:起点,第一控制点,第二控制点,终点,而我们的CSS3运动函数的三次贝塞尔曲线表达式把起点与终点分别约定为(0,0)与(1,1),所以我们这个括号里传的四个参数分别是第一、二控制点的X坐标、y坐标,即为

cubic-bezier( P1x, P1y, P2x, P2y)

那既然如此我们就清楚了:我们三次函数表达式中需要的数据都齐全了,下面仅需将CSS表达式转化成JS表达式就好,我们的方法接受CSS3贝塞尔曲线表达式,返回值为接受x为参数返回值为对应y值的方法。

点击查看CSS属性转化
本例子实现了以下效果:将CSS3的属性转化成对应的三次贝塞尔曲线。那现在有一个问题:我们如何使用这个贝塞尔曲线作为我们所做动画的时间变化函数?

时间比例函数

实际上,我们在这里有三个变量:t,x,y,根据t与四个已知点我们可以获得对应点的xy值,但是这个等式对我们使用它作为时间函数并没有任何作用,我们需要的是:将x作为自变量匀速递增,这时的x的物理意义便是运动时间,而x值在贝塞尔曲线上对应的点的y坐标的意义便是物体运动的百分比。那么我们目前面临的问题便是:给定一个x,如何在某个特定的贝塞尔曲线上计算出对应的y值?

贝塞尔曲线控制器

实际上我们是没有办法直接算出y的,根据我们已知的函数方程

x = P0x(1-t)³ + 3P1xt(1-t)² + 3P2xt²(1-t) + P3xt³ (1)

y = P0y(1-t)³ + 3P1yt(1-t)² + 3P2yt²(1-t) + P3yt³ (2)

在这两个函数表达式中我们已知的变量只有x,所以我们只能通过(1)式反求出此时的比例t,然后再代入(2)式获取y值。
因为P0P1P2P3都是已知量,将(1)式简化后是一个t相关的一元三次函数:

at³ + bt² + ct + d = 0 // 此多项式为由上式合并同类项后得出,参数为合并后代数值

也就是说我们目前已知a,b,c,d,求出t值,并且我们知道t值的取值范围是t∈[0,1],所以解三次方程就可以得出了。

但是在实践中有个问题:解三次函数会涉及到大量的开方相除操作,并没有现成的解三次方函数。这里需要一些计算机求值方面的技巧:我们可以通过二分法去逼近这个结果。因为我们的t值是[0,1]区间的,所以我们可以尝试二分解决,而二分法的精度是1/2^n,当n=10时,这个精度最大值便已经是1/1024≈0.001了,这对我们来说已经比较精确,但是还有方法可以将这个精度提升:

  1. 提高n的次数:n=20时这个精度值便为0.000001了,对于我们进行求值计算已经十分精确。
  2. 先控制区间,然后再在区间内进行二分逼近,比如先将t分成十份,然后将t对应的x值与目标值进行逐个比较,确定区间后进行二分操作,都可以。

除了二分法外,进行曲线上求值还有一个更为高效的方法,叫做牛顿迭代法,是根据曲线上某点坐标及其导数进行区间推导的过程,理解起来复杂程度较高,但是在适用条件内逼近精度平方级递增,效率奇高。想要更多了解相关知识的同学可以看一下我列的参考资料,或者还是求助万能的维基百科,我就不多做介绍了。

那没有具体介绍算法,咋写?这个比较好说,少年我送你一个,这个库是我fork之后在主体文件上增加了注解。数学理论和计算机实践一个很大的不同就是:数学严谨且精确,而计算机计算则需要我们做更多权衡:精确度和性能,这个库很棒,能帮助我们理解算法同时也给出了生产解决方案,性能不俗。

那我们下一步就可以尝试使用这个解决方案来进行时间动画控制

制作自己的时间函数控制器

首先我们引入,这个库导出的方法是bezier-curve,这个方法的调用返回值是一个函数,这个函数接受x作为参数,返回对应的y值,这个例子展示了这个功能,而以x为时间变量,y值的变化就是对应的运动函数,在这里我们使用t作为时间变化变量,tSpeed表示t变化快慢,然后通过requestAnimationFrame来不断进行渲染。

配和运动函数的动态运动截图

目前我们已经实现了一个小的三次贝塞尔曲线运动函数控制器,下一步我们需要解决的是什么呢?本篇文章未解决的问题,将有另一篇文章给出:

  1. 如何使用更为复杂的贝塞尔曲线来描述一个多节动画函数?
  2. 如何控制沿一般曲线运动物体的速度?
  3. 不借助SVG动画的帮助,我们如何实现物体沿一般复杂曲线运动并使运动物体自动旋转?
参考资料
  1. http://stackoverflow.com/questions/4089443/find-the-tangent-of-a-point-on-a-cubic-bezier-curve-on-an-iphone

  2. https://pomax.github.io/bezierinfo/

  3. https://www.geometrictools.com/Documentation/MovingAlongCurveSpecifiedSpeed.pdf

  4. https://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html

  5. https://medium.com/@ramshandilya/draw-smooth-curves-through-a-set-of-points-in-ios-34f6d73c8f9#.w7sd3hiyc

  6. http://zqdevres.qiniucdn.com/data/20110728232822/index.html

  7. http://bl.ocks.org/hnakamur/e7efd0602bfc15f66fc5

  8. http://math.stackexchange.com/questions/12186/arc-length-of-b%C3%A9zier-curves

  9. http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/

  10. http://stackoverflow.com/questions/29438398/cheap-way-of-calculating-cubic-bezier-length

  11. https://github.com/gre/bezier-easing/blob/master/src/index.js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 1.曲线介绍 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线...
    秦明Qinmin阅读 7,450评论 2 19
  • 背景: 给一系列顶点,如果只是用直线将其中的各个点依次连接起来,最终形成一个折线图,这种很容易实现。但是现实...
    狂风无迹阅读 39,010评论 12 70
  • 一、前言 本系列文章通过介绍 贝塞尔曲线 的基础知识,贝塞尔曲线在iOS中的应用以及一些高级技巧,循序渐进,试图让...
    我是王海龙阅读 4,238评论 4 19
  • 贝塞尔曲线开发的艺术 一句话概括贝塞尔曲线:将任意一条曲线转化为精确的数学公式。 很多绘图工具中的钢笔工具,就是典...
    eclipse_xu阅读 27,677评论 38 370
  • 让我再看你一遍从头到尾, 像是被时间蒙住了我的双眼。 请你再讲一遍,关于那天, 撑着阳伞的姑娘和擦汗的男人。 我知...
    鱼耗子阅读 328评论 0 0