为何要讲贝塞尔曲线,实际上 Android 中不少效果都有用到贝塞尔曲线。html
QQ空间 直播页面右下角的礼物冒泡特效 android
水流波动效果git
图片或书本翻页效果github
能够先对贝塞尔曲线有一个大概的认识。canvas
贝塞尔曲线维基百科api
贝塞尔曲线的数学基础是早在 1912 年就广为人知的 伯恩斯坦多项式 。但直到 1959 年,当时就任于雪铁龙的法国数学家 Paul de Casteljau 才开始对它进行图形化应用的尝试,并提出了一种数值稳定的 de Casteljau 算法。然而贝塞尔曲线的得名,倒是因为 1962 年另外一位就任于雷诺的法国工程师 Pierre Bézier 的普遍宣传。他使用这种只须要不多的控制点就可以生成复杂平滑曲线的方法,来辅助汽车车体的工业设计。函数
正是由于控制简便却具备极强的描述能力,贝塞尔曲线在工业设计领域迅速获得了普遍的应用。不只如此,在计算机图形学领域,尤为是矢量图形学,贝塞尔曲线也占有重要的地位。今天咱们最多见的一些矢量绘图软件,如 Flash、Illustrator、CorelDraw 等,无一例外都提供了绘制贝塞尔曲线的功能。甚至像 Photoshop 这样的位图编辑软件,也把贝塞尔曲线做为仅有的矢量绘制工具(钢笔工具)包含其中。工具
贝塞尔曲线在 Web 开发领域一样占有一席之地。CSS3 新增了 transition-timing-function 属性,它的取值就能够设置为一个三次贝塞尔曲线方程。在此以前,也有很多 JavaScript 动画库使用贝塞尔曲线来实现美观逼真的缓动效果。
线性贝塞尔曲线
给定点P0、P1,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:
二次方贝塞尔曲线
二次方贝塞尔曲线的路径由给定点P0、P一、P2的函数B(t)追踪:
三次方贝塞尔曲线
P0、P一、P二、P3 四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于 P0 走向 P1 ,并从 P2 的方向来到 P3 。通常不会通过 P1 或 P2 ;这两个点只是在那里提供方向资讯。 P0 和 P1 之间的间距,决定了曲线在转而趋进 P2 以前,走向 P1 方向的“长度有多长”。
曲线的参数形式为:
n阶贝塞尔曲线
n阶贝塞尔曲线可以下判断,给定点P0、p一、...、Pn,其贝塞尔曲线即
看公式仍是有些抽象,接下来能够看一下图解,具像的了解一下什么是贝塞尔曲线:以二阶贝塞尔曲线和三阶贝塞尔曲线为例。
有一篇文章写的极好
贝塞尔曲线不只能画直线,也能画曲线。即使是更复杂的曲线,控制点的增长也只是线性的。这一特色使其不光在工业设计领域大展拳脚,就连数学基础很差的人也能够比较容易地掌握,好比大多数平面美术设计师们。
简单来讲,贝塞尔曲线就是将任意一条曲线转化为准确的数学公式。 Bezier 曲线是用一系列点来控制曲线状态的。
一些关键的名词
在 Android 开发中,咱们只考虑二阶贝塞尔曲线和三阶贝塞尔曲线, SDK 也是只提供了二阶和三阶的 API 调用。
固然,对于再高阶的贝塞尔曲线,一般能够拆分红多个低阶的贝塞尔曲线。
推荐一个好的贝塞尔曲线的动态演示,能够很直观的感觉一下:
myst729.github.io/bezier-curv…
这里推荐 医生 的一片博文,已经很是全面的介绍了贝塞尔曲线在 Android 中的一些用法及实例,写的很好。
下面的内容都基于这篇文章。
quadTo 是基于绝对坐标,而 rQuadTo 是基于相对坐标。
mPath.moveTo(mStartPointX, mStartPointY);
//二阶贝塞尔曲线
mPath.quadTo(mAuxiliaryX, mAuxiliaryY, mEndPointX, mEndPointY);
canvas.drawPath(mPath, mPaintBezier);复制代码
这两个API在原理上也是能够互相转换的。
两个点的话涉及到多点触摸,有兴趣能够看看下面这篇文章。
cubicTo是基于绝对坐标,而rCubicTo是基于相对坐标。
mPath.moveTo(mStartPointX, mStartPointY);
// 三阶贝塞尔曲线
mPath.cubicTo(mAuxiliaryOneX, mAuxiliaryOneY, mAuxiliaryTwoX, mAuxiliaryTwoY, mEndPointX, mEndPointY);
canvas.drawPath(mPath, mPaintBezier);复制代码
三阶贝塞尔曲线其实也没什么,就是多了一个控制点,记录下他的位置就能够了。
当在屏幕上绘制路径的时候,咱们一般会经过 Path.lineTo 将各个触点链接起来,可是这样确定会很生硬。由于是经过直线来链接的。若是经过二阶贝塞尔曲线,就会圆滑不少。不会出现太多的生硬链接。
if (dx >= offset || dy >= offset) {
// 贝塞尔曲线的控制点为起点和终点的中点
float cX = (x1 + preX) / 2;
float cY = (y1 + preY) / 2;
mPath.quadTo(preX, preY, cX, cY);
// mPath.lineTo(x1, y1);
mX = x1;
mY = y1;
}复制代码
经过纪录 Move 过程当中点位置的变化,而后传入 quadTo 中的参数,就能够实现圆滑的绘图。
根据公式咱们能够模仿着写一个工具类:
public class BezierUtil {
/** * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1] * * @param t 曲线长度比例 * @param p0 起始点 * @param p1 控制点 * @param p2 终止点 * @return t对应的点 */
public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
PointF point = new PointF();
float temp = 1 - t;
point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
return point;
}
/** * B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3, t ∈ [0,1] * * @param t 曲线长度比例 * @param p0 起始点 * @param p1 控制点1 * @param p2 控制点2 * @param p3 终止点 * @return t对应的点 */
public static PointF CalculateBezierPointForCubic(float t, PointF p0, PointF p1, PointF p2, PointF p3) {
PointF pointF = new PointF();
float temp = 1- t;
pointF.x = p0.x * temp * temp * temp + 3 * p1.x * t * temp * temp + 3 * p2.x * t * t * temp + p3.x * t * t * t;
pointF.y = p0.y * temp * temp * temp + 3 * p1.y * t * temp * temp + 3 * p2.y * t * t * temp + p3.y * t * t * t;
return pointF;
}
}复制代码
github.com/venshine/Be… ( 经过 de Casteljau 算法绘制贝塞尔曲线,并计算它的切线,实现 1-7 阶贝塞尔曲线的造成动画 )