一路升级打怪终于到了贝塞尔曲线啦!学到这里我们就可以搞一些炫酷的特效啦,qq的红点拖动、小说阅读的翻页特效这些都是常见的典例,是不是好期待!!!是不是想开启装X模式,自己亲手实战,别急我们先探讨下本节内容。。。
知识点
一、贝塞尔曲线简介
1、起源
在数学的数值分析领域中,贝塞尔曲线(英语:Bézier curve,亦作“贝塞尔”)是计算机图形学中相当重要的参数曲线。更高维度的广泛化贝塞尔曲线就称作贝兹曲面,其中贝兹三角是一种特殊的实例。
贝塞尔曲线于1962年,由法国工程师皮埃尔·贝兹(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由保尔·德·卡斯特里奥于1959年运用德卡斯特里奥算法开发,以稳定数值的方法求出贝塞尔曲线。
ps:这看着怎么这么详细?嘿嘿!没错这就是“维基百科” 对贝塞尔曲线的解释
2、常见场景
1、qq消息红点气泡的拖动
2、阅读app的翻页特效
3、炫酷特效太多啦…
3、分类
一阶贝塞尔曲线
二阶贝塞尔曲线
三阶贝塞尔曲线
n阶。。。。。
二、原理及其要点
须知!!!
1、图片来源:维基百科-贝塞尔曲线
2、数学公式可以忽略,我们主要看原理图(理解)
3、看对应的安卓封装方法(运用)
4、说了一大堆其实就是运用贝塞尔曲线画一条曲线。
1、一阶原理图解
(1)数学公式----看着算啦
给定点P0、P1,线性贝塞尔曲线(一阶贝塞尔曲线)只是一条两点之间的直线。这条线由下式给出:
(2)原理图
定点:p0、p1
控制点:没有
结果:一条直线,和我们前面学习的path的lineTo一样,从起点到终点的一条直线。
(3)对应Path方法
对应path的 lineTo方法
2、二阶原理图
(1)数学公式----看着算啦
二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:
(2)原理图
二阶贝塞尔曲线;
1、定点p0、p2
2、控制点p1
曲线的得到过程:
如上图D、E、F分别为所在直线的取点。当D从A到B变化(或者B到A)E从B到C变化(或者C到B)F点的位置变化轨迹就形成曲线。
如上我们可以拆分二阶贝塞尔曲线:
1、定点A、B 、 无控制点的一阶贝塞尔曲线
2、定点B、C、无控制点的一阶贝塞尔曲线
3、定点D、E无控制点 的一阶贝塞尔曲线
ps:当D点E点在AB 、BC上变化时,定点D、E无控制点 的一阶贝塞尔曲线就形成了曲线轨迹。
(3)对应Path方法
对应path的 quadTo方法
/**
* Add a quadratic bezier from the last point, approaching control point
* (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
* this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the control point on a quadratic curve
* @param y1 The y-coordinate of the control point on a quadratic curve
* @param x2 The x-coordinate of the end point on a quadratic curve
* @param y2 The y-coordinate of the end point on a quadratic curve
*/
public void quadTo(float x1, float y1, float x2, float y2)
画一条贝塞尔曲线,控制点为x1,y1。结束点为x2,y2。如果path使用前没有调用过moveTo那么,起点为上一次path结束点或者默认为原点
参数:x1,y1控制点坐标
参数:x2,y2结束点坐标
3、三阶原理图
(1)数学公式----看着算啦
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;这两个点只是在那里提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P2之前,走向P1方向的“长度有多长”。
(2)原理图
(3)对应Path方法
对应path的 cubicTo方法
/**
* Add a cubic bezier from the last point, approaching control points
* (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
* made for this contour, the first point is automatically set to (0,0).
*
* @param x1 The x-coordinate of the 1st control point on a cubic curve
* @param y1 The y-coordinate of the 1st control point on a cubic curve
* @param x2 The x-coordinate of the 2nd control point on a cubic curve
* @param y2 The y-coordinate of the 2nd control point on a cubic curve
* @param x3 The x-coordinate of the end point on a cubic curve
* @param y3 The y-coordinate of the end point on a cubic curve
*/
public void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3)
参数:x1,y1控制点1坐标
参数:x2,y2控制点2坐标
参数:x3,y3结束点坐标
三、贝塞尔曲线的使用
1、一阶贝塞尔曲线
一阶就是直线,参考path的画直线操作即可。
2、二阶贝塞尔曲线
上文了解:二阶贝塞尔曲线组成
1、两个定点(确定位置)
2、一个控制点(确定曲线弯曲程度)
(1)栗子预览
(2)代码
/**
* Created by sunnyDay on 2019/8/2 11:28
* 二阶贝塞尔曲线
*/
public class Bezier extends View {
private Paint paint;
private Point start, end, control;
public Bezier(Context context) {
this(context, null);
}
public Bezier(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Bezier(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
// 初始化画笔
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
paint.setTextSize(60);
// 初始化点
start = new Point(-200, -200);
end = new Point(200, 200);
control = new Point(0, -200);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int centerX = w / 2;
int centerY = h / 2;
// 初始化 两定点、控制点坐标
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control.x = centerX;
control.y = centerY - 100;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 更新控制点 通知重绘
control.x = (int) event.getX();
control.y = (int) event.getY();
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.GRAY);
paint.setStrokeWidth(20);
// 绘制贝塞尔曲线的起始点 控制点
canvas.drawPoint(start.x, start.y, paint);
canvas.drawPoint(end.x, end.y, paint);
canvas.drawPoint(control.x, control.y, paint);
paint.setStrokeWidth(4);
// 绘制定点到控制点的辅助线
canvas.drawLine(start.x, start.y, control.x, control.y, paint);
canvas.drawLine(end.x, end.y, control.x, control.y, paint);
//绘制贝塞尔曲线
paint.setColor(Color.RED);
paint.setStrokeWidth(8);
Path path = new Path(); //此处有坑
path.moveTo(start.x, start.y); //起点
path.quadTo(control.x, control.y, end.x, end.y);
canvas.drawPath(path, paint);
}
}
1、模仿大佬的到吗实现了一遍
2、代码中的坑,当时自己天真想着onDraw方法不断重绘即Path path = new Path();产生过多的对象岂不浪费,于是就吧对象的初始化放在了构造中结果gg,绘制时path为同一个对象,导致出现了好多曲线。这里需要留意下。。。。。
3、三阶贝塞尔曲线
三阶贝塞尔曲线组成:
1、两个数据点
2、两个控制点
(1)效果
(2)栗子
package com.example.customview;
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by sunnyDay on 2019/8/2 11:28
* 三阶贝塞尔曲线
*/
public class Bezier3 extends View {
private Paint paint;
private Point start, end, control, control2;
private boolean mode = true;
public Bezier3(Context context) {
this(context, null);
}
public Bezier3(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Bezier3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
paint = new Paint();
// 初始化画笔
paint.setColor(Color.BLUE);
paint.setAntiAlias(true);
paint.setStrokeWidth(8);
paint.setStyle(Paint.Style.STROKE);
paint.setTextSize(60);
// 初始化点
start = new Point(0, 0);
end = new Point(0, 0);
control = new Point(0, 0);
control2 = new Point(0,0);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int centerX = w / 2;
int centerY = h / 2;
// 初始化 两定点、控制点坐标
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control.x = centerX-200;
control.y = centerY - 100;
control2.x = centerX+200;
control2.y = centerY - 100;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 更新控制点 通知重绘
if(mode){
control.x = (int) event.getX();
control.y = (int) event.getY();
}else{
control2.x = (int) event.getX();
control2.y = (int) event.getY();
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.GRAY);
paint.setStrokeWidth(20);
// 绘制贝塞尔曲线的起始点 控制点
canvas.drawPoint(start.x, start.y, paint);
canvas.drawPoint(end.x, end.y, paint);
canvas.drawPoint(control.x, control.y, paint);
canvas.drawPoint(control2.x, control2.y, paint);
paint.setStrokeWidth(4);
// 绘制定点到控制点、控制点到控制点的辅助线
canvas.drawLine(start.x, start.y, control.x, control.y, paint);
canvas.drawLine(control.x,control.y,control2.x,control2.y,paint);
canvas.drawLine(control2.x,control2.y, end.x, end.y, paint);
//绘制贝塞尔曲线
paint.setColor(Color.RED);
paint.setStrokeWidth(8);
Path path = new Path(); //此处有坑
path.moveTo(start.x, start.y); //起点
path.cubicTo(control.x, control.y, control2.x,control2.y, end.x, end.y);
canvas.drawPath(path, paint);
}
public void setMode(boolean mode) {
this.mode = mode;
}
}
和二阶的使用方式不同:
1、多画了一个定点
2、多画了一个辅助线
3、path调用的方法不同(cubicTo)
4、升阶与降阶
高阶的贝塞尔曲线都可以由低阶的组合来实现
(1)降阶
定义:
保持曲线形状与方向不变
减少控制点数量
结果:
数据点变多
控制点可能减少
灵活性变弱
(2)升阶
定义:
保持曲线形状与方向不变,
增加控制点数量
结果:
数据点不变,
控制点增加,
灵活性变强
四、感悟
理解贝塞尔曲线的原理
理解曲线的生成方式
贝塞尔曲线就是画的曲线直线集合(曲线 的圆、圆弧都可以用贝塞尔曲线画出)
end
参考模仿:
Path之贝塞尔曲线
自定义view学习推荐参考:
两位大佬写的真心不错!