本文主要內(nèi)容為貝塞爾曲線原理解析并用 SurfaceView 實現(xiàn)其展示動畫
關于SurfaceView 的使用蛉顽,大家可以看我的上一篇文章 Android:SurfaceView 的使用(附代碼模板)
概述
貝塞爾曲線(Bézier curve)乏屯,又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數(shù)學曲線瑞凑。一般的矢量圖形軟件通過它來精確畫出曲線扼褪,貝茲曲線由線段與節(jié)點組成砂碉,節(jié)點是可拖動的支點,線段像可伸縮的皮筋旬蟋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。貝塞爾曲線是計算機圖形學中相當重要的參數(shù)曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具技掏,如PhotoShop等幔崖。在Flash4中還沒有完整的曲線工具滋迈,而在Flash5里面已經(jīng)提供出貝塞爾曲線工具罪郊。
曲線作用
由于用計算機畫圖大部分時間是操作鼠標來掌握線條的路徑,與手繪的感覺和效果有很大的差別扣囊。即使是一位精明的畫師能輕松繪出各種圖形乎折,拿到鼠標想隨心所欲的畫圖也不是一件容易的事。這一點是計算機萬萬不能代替手工的工作侵歇,所以到目前為止人們只能頗感無奈骂澄。使用貝塞爾工具畫圖很大程度上彌補了這一缺憾。貝塞爾曲線是計算機圖形圖像造型的基本工具惕虑,是圖形造型運用得最多的基本線條之一坟冲。
——來自百度百科
如果大家用過 XMind 軟件或者是 WPS 軟件來繪制思維導圖的話,那么對下面的連線一定不會陌生溃蔫。下圖中如果我們要連接兩個分支主題健提,就會用到基于三階貝塞爾曲線的連線功能,如下所示酒唉。我們先確定需要被連接起來的起始點和終點矩桂,然后拖動兩個控制點就可以任意地繪制一條連接曲線沸移。通過這篇文章痪伦,我為大家講解一下貝塞爾曲線的實現(xiàn)原理侄榴,看完本文,你也可以輕輕松松實現(xiàn)這個炫酷的貝塞爾曲線网沾。
貝塞爾曲線公式
以下公式中:B(t)為t時間下 點的坐標癞蚕;P0為起點,Pn為終點,Pi為控制點
一階貝塞爾曲線公式(線性公式)
給定點P0、P1辉哥,線性貝茲曲線只是一條兩點之間的直線桦山。且其等同于線性插值。
二階貝塞爾曲線公式
設P0醋旦、P02恒水、P2是一條曲線上順序三個不同的點。過P0和P2點的兩切線交于P1點饲齐,在P02點的切線交P0P1和P2P1于P01和P11钉凌,則如下比例成立:
當P0,P2固定捂人,引入?yún)?shù) t御雕,令上述比值為 t:(1-t),即有:
將一式二式代入三式可得:
二階貝塞爾曲線P02可以定義為分別由前兩個頂點(P0,P1)和后兩個頂點(P1,P2)決定的一階貝塞爾曲線的線性組合滥搭。
可得出二階貝塞爾曲線的公式:
三階貝塞爾曲線公式
按照二階的推導原理酸纲,以此類推,由四個控制點定義的三階貝塞爾曲線P03可被定義為分別由(P0瑟匆,P1闽坡,P2)和(P1,P2愁溜,P3)確定的兩條二階貝塞爾曲線的線性組合:P03 = (1-t)P02 + tP12
可遞歸代入得出三階貝塞爾曲線公式:
現(xiàn)代的成象系統(tǒng)无午,如PostScript、Asymptote和Metafont祝谚,運用了以貝塞爾樣條組成的三階貝塞爾曲線宪迟,用來描繪曲線輪廓。
四階及以上的貝塞爾曲線公式
由(n+1)個控制點 Pi(i=0,1,...,n) 定義的n階貝塞爾曲線 P0n 可被定義為分別由前交惯、后 n 個控制點定義的兩條 (n-1) 階貝塞爾曲線 P0n-1 與 P1n-1 的線性組合:
由此得到貝塞爾曲線的遞推計算公式(通用公式):
其一般參數(shù)公式為:
n 階貝塞爾曲線可如下推斷次泽。給定點P0、P1席爽、…意荤、Pn,其貝塞爾曲線即:
四階貝塞爾曲線:
五階貝塞爾曲線:
公式說明
- 開始于P0并結束于Pn的曲線只锻,即所謂的端點插值法屬性玖像。
- 曲線是直線的充分必要條件是所有的控制點都位在曲線上。同樣的齐饮,貝塞爾曲線是直線的充分必要條件是控制點共線捐寥。
- 曲線的起始點(結束點)相切于貝塞爾多邊形的第一節(jié)(最后一節(jié))笤昨。
- 一條曲線可在任意點切割成兩條或任意多條子曲線,每一條子曲線仍是貝塞爾曲線握恳。
- 一些看似簡單的曲線(如圓)無法以貝塞爾曲線精確的描述瞒窒,或分段成貝塞爾曲線(雖然當每個內(nèi)部控制點對單位圓上的外部控制點水平或垂直的的距離為時,分成四段的貝塞爾曲線乡洼,可以小于千分之一的最大半徑誤差近似于圓)崇裁。
- 位于固定偏移量的曲線(來自給定的貝塞爾曲線),又稱作偏移曲線(假平行于原來的曲線束昵,如兩條鐵軌之間的偏移)無法以貝塞爾曲線精確的形成(某些瑣屑實例除外)拔稳。無論如何,現(xiàn)存的啟發(fā)法通城鲁可為實際用途中給出近似值壳炎。
Android上實現(xiàn)貝賽爾曲線
在Android實現(xiàn)貝賽爾曲線,要借助android.graphics.Path逼侦,其中繪制貝賽爾曲線的方法在Api v1就已經(jīng)提供了:
Path.moveTo(float x, float y) // Path的初始點
Path.lineTo(float x, float y) // 線性公式的貝賽爾曲線, 其實就是直線
Path.quadTo(float x1, float y1, float x2, float y2) // 二階貝賽爾曲線
Path.cubicTo(float x1, float y1, float x2, float y2, float x3, float y3) // 三階貝賽爾曲線
...
這上面是Java層調(diào)用的代碼匿辩,最終調(diào)用的是Skia庫的一系列方法,Skia是一個C++2D向量圖形處理函數(shù)庫榛丢,感興趣的可以繼續(xù)深入研究研究铲球。
實現(xiàn)二階貝塞爾曲線
一、首先來看一下可操作的二階貝塞爾曲線動態(tài)圖:
自定義 MyBezierCurveQuadratic 類代碼如下晰赞,很簡單:
package com.example.pc.mybeziercurveview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 二階
* Created by Deeson on 2017/5/24.
*/
public class MyBezierCurveQuadratic extends View{
private Paint mPaint;
private Path mPath;
private int centerX,centerY;
private PointF start,end,control;
public MyBezierCurveQuadratic(Context context) {
super(context);
init();
}
public MyBezierCurveQuadratic(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyBezierCurveQuadratic(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPath = new Path();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(8);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setTextSize(60);
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;
//初始化數(shù)據(jù)點和控制點的位置
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control.x = centerX;
control.y = centerY - 100;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制數(shù)據(jù)點和控制點
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(control.x, control.y, end.x, end.y, mPaint);
//繪制二階貝塞爾曲線
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(8);
mPath.reset();
mPath.moveTo(start.x,start.y);
mPath.quadTo(control.x, control.y, end.x, end.y);
canvas.drawPath(mPath,mPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
control.x = event.getX();
control.y = event.getY();
invalidate();
break;
}
return true;
}
}
二稼病、接著來看,如何用 SurfaceView 實現(xiàn)如下曲線動畫效果:
關于SurfaceView 的使用掖鱼,大家可以看我的上一篇文章 Android:SurfaceView 的使用(附代碼模板)
這里然走,最關鍵的代碼是開啟子線程繪制二階貝塞爾曲線,如下:
//輔助線坐標點
process1.x = (1 - t) * start.x + t * control.x;
process1.y = (1 - t) * start.y + t * control.y;
process2.x = (1 - t) * control.x + t * end.x;
process2.y = (1 - t) * control.y + t * end.y;
//貝塞爾曲線通用函數(shù)
x = (1 - t) * process1.x + t * process2.x;
y = (1 - t) * process1.y + t * process2.y;
mPath.lineTo(x, y);
//繪制數(shù)據(jù)點和控制點
mCanvas.drawPoint(start.x, start.y, mPointPaint);
mCanvas.drawPoint(control.x, control.y, mPointPaint);
mCanvas.drawPoint(end.x, end.y, mPointPaint);
//繪制數(shù)據(jù)點和控制點的連線
mCanvas.drawLine(start.x, start.y, control.x, control.y, mLinePaint);
mCanvas.drawLine(control.x, control.y, end.x, end.y, mLinePaint);
//繪制輔助線和輔助點
mCanvas.drawLine(process1.x, process1.y, process2.x, process2.y, mAssistLinePaint);
mCanvas.drawPoint(process1.x,process1.y,mAssistPointPaint);
mCanvas.drawPoint(process2.x,process2.y,mAssistPointPaint);
//繪制二階貝塞爾曲線的當前點
mCanvas.drawPoint(x, y, mPointPaint);
//繪制二階貝塞爾曲線
mCanvas.drawPath(mPath, mPaint);
自定義 QuadraticBezierShowView 類的完整代碼如下:
package com.example.pc.mybeziercurveview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 二階
* Created by Deeson on 2017/5/24.
*/
public class QuadraticBezierShowView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//分別對應貝塞爾曲線戏挡、點芍瑞、數(shù)據(jù)點和控制點之間的線、輔助線褐墅、輔助點
private Paint mPaint, mPointPaint, mLinePaint, mAssistLinePaint,mAssistPointPaint;
//繪制貝塞爾曲線的path
private Path mPath;
//布局的中心點
private int centerX, centerY;
//分別對應貝塞爾曲線的起點拆檬、終點、控制點妥凳、輔助線的起點竟贯、終點
private PointF start, end, control, process1, process2;
private SurfaceHolder mHolder;
//用于繪圖的canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
float x = 0;//貝塞爾曲線的實時點x坐標
float y = 0;//貝塞爾曲線的實時點y坐標
float t = 0;//實施進度,0<=t<=1
public QuadraticBezierShowView(Context context) {
super(context);
init();
}
public QuadraticBezierShowView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public QuadraticBezierShowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPath = new Path();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setTextSize(60);
mPointPaint = new Paint();
mPointPaint.setColor(Color.BLACK);
mPointPaint.setStrokeWidth(10);
mPointPaint.setStyle(Paint.Style.STROKE);
mLinePaint = new Paint();
mLinePaint.setColor(Color.GRAY);
mLinePaint.setStrokeWidth(4);
mLinePaint.setStyle(Paint.Style.STROKE);
mAssistLinePaint = new Paint();
mAssistLinePaint.setColor(Color.GREEN);
mAssistLinePaint.setStrokeWidth(4);
mAssistLinePaint.setStyle(Paint.Style.STROKE);
mAssistPointPaint = new Paint();
mAssistPointPaint.setColor(Color.GREEN);
mAssistPointPaint.setStrokeWidth(10);
mAssistPointPaint.setStyle(Paint.Style.FILL);
start = new PointF(0, 0);
end = new PointF(0, 0);
control = new PointF(0, 0);
process1 = new PointF(0, 0);
process2 = new PointF(0, 0);
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
//初始化數(shù)據(jù)點和控制點的位置
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control.x = centerX - 50;
control.y = centerY - 300;
x = start.x;
y = start.y;
mPath.moveTo(x, y);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing) {
draw();
if (t <= 1) {
t += 0.003;
//輔助線坐標點
process1.x = (1 - t) * start.x + t * control.x;
process1.y = (1 - t) * start.y + t * control.y;
process2.x = (1 - t) * control.x + t * end.x;
process2.y = (1 - t) * control.y + t * end.y;
//貝塞爾曲線通用函數(shù)
x = (1 - t) * process1.x + t * process2.x;
y = (1 - t) * process1.y + t * process2.y;
//二階貝塞爾曲線函數(shù)
// x = (float) (Math.pow((1 - t), 2) * start.x + 2 * t * (1 - t) * control.x + Math.pow(t, 2) * end.x);
// y = (float) (Math.pow((1 - t), 2) * start.y + 2 * t * (1 - t) * control.y + Math.pow(t, 2) * end.y);
mPath.lineTo(x, y);
} else {
mIsDrawing = false;
}
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
mCanvas.drawColor(Color.WHITE);
//繪制數(shù)據(jù)點和控制點
mCanvas.drawPoint(start.x, start.y, mPointPaint);
mCanvas.drawPoint(control.x, control.y, mPointPaint);
mCanvas.drawPoint(end.x, end.y, mPointPaint);
//繪制數(shù)據(jù)點和控制點的連線
mCanvas.drawLine(start.x, start.y, control.x, control.y, mLinePaint);
mCanvas.drawLine(control.x, control.y, end.x, end.y, mLinePaint);
//繪制輔助線和輔助點
mCanvas.drawLine(process1.x, process1.y, process2.x, process2.y, mAssistLinePaint);
mCanvas.drawPoint(process1.x,process1.y,mAssistPointPaint);
mCanvas.drawPoint(process2.x,process2.y,mAssistPointPaint);
//繪制二階貝塞爾曲線的當前點
mCanvas.drawPoint(x, y, mPointPaint);
//繪制二階貝塞爾曲線
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != mCanvas) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
實現(xiàn)三階貝塞爾曲線
一逝钥、同樣的屑那,先看看可操作的三階貝塞爾曲線:
自定義 MyBezierCurveCubic 類的完整代碼如下:
package com.example.pc.mybeziercurveview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 三階
* Created by Deeson on 2017/5/24.
*/
public class MyBezierCurveCubic extends View{
private Paint mPaint;
private Path mPath;
private int centerX,centerY;
private PointF start,end,control1,control2;
public static final int CONTROL_ONE = 0;
public static final int CONTROL_TWO = 1;
private int control = CONTROL_ONE;
public MyBezierCurveCubic(Context context) {
super(context);
init();
}
public MyBezierCurveCubic(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyBezierCurveCubic(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPath = new Path();
mPaint.setColor(Color.BLACK);
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);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w/2;
centerY = h/2;
//初始化數(shù)據(jù)點和控制點
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control1.x = centerX - 200;
control1.y = centerY - 200;
control2.x = centerX + 200;
control2.y = centerY + 200;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//繪制數(shù)據(jù)點和控制點
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);
mPath.reset();
mPath.moveTo(start.x,start.y);
mPath.cubicTo(control1.x,control1.y,control2.x,control2.y,end.x,end.y);
canvas.drawPath(mPath,mPaint);
}
public void setControl(int control){
this.control = control;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
if(control == CONTROL_ONE){
control1.x = event.getX();
control1.y = event.getY();
}else{
control2.x = event.getX();
control2.y = event.getY();
}
invalidate();
break;
}
return true;
}
}
二、接著來看,如何用 SurfaceView 實現(xiàn)如下三階貝塞爾曲線動畫效果:
關于SurfaceView 的使用持际,大家可以看我的上一篇文章 Android:SurfaceView 的使用(附代碼模板)
自定義 CubicBezierShowView 類的完整代碼如下:
package com.example.pc.mybeziercurveview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* 三階
* Created by Deeson on 2016/7/12.
*/
public class CubicBezierShowView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
//分別對應貝塞爾曲線沃琅、點、數(shù)據(jù)點和控制點之間的線选酗、第一層輔助線阵难、第一層輔助點岳枷、第二層輔助線芒填、第二層輔助點
private Paint mPaint, mPointPaint, mLinePaint, mAssistLine1Paint, mAssistPoint1Paint, mAssistLine2Paint, mAssistPoint2Paint;
//繪制貝塞爾曲線的path
private Path mPath;
//布局的中心點
private int centerX, centerY;
//分別對應三階貝塞爾曲線的起點、終點空繁、控制點
private PointF start, end, control1, control2;
//第一層輔助線的3個端點(相當于動態(tài)的二階貝塞爾曲線的起點殿衰,控制點,終點)
private PointF process1, process2, process3;
//第二層輔助線的起點和終點
private PointF secondProcess1, secondProcess2;
private SurfaceHolder mHolder;
//用于繪圖的canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
float x = 0;//貝塞爾曲線的實時點x坐標
float y = 0;//貝塞爾曲線的實時點y坐標
float t = 0;//實施進度盛泡,0<=t<=1
public CubicBezierShowView(Context context) {
super(context);
init();
}
public CubicBezierShowView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CubicBezierShowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//貝塞爾曲線
mPaint = new Paint();
mPath = new Path();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setTextSize(60);
//點
mPointPaint = new Paint();
mPointPaint.setColor(Color.BLACK);
mPointPaint.setStrokeWidth(10);
mPointPaint.setStyle(Paint.Style.STROKE);
//數(shù)據(jù)點和控制點的連線
mLinePaint = new Paint();
mLinePaint.setColor(Color.GRAY);
mLinePaint.setStrokeWidth(4);
mLinePaint.setStyle(Paint.Style.STROKE);
//第一層輔助線
mAssistLine1Paint = new Paint();
mAssistLine1Paint.setColor(Color.GREEN);
mAssistLine1Paint.setStrokeWidth(4);
mAssistLine1Paint.setStyle(Paint.Style.STROKE);
//第一層輔助點
mAssistPoint1Paint = new Paint();
mAssistPoint1Paint.setColor(Color.GREEN);
mAssistPoint1Paint.setStrokeWidth(10);
mAssistPoint1Paint.setStyle(Paint.Style.FILL);
//第二層輔助線
mAssistLine2Paint = new Paint();
mAssistLine2Paint.setColor(Color.BLUE);
mAssistLine2Paint.setStrokeWidth(4);
mAssistLine2Paint.setStyle(Paint.Style.STROKE);
//第二層輔助線
mAssistPoint2Paint = new Paint();
mAssistPoint2Paint.setColor(Color.BLUE);
mAssistPoint2Paint.setStrokeWidth(10);
mAssistPoint2Paint.setStyle(Paint.Style.FILL);
//三階貝塞爾曲線的起點終點
start = new PointF(0, 0);
end = new PointF(0, 0);
//三階貝塞爾曲線的兩個控制點
control1 = new PointF(0, 0);
control2 = new PointF(0, 0);
//第一層輔助線的三個端點
process1 = new PointF(0, 0);
process2 = new PointF(0, 0);
process3 = new PointF(0, 0);
//第二層輔助線的兩個端點
secondProcess1 = new PointF(0, 0);
secondProcess2 = new PointF(0, 0);
mHolder = getHolder();
mHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
centerX = w / 2;
centerY = h / 2;
//初始化數(shù)據(jù)點和控制點的位置
start.x = centerX - 200;
start.y = centerY;
end.x = centerX + 200;
end.y = centerY;
control1.x = centerX - 150;
control1.y = centerY - 300;
control2.x = centerX + 170;
control2.y = centerY - 340;
x = start.x;
y = start.y;
mPath.moveTo(x, y);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing) {
draw();
if (t <= 1) {
t += 0.003;
//重點在這里
bezierDraw();
mPath.lineTo(x, y);
} else {
mIsDrawing = false;
}
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
mCanvas.drawColor(Color.WHITE);
//繪制數(shù)據(jù)點和控制點
mCanvas.drawPoint(start.x, start.y, mPointPaint);
mCanvas.drawPoint(control1.x, control1.y, mPointPaint);
mCanvas.drawPoint(control2.x, control2.y, mPointPaint);
mCanvas.drawPoint(end.x, end.y, mPointPaint);
//繪制數(shù)據(jù)點和控制點的連線
mCanvas.drawLine(start.x, start.y, control1.x, control1.y, mLinePaint);
mCanvas.drawLine(control1.x, control1.y, control2.x, control2.y, mLinePaint);
mCanvas.drawLine(control2.x, control2.y, end.x, end.y, mLinePaint);
//繪制第一層輔助線和輔助點
mCanvas.drawLine(process1.x, process1.y, process2.x, process2.y, mAssistLine1Paint);
mCanvas.drawLine(process2.x, process2.y, process3.x, process3.y, mAssistLine1Paint);
mCanvas.drawPoint(process1.x, process1.y, mAssistPoint1Paint);
mCanvas.drawPoint(process2.x, process2.y, mAssistPoint1Paint);
mCanvas.drawPoint(process3.x, process3.y, mAssistPoint1Paint);
//繪制第二層輔助線和輔助點
mCanvas.drawLine(secondProcess1.x, secondProcess1.y, secondProcess2.x, secondProcess2.y, mAssistLine2Paint);
mCanvas.drawPoint(secondProcess1.x, secondProcess1.y, mAssistPoint2Paint);
mCanvas.drawPoint(secondProcess2.x, secondProcess2.y, mAssistPoint2Paint);
//繪制三階貝塞爾曲線的當前點
mCanvas.drawPoint(x, y, mPointPaint);
//繪制三階貝塞爾曲線
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != mCanvas) {
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
private void bezierDraw() {
//第一層輔助線坐標點
process1.x = (1 - t) * start.x + t * control1.x;
process1.y = (1 - t) * start.y + t * control1.y;
process2.x = (1 - t) * control1.x + t * control2.x;
process2.y = (1 - t) * control1.y + t * control2.y;
process3.x = (1 - t) * control2.x + t * end.x;
process3.y = (1 - t) * control2.y + t * end.y;
//第二層輔助線坐標點
secondProcess1.x = (1 - t) * process1.x + t * process2.x;
secondProcess1.y = (1 - t) * process1.y + t * process2.y;
secondProcess2.x = (1 - t) * process2.x + t * process3.x;
secondProcess2.y = (1 - t) * process2.y + t * process3.y;
//貝塞爾曲線通用公式
x = (1 - t) * secondProcess1.x + t * secondProcess2.x;
y = (1 - t) * secondProcess1.y + t * secondProcess2.y;
//三階貝塞爾曲線函數(shù)
// x = (float) (Math.pow((1 - t), 3) * start.x + 3 * t * Math.pow((1 - t), 2) * control1.x + 3 * Math.pow(t, 2) * (1 - t) * control2.x + Math.pow(t, 3) * end.x);
// y = (float) (Math.pow((1 - t), 3) * start.y + 3 * t * Math.pow((1 - t), 2) * control1.y + 3 * Math.pow(t, 2) * (1 - t) * control2.y + Math.pow(t, 3) * end.y);
}
}
最后
Demo下載地址 點這里闷祥,Demo里還有四階曲線的展示,可以看看傲诵,原理與二階三階雷同凯砍,就不貼代碼了。