贝塞尔曲线(Bezier Curve)在计算机图形领域应用很是普遍,好比咱们熟知的 CSS 动画、 Canvas 以及 Photoshop 等均可以看到贝塞尔曲线的身影。javascript
贝塞尔曲线于 1962 年,由法国工程师皮埃尔·贝济埃(Pierre Bézier)所普遍发表,他运用贝塞尔曲线来为汽车的主体进行设计。java
贝塞尔曲线主要用于二维图形应用程序中的数学曲线,曲线由起始点,终止点(也称锚点)和控制点组成,经过调整控制点,经过必定方式绘制的贝塞尔曲线形状会发生变化。后面会具体介绍绘制的方法。git
在计算机图形学中贝赛尔曲线的运用很普遍,例如Photoshop中的钢笔效果,Flash5的贝塞尔曲线工具,在软件GUI开发中通常也会提供对应的方法来实现贝赛尔曲线,咱们熟知的CSS动画过渡时间函数也是经过贝塞尔曲线(三阶贝塞尔曲线)获取的。github
贝塞尔曲线根据控制点的数量分为:segmentfault
下图为一个三阶的贝塞尔曲线,包括四个控制点,分别为。数组
那咱们经过控制点是怎么绘制出贝塞尔曲线的呢?缓存
经过上图的三阶贝塞尔曲线举例,基本的步骤以下:函数
经过控制的值,由 0 增长至 1,就绘制出了一条由起点
至终点
的贝塞尔曲线。工具
你能够经过下面这个动画直观感觉一下绘制的过程:动画
对于一阶贝塞尔曲线,咱们能够经过几何知识,很容易根据的值得出线段上那个点的坐标:
而后能够得出:
对于二阶贝塞尔曲线,其实你能够理解为:在上利用一阶公式求出点
,而后在
上利用一阶公式求出点
,最后在
上再利用一阶公式就能够求出最终贝塞尔曲线上的点
。具体推导过程以下:
先求出线段上的控制点。
将上面的公式带入至下列公式中:
得出如下公式:
与二阶贝塞尔曲线相似,能够经过相同的方法得出如下坐标公式:
这里我就直接把阶贝塞尔曲线公式给出来了,有兴趣的同窗能够自行研究一下。
即:
公式中
的值为
,与统计学有关,有兴趣的同窗能够看一看个人这篇文章。
其中的值为:
若是要实现一个这样的三阶贝塞尔曲线,咱们须要不只须要获取到一些曲线上的点,还须要经过x轴获取y轴坐标。
CSS中的easing贝塞尔曲线有一个特色,那就是起点和终点是固定的,也就是分别是。因此未知的点就只有两个,也就是须要传入四个值,而且这四个值的范围须要在
内。
因此咱们须要建立一个类CubicBezier,它拥有属性controlPoints
:
class CubicBezier {
constructor(x1, y1, x2, y2) {
this.controlPoints = [x1, y1, x2, y2];
}
}
复制代码
经过上述代码初始化之后,咱们还须要根据t(取值范围为)值获取坐标,以及一个曲线上坐标集合的数组。另外还须要使用三阶贝塞尔公式:
由于
点坐标为[0, 0],
点坐标为
为因此公式进而能够写成:
class CubicBezier {
constructor(x1, y1, x2, y2) {
this.controlPoints = [x1, y1, x2, y2];
}
getCoord(t) {
// 若是t取值不在0到1之间,则终止操做
if (t > 1 || t < 0) return;
const _t = 1 - t;
const [ x1, y1, x2, y2 ] = this.controlPoints;
const coefficient1 = 3 * t * Math.pow(_t, 2);
const coefficient2 = 3 * _t * Math.pow(t, 2);
const coefficient3 = Math.pow(t, 3);
const px = coefficient1 * x1 + coefficient2 * x2 + coefficient3;
const py = coefficient1 * y1 + coefficient2 * y2 + coefficient3;
// 结果只保留三位有效数字
return [parseFloat(px.toFixed(3)), parseFloat(py.toFixed(3))];
}
}
复制代码
利用上述的Bezier类,咱们就能够根据两个控制点构建Bezier实例,经过这个实例咱们能够根据t值,获取点上的近似值。
那么若是咱们想要根据x轴坐标值,来获取y轴坐标时,咱们该怎么作呢?
这里我使用了一个近似处理的办法,具体以下:
因此咱们须要进一步改造Bezier构造函数,须要缓存固定数量坐标数组的属性coords
,以及获取coords
的方法getCoordsArray
,最后还有获取y轴坐标的方法getY
,具体的实现方法以下:
class CubicBezier {
constructor(x1, y1, x2, y2) {
const precision = 100;
this.controlPoints = [x1, y1, x2, y2];
this.coords = this.getCoordsArray(precision);
}
getCoord(t) {
// 若是t取值不在0到1之间,则终止操做
if (t > 1 || t < 0) return;
const _t = 1 - t;
const [ x1, y1, x2, y2 ] = this.controlPoints;
const coefficient1 = 3 * t * Math.pow(_t, 2);
const coefficient2 = 3 * _t * Math.pow(t, 2);
const coefficient3 = Math.pow(t, 3);
const px = coefficient1 * x1 + coefficient2 * x2 + coefficient3;
const py = coefficient1 * y1 + coefficient2 * y2 + coefficient3;
// 结果只保留三位有效数字
return [parseFloat(px.toFixed(3)), parseFloat(py.toFixed(3))];
}
getCoordsArray(precision) {
const step = 1 / (precision + 1);
const result = [];
for (let t = 0; t <= precision + 1; t++) {
result.push(this.getCoord(t * step));
}
this.coords = result;
return result;
}
getY(x) {
if (x >= 1) return 1;
if (x <= 0) return 0;
let startX = 0;
for (let i = 0; i < this.coords.length; i++) {
if (this.coords[i][0] >= x) {
startX = i;
break;
}
}
const axis1 = this.coords[startX];
const axis2 = this.coords[startX - 1];
const k = (axis2[1] - axis1[1]) / (axis2[0] - axis1[0]);
const b = axis1[1] - k * axis1[0];
// 结果也只保留三位有效数字
return parseFloat((k * x + b).toFixed(3));
}
}
复制代码
而后经过下述方式就可使用咱们的CubicBezier
了:
const cubicBezier = new CubicBezier(0.3, 0.1, 0.3, 1);
cubicBezier.getY(0.1); // 0.072
cubicBezier.getY(0.7); // 0.931
复制代码
我写了一个应用这个
CubicBezier
构造函数的库Animate-Scroll,有兴趣的能够去看一下源码。
一个阶贝塞尔曲线能够经过一个形状彻底一致的
阶贝塞尔曲线表示。那咱们该怎么作,才能获取这个
阶贝塞尔曲线呢?
由高阶贝塞尔曲线表示低阶贝塞尔曲线的过程,咱们称之为升阶。
咱们须要用到这个等式来作升阶。
将如下等式带入上面这个公式中:
而后得出如下公式:
根据以上结果能够得出控制点由以前的
变成了
,
,
和
四个控制点了,从而完成了升阶。
这里须要进行一些推导(这里的推导须要用到
公式,有兴趣的同窗能够本身推导一下),由于:
贝塞尔公式能够表示为:
带入上述两个等式,得:
由于当
时:
因此该式能够写成:
又由于:
当
时:
因此:
将上述两个等式(1)和(2)代入公式(0)中,最终能够得出下面这个升阶公式:
关于贝塞尔曲线基本的内容就差很少讲完了,若是您发现不正确或者有补充的地方,欢迎在评论里指出😊。