前一篇文章讲了Path绘制直线以及各种基本图形,这篇文主要说Path绘制曲线,就是曲线中非常有名的贝赛尔曲线。
贝赛尔曲线是由法国数学家Pierre Bézier所发明,由此为计算机矢量图形学奠定了基础。它的主要意义在于无论是直线或曲线都能在数学上予以描述。
贝塞尔曲线作用十分广泛:
- QQ小红点拖拽效果
- 一些炫酷的下拉刷新控件
- 阅读软件的翻书效果
- 一些平滑的折线图的制作
- 很多炫酷的动画效果
理解贝塞尔曲线
塞尔曲线由起始点、数据点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。
数据点:确定曲线的起始和结束位置
控制点:确定曲线的弯曲程度
- 一阶曲线原理:
一阶曲线是没有控制点的,仅有两个数据点(A 和 B),最终效果一个线段。
- 二阶曲线原理:
二阶曲线由两个数据点(A 和 C),一个控制点(B)来描述曲线状态,大致如下:
上图中红色曲线部分就是传说中的二阶贝塞尔曲线,那么这条红色曲线是如何生成的呢?接下来我们就以其中的一个状态分析一下:
Paste_Image.png连接AB BC,并在AB上取点D,BC上取点E,使其满足条件: Paste_Image.png
连接DE,取点F,使得: 2222.gif
- 三阶曲线原理:
三阶曲线由两个数据点(A 和 D),两个控制点(B 和 C)来描述曲线状态,如下:
三阶曲线计算过程与二阶类似,具体可以见下图动态效果:
贝塞尔曲线常用操作速查表
贝塞尔曲线 | 对应的方法 | 演示动画 |
---|---|---|
一阶曲线(线性曲线) | lineTo | |
二阶曲线 | quadTo | |
三阶曲线 | cubicTo | |
四阶曲线 | 无 |
了解贝塞尔曲线相关函数使用方法
- 一阶曲线:参照上篇文章Path的操作
- 二阶曲线:二阶曲线是由两个数据点,一个控制点构成,两个数据点是控制贝塞尔曲线开始和结束的位置,比较容易理解,而控制点则是控制贝塞尔的弯曲状态,相对来说比较难以理解,所以本示例重点在于理解贝塞尔曲线弯曲状态与控制点的关系。直接上代码:
public class BezierTwo extends View {
private Paint mPaint;
private int centerX, centerY;
private PointF start, end, control; //起点,结束点,控制点
public BezierTwo(Context context) {
super(context);
init();
}
public BezierTwo(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BezierTwo(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(8);
//初始化起点,结束点,控制点
start = new PointF(0, 0);
end = new PointF(0, 0);
control = new PointF(0, 0);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
//重新设置起点,结束点和控制点的位置
start.x = centerX - 200;
start.y = centerY;
end.x = centerY + 200;
end.y = centerY;
control.x = centerX;
control.y = centerY - 100;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//根据触摸点更新控制点,并重绘
control.x = event.getX();
control.y = event.getY();
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制数据点和控制点
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(20);
canvas.drawPoint(start.x, start.y, mPaint);
canvas.drawPoint(end.x, end.y, mPaint);
canvas.drawPoint(control.x, control.y, mPaint);
//绘制辅助线
mPaint.setStrokeWidth(4);
canvas.drawLine(start.x,start.y,control.x,control.y,mPaint);
canvas.drawLine(end.x,end.y,control.x,control.y,mPaint);
//绘制贝赛尔曲线
mPaint.setStrokeWidth(8);
mPaint.setColor(Color.RED);
Path path=new Path();
path.moveTo(start.x,start.y);
path.quadTo(control.x,control.y,end.x,end.y);
canvas.drawPath(path,mPaint);
}
}
效果图如下:
为了更直观,上图还绘制了控制点和辅助线,从效果图可以看出,贝赛尔曲线是有类似于橡皮筋的效果,所以经常用于绘制一些具有弹性的效果中。
- 三阶曲线:三阶曲线由两个数据点和两个控制点来控制曲线状态。
public class BezierThree extends View {
private Paint mPaint;
private int centerX, centerY;
private PointF start, end, control1, control2;
private boolean mode = true;
public BezierThree(Context context) {
super(context);
init();
}
public BezierThree(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BezierThree(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(8);
mPaint.setStyle(Paint.Style.STROKE);
start = new PointF(0, 0);
end = new PointF(0, 0);
control1 = new PointF(0, 0);
control2 = new PointF(0, 0);
}
public void setMode(boolean mode) {
this.mode = mode;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
//初始化数据点和控制点
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control1.x = centerX;
control1.y = centerY - 100;
control2.x = centerX;
control2.y = centerY - 100;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//根据触摸位置更新控制点并重绘
if (mode) {
control1.x = event.getX();
control1.y = event.getY();
}else {
control2.x=event.getX();
control2.y=event.getY();
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制数据点和控制点
mPaint.setColor(Color.GRAY);
mPaint.setStrokeWidth(20);
canvas.drawPoint(start.x,start.y,mPaint);
canvas.drawPoint(end.x,end.y,mPaint);
canvas.drawPoint(control1.x,control1.y,mPaint);
canvas.drawPoint(control2.x,control2.y,mPaint);
//绘制辅助线
mPaint.setStrokeWidth(4);
canvas.drawLine(start.x,start.y,control1.x,control1.y,mPaint);
canvas.drawLine(control1.x,control1.y,control2.x,control2.y,mPaint);
canvas.drawLine(control2.x,control2.y,end.x,end.y,mPaint);
// 绘制贝塞尔曲线
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(8);
Path path=new Path();
path.moveTo(start.x,start.y);
path.cubicTo(control1.x,control1.y,control2.x,control2.y,end.x,end.y);
canvas.drawPath(path,mPaint);
}
效果如图:
源码下载:https://github.com/baojie0327/ViewAndGroup
网友评论