JS模拟CSS3动画-贝塞尔曲线

1、什么是贝塞尔曲线

1962年,法国工程师皮埃尔·贝塞尔(Pierre Bézier),贝塞尔曲线来为为解决汽车的主体的设计问题而发明了贝塞尔曲线。现在,贝赛尔曲线是计算机图形学中至关重要的一种曲线,它能过优雅地模拟人手绘画出的线。它经过控制曲线上的点(起始点、终止点以及多个参考点)来创造、编辑图形。其中起重要做用的是位于曲线中央的控制线。这条线是虚拟的,中间与贝塞尔曲线交叉,两端是控制端点。移动两端的端点时贝塞尔曲线改变曲线的曲率(弯曲的程度);移动中间点(也就是移动虚拟的控制线)时,贝塞尔曲线在起始点和终止点锁定的状况下作均匀移动。javascript

2、贝塞尔曲线的应用

  • 贝赛尔曲线普遍应用于绘图软件中,例如Adobe PhotoShop、Adobe Flash。css

  • Android能够经过自定义的view来实现贝塞尔曲线前端

  • ios则可使用UIBezierPath类来生成贝塞尔曲线java

  • 前端,canvas bezierCurveTo,css animation-timing-function: cubic-bezier(x,x,x,x}都有关于贝赛尔曲线的一些应用ios

3、贝塞尔曲线公式及其分析

一次:git

image

image

二次:canvas

image

image

三次:性能

image

image

n次测试

image

可是公式中只是给出了点与点之间的关系,并无给出y与x坐标的关系,为此咱们须要对其进行分解,下面以三次贝塞尔曲线为例子:动画

image

4、贝塞尔曲线在动画中的应用

image

如图,X轴用来表示时间,Y轴用来表示动画的完成度。因此贝塞尔曲线在动画中的应用很简单,每隔必定时间传入当前动画的执行时间占总时间的比例而后就能够得出动画在此时刻完成的程度,最后只须要设置一下此时刻动画的完成度就好了。因为它的起始坐标分别为(0,0),(1,1)。

image

JS代码实现

咱们已经求出了三次贝塞尔曲线x,y与t的关系,那么就能够根据这个关系经过JS实现

function UnitBezier(p1x,p1y,p2x,p2y) { this.cx = 3.0 * p1x; this.bx = 3.0 * (p2x - p1x) - this.cx; this.ax = 1.0 - this.cx -this.bx; this.cy = 3.0 * p1y; this.by = 3.0 * (p2y - p1y) - this.cy; this.ay = 1.0 - this.cy - this.by; } UnitBezier.prototype = { sampleCurveX : function(t) { return ((this.ax * t + this.bx) * t + this.cx) * t; }, sampleCurveY : function(t) { return ((this.ay * t + this.by) * t + this.cy) * t; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

可能到这里感受上,咱们已经实现了贝塞尔曲线的在动画上的应用了,咱们看下这个demo.

image

然而咱们发现原始JS实现的动画效果明显不一样于CSS3中的动画效果。那么该如何去模拟了?看下面的代码:

function UnitBezier(p1x,p1y,p2x,p2y) { this.cx = 3.0 * p1x; this.bx = 3.0 * (p2x - p1x) - this.cx; this.ax = 1.0 - this.cx -this.bx; this.cy = 3.0 * p1y; this.by = 3.0 * (p2y - p1y) - this.cy; this.ay = 1.0 - this.cy - this.by; } UnitBezier.prototype = { sampleCurveX : function(t) { //贝赛尔曲线t时刻的坐标点的X坐标 return ((this.ax * t + this.bx) * t + this.cx) * t; }, sampleCurveY : function(t) { //贝赛尔曲线t时刻的坐标点的y坐标 return ((this.ay * t + this.by) * t + this.cy) * t; }, solve:function(t){ this.sampleCurveY(this.sampleCurveX(t)) } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

咱们再来测试一下看下效果

image

额,这下差异更大。可是仔细想一想这里全部的数据基本都是浮点运算,那么产生的偏差可想而知。

5、浮点型运算偏差

在计算机中浮点型运算形成的偏差很是广泛,不论是C、C++、java、javaScript等待语言都存在,由于都是按照IEEE 754标准来进行浮点运算。

IEEE 754

按照IEEE 754 标准,32位中有:

  • 1位是符号位(sign)
  • 8位是指数位(exponent)
  • 23位是数值 (fraction)

image

那么计算出的结果为:
image

举些栗子

好比 0.1的单精度浮点数在计算机二进制数为:

0 01111011 10011001100110011001101

那么实际值为

image

好比 0.5的单精度浮点数在计算机二进制数为:

0 01111110 00000000000000000000000

6、怎样缩小浮点型运算产生的偏差

首先咱们能够来看下最简单的减小偏差的方法

放大法

因为浮点型运算会产生偏差,所以咱们能够变相地将它放大,知道它为整数,最后返回的时候在将值还原,而这种方法是为彻底将偏差消除的。


Math.formatFloat = function(f, digit) {
var m = Math.pow(10, digit);
return parseInt(f*m,10) m;
}

var numA = 0.1;
var numB = 0.2;
alert(Math.formatFloat(numA+numB,1)===0.3);

虽然这种方法在JS中可以很好地消除偏差,可是并非全部的场景均适应。它比较时候简单的加减运算和位数较低的乘法除法运算,这是由于在JS中精确整数的范围被定义为:−9007199254740992到9007199254740992(2的53次方),也就是说最多16位。然而咱们的贝塞尔曲线应用在动画的过程当中确定会产生位数超过16位的浮点数,这种方式仍然不能解决问题。

二分法

二分法是咱们在高中的时候老师就已经给咱们讲过,不过那个时候一般是用来判断f(x0)是正数仍是负数,有时候还会给出精度要求。在这里咱们一样也可使用二分法来解决,咱们只须要不断地利用二分法找出(f(x0)-x)

function division(x,t1,t2, epsilon,func) {//计算x的近似值 var t0, t1, t2, x2, i; t2 = x; if (t2 < t0) return t0; if (t2 > t1) return t1; while (t0 < t1) { x2 = func(t2); if (Math.abs(x2 - x) < epsilon) return t2; if (x > x2) t0 = t2; else t1 = t2; t2 = (t1 - t0) * .5 + t0; } return t2; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

牛顿迭代法

牛顿迭代法也是经常在计算机中减小偏差的一种经常使用方法,公式以下:

设r是f(x)=0的根,选取x0做为r的初始近似值,过点(x0,f(x0))作曲线 的切线L,L的方程为y=f(x0)+f’(x0)(x-x0),求出L与x轴交点的横坐标 x1=x0-f(x0)/f’(x0) ,称x1为r的一次近似值。过点 作曲线 的切线,并求该切线与x轴交点的横坐标 ,称 为r的二次近似值。重复以上过程,得r的近似值序列,其中,xn+1=xn-f(xn)/f’(xn)称为r的n+1次近似值。

在处理贝塞尔曲线的过程当中,因为咱们不可能求出真正准确的值,为此咱们只须要知足f(x0)-x

最终的效果

最后已经很是接近CSS3自带的贝塞尔曲线动画效果了,虽然仍有一点差异,这是由于采起的方法只能使偏差只能缩小而不能被消除,并且精度越多,那么JS动画性能就越差,因此这里精度为设定为0.01.

image

原文见这里(http://www.yuchenblog.cn/?p=102)

相关文章
相关标签/搜索