貝塞爾曲線:原理、自定義貝塞爾曲線View、使用!!!

 

1、原理html

轉自:http://www.2cto.com/kf/201401/275838.htmljava

Android動畫學習Demo(3) 沿着貝塞爾曲線移動的Property Animationandroid

Property Animation中最重要,最基礎的一個類就是ValueAnimator了。Property Animation利用ValueAnimator來跟蹤記錄對象屬性已經變化了多長時間及當前這個時間點的值。git

而在ValueAnimator中,又封裝了兩個類:github

1)TimeInterpolator,也稱插值器,是來計算當前動畫運動的一個跟時間有關係的比例因子。算法

2)TypeEvaluator,這個就是利用TimeInterpolator計算出來的因子來算出當前動畫運行到的位置。canvas

這樣講太抽象了,咱們仍是先用天然語言來描述一下整個動畫的過程吧。ide

動畫的原理,其實就是一幀幀的畫面順着時間順序,在咱們眼中造成視覺殘留的效果。因此在動畫中,時間的概念是很重要的,只有時間的變化,才能造成動畫效果。函數

0)動畫準備開始,咱們在這裏設置了一個動畫的時長(duration),若是不設置的話,動畫的時長就是300毫秒,每一個畫面顯示的時間是10ms。同時也設置了某個屬性值在這個時間段中變化的起始值start和結束值end,意思就是說,在duration時間中,屬性值要從start 變化到 end。佈局

1)動畫開始了,過了 t 時間,ValueAnimator會根據 t / duration 算出一個時間消逝的比例因子(elapsed fraction),意思就是說,如今時間到 t了,咱們假設總的時間的duration就是3t吧,那就是如今已通過了1/3時間了,那這個屬性值也應該要變化到1/3了。

2)動畫繼續,如今到了2t了,那麼如今動畫時間已通過了2/3了,那麼這個屬性值是否是已經變化到2/3了呢。

3)如今到了3t了,動畫結束了,屬性值就已經從start變成end值了。

那麼如今問題來了,若是都是這樣算的話,那動畫不就一直是很勻速的了嗎?是的,若是用的是LinearInterpolator的話。

TimeInterpolator

TimeInterpolator就是用來改變咱們這個動畫速度的這樣一個類了。爲何叫插值器呢?我理解就是,原本動畫踩着時間點,一步一步走的挺好的,它硬生生在中間的插了些值進去,或者抽了一些值出去,讓整條路變得很差走了,前面變然變上坡了,走起來就慢了,原本過去 t 時間以後,動畫的畫面也應該在1/3的位置了,可是路很差走,它就走不到1/3,而可能只走了1/4了,然後面是下坡,一激動,步伐就快了許多,又遇上去了,可是無論中間的路怎麼變化,時間點一到,必定是剛恰好落在最終的位置上的。 Android中提供的Interpolator主要有九個: 1)AccelerateDecelerateInterpolator:先加速再減速。
2)AccelerateInterpolator:一直加速。
3)AnticipateInterpolator:先日後一下,再嗖的一聲勇往直前。
4)AnticipateOvershootInterpolator:先日後一下,再一直往前超過終點,再往回收一下。
5)BounceInterpolator:最後像個小球彈幾下。
6)CycleInterpolator:重複幾回,感受就是環形進度條那種,具體我還沒試過。
7)DecelerateInterpolator:一直減速。
8)LinearInterpolator:線性,這個就是咱們上面講到的很均勻的了。
9)OvershootInterpolator:到了終點以後,超過一點,再往回走。有個參數能夠定義,超過的力度。 
這些Interpolator都是實現了TimeInterpolator接口的類,它們只須要實現一個方法:getInterpolation (float input),將這個input根據本身的需求從新計算這個比例 
第一步:當到了某時間t以後,ValueAnimator會算出某個比例 fraction = t / duration,而Interpolator會接收這個比例fraction,再調用其getInterpolation方法將這個比例因子從新計算一下,返回一個新的比例因子,好比LinearInterpolator實現的方法就是什麼都不變,以下:

?
1
2
3
public float getInterpolation( float input) {
     return input;
}

而 AccelerateDecelerateInterpolator 則會利用餘弦函數的對稱性變化計算這個比例因子,以下:

?
1
2
3
public float getInterpolation( float input) {
     return ( float )(Math.cos((input + 1 ) * Math.PI) / 2 .0f) + 0 .5f;
}


如上所述,經過第一步 Interpolator 的插值,咱們會獲得一個比例因子,接下來就是要用到咱們的TypeEvaluator了。 

TypeEvaluator

第二步:TypeEvaluator會接受第一步中算出來的比例因子,而後算出當前的屬性的值,將其返回給ValuaAnimator,由ValueAnimator去設置對應屬性的值。 好比,我本身寫了一個BezierTypeEvaluator,根據時間的變化來讓一個按鈕沿着貝塞爾曲線移動,以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class BezierEvaluator implements TypeEvaluator<pointf>{
 
         @Override
         public PointF evaluate( float fraction, PointF startValue,
                 PointF endValue) {
             final float t = fraction;
             float oneMinusT = 1 .0f - t;
             PointF point = new PointF();
             
             PointF point0 = (PointF)startValue;
             
             PointF point1 = new PointF();
             point1.set(width, 0 );
             
             PointF point2 = new PointF();
             point2.set( 0 , height);
             
             PointF point3 = (PointF)endValue;
             
             point.x = oneMinusT * oneMinusT * oneMinusT * (point0.x)
                    + 3 * oneMinusT * oneMinusT * t * (point1.x)
                    + 3 * oneMinusT * t * t * (point2.x)
                    + t * t * t * (point3.x);
             
            point.y = oneMinusT * oneMinusT * oneMinusT * (point0.y)
                    + 3 * oneMinusT * oneMinusT * t * (point1.y)
                    + 3 * oneMinusT * t * t * (point2.y)
                     + t * t * t * (point3.y);          
             return point;
         }  
     }</pointf>


自定義TypeEvaluator,咱們必須實現其evaluate方法,目的就是計算出目前的對象對應屬性的值,而它會接收三個參數,一個是上文中經過interpolator算出的比例,還有咱們在建立動畫時設置的起始值和結束值。 

ValueAnimator.AnimatorUpdateListener

既然咱們已經算出了在 t 時刻,對象的某個屬性的值,那麼咱們要把這個值從新設置到對象中,纔可以起做用啊。因此ValueAnimator也提供了一個內部的Listener接口,其只有一個方法,就是獲取TypeEvaluator計算出來的值,並設置給對應的屬性,好比咱們Demo中的代碼:

?
1
2
3
4
5
6
7
8
valueAnimator.addUpdateListener( new AnimatorUpdateListener() {         
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 PointF pointF = (PointF)animation.getAnimatedValue();
                 button.setX(pointF.x);
                 button.setY(pointF.y);
             }
         });


咱們在這裏改變Button的X座標和Y座標,從而改變其具體的位置。至於validate,而後引發從新繪製的過程,對於這些基本的屬性,ValueAnimator已經幫咱們實現了。 下面,咱們看看效果圖,而後再總結一下ValueAnimator的實現機制。 n峨糙鋙
mv賁�*'~稖�*'妠^畾%j�+~稖�*'5�?糙鋙-畨噘r亙溩癘*^謦公h痬9鉌f瓂語栫坎阼�-畨噍┾櫕^uVス�'奻�)u玘.+-zwNx欲-畨鄋硾嶇-x�+x�+R梈碘獠拽z醬鐛n7湸	鈾玥畾瑗覯戎騶y薂�)毝婇畩^峧g瑉荽鐛n7湸	鈾玥�黔n�符稾符穐符穖9鉓誮[�x�+n丁下載 
嗯,這一篇文章大概就是這樣了,你們若是有興趣瞭解Property Animation的應用的話,能夠看一下Android動畫學習Demo(2) 關於Property Animation的用法及總結

最後仍是要提醒一下,Property Animation是3.0之後才支持的,若是你們想在3.0以前去應用這些屬性的話,能夠去下載jake wharton的nineoldandroids包,基本上均可以直接將方法套上,不過據我實驗,仍是有某些方法,好比 PropertyValuesHolder就會有些bug出現的。我把這個包也放在這裏吧,
點擊NineoldAndroids下載

 

 

2、自定義貝塞爾曲線View

轉自:http://www.2cto.com/kf/201604/497130.html

Android 自定義View高級特效,神奇的貝塞爾曲線

效果圖

效果圖

效果圖中咱們實現了一個簡單的隨手指滑動的二階貝塞爾曲線,還有一個複雜點的,穿越全部已知點的貝塞爾曲線。學會使用貝塞爾曲線後能夠實現例如QQ紅點滑動刪除啦,360動態球啦,bulabulabula~

什麼是貝塞爾曲線?

貝賽爾曲線(Bézier曲線)是電腦圖形學中至關重要的參數曲線。更高維度的普遍化貝塞爾曲線就稱做貝塞爾曲面,其中貝塞爾三角是一種特殊的實例。貝塞爾曲線於1962年,由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所普遍發表,他運用貝塞爾曲線來爲汽車的主體進行設計。貝塞爾曲線最初由Paul de Casteljau於1959年運用de Casteljau算法開發,以穩定數值的方法求出貝塞爾曲線。

讀完上述貝塞爾曲線簡介我仍是一頭霧水,來個示例唄。

示例

線性貝塞爾曲線

給定點P0、P1,線性貝塞爾曲線只是一條兩點之間的直線。這條線由下式給出:
1
1

二次方貝塞爾曲線

二次方貝塞爾曲線的路徑由給定點P0、P一、P2的函數B(t)追蹤:
2
22

三次方貝塞爾曲線

P0、P一、P二、P3四個點在平面或在三維空間中定義了三次方貝塞爾曲線。曲線起始於P0走向P1,並從P2的方向來到P3。通常不會通過P1或P2;公式以下:
3
33

N次方貝塞爾曲線

身爲三維生物超出三維我很方,這裏只給示例圖。想具體瞭解的同窗請左轉度娘。
44

就當沒看過上面

Android在API=1的時候就提供了貝塞爾曲線的畫法,只是隱藏在Path#quadTo()和Path#cubicTo()方法中,一個是二階貝塞爾曲線,一個是三階貝塞爾曲線。固然,若是你想本身寫個方法,依照上面貝塞爾的表達式也是能夠的。不過通常沒有必要,由於Android已經在native層爲咱們封裝好了二階和三階的函數。

從一個二階貝塞爾開始

自定義一個BezierView

初始化各個參數,花3s掃一下便可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<code class = "hljs java" >    private Paint mPaint;
     private Path mPath;
     private Point startPoint;
     private Point endPoint;
     // 輔助點
     private Point assistPoint;
         public BezierView(Context context) {
         this (context, null );
     }
 
     public BezierView(Context context, AttributeSet attrs) {
         this (context, attrs, 0 );
     }
 
     public BezierView(Context context, AttributeSet attrs, int defStyleAttr) {
         super (context, attrs, defStyleAttr);
         init(context);
     }
 
     private void init(Context context) {
         mPaint = new Paint();
         mPath = new Path();
         startPoint = new Point( 300 , 600 );
         endPoint = new Point( 900 , 600 );
         assistPoint = new Point( 600 , 900 );
         // 抗鋸齒
         mPaint.setAntiAlias( true );
         // 防抖動
         mPaint.setDither( true );
     }</code>

onDraw中畫二階貝塞爾

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class = "hljs avrasm" >        // 畫筆顏色
         mPaint.setColor(Color.BLACK);
         // 筆寬
         mPaint.setStrokeWidth(POINTWIDTH);
         // 空心
         mPaint.setStyle(Paint.Style.STROKE);
         // 重置路徑
         mPath.reset();
         // 起點
         mPath.moveTo(startPoint.x, startPoint.y);
         // 重要的就是這句
         mPath.quadTo(assistPoint.x, assistPoint.y, endPoint.x, endPoint.y);
         // 畫路徑
         canvas.drawPath(mPath, mPaint);
         // 畫輔助點
         canvas.drawPoint(assistPoint.x, assistPoint.y, mPaint);</code>

上面註釋很清晰就不贅述了。示例中貝塞爾是能夠跟着手指的滑動而變化,我一拍榴蓮,確定是複寫了onTouchEvent()!

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code class = "hljs cs" >    @Override
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
                 assistPoint.x = ( int ) event.getX();
                 assistPoint.y = ( int ) event.getY();
                 Log.i(TAG, "assistPoint.x = " + assistPoint.x);
                 Log.i(TAG, "assistPoint.Y = " + assistPoint.y);
                 invalidate();
                 break ;
         }
         return true ;
     }</code>

最後將咱們自定義的BezierView添加到佈局文件中。至此一個簡單的二階貝塞爾曲線就完成了。假設一下,在向下拉動的過程當中,在曲線上增長一個「小超人」,360動態清理是否是就出來了呢?有興趣的能夠本身拓展下。

以一個三階貝塞爾結束

天氣預報曲線圖示例

(圖一)
DEMO1
(圖二)
demo2

概述

要想獲得上圖的效果,須要二階貝塞爾和三階貝塞爾配合。具體表現爲,第一段和最後一段曲線爲二階貝塞爾,中間N段都爲三階貝塞爾曲線。

思路

先根據相鄰點(P1,P2, P3)計算出相鄰點的中點(P4, P5),而後再計算相鄰中點的中點(P6)。而後將(P4,P6, P5)組成的線段平移到通過P2的直線(P8,P2,P7)上。接着根據(P4,P6,P5,P2)的座標計算出(P7,P8)的座標。最後根據P7,P8等控制點畫出三階貝塞爾曲線。

點和線的解釋

黑色點:要通過的點,例如溫度 藍色點:兩個黑色點構成線段的中點 黃色點:兩個藍色點構成線段的中點 灰色點:貝塞爾曲線的控制點 紅色線:黑色點的折線圖 黑色線:黑色點的貝塞爾曲線,也是咱們最終想要的效果

聲明

爲了方便講解以及讀者的理解。本篇以圖一效果爲例進行講解。BezierView座標都是根據屏幕動態生成的,想要圖二的效果只需修改初始座標,不用對代碼作很大的修改便可實現。

那麼,開始吧!

初始化參數

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<code class = "hljs java" >    private static final String TAG = "BIZIER" ;
     private static final int LINEWIDTH = 5 ;
     private static final int POINTWIDTH = 10 ;
 
     private Context mContext;
     /** 即將要穿越的點集合 */
     private List<point> mPoints = new ArrayList<>();
     /** 中點集合 */
     private List<point> mMidPoints = new ArrayList<>();
     /** 中點的中點集合 */
     private List<point> mMidMidPoints = new ArrayList<>();
     /** 移動後的點集合(控制點) */
     private List<point> mControlPoints = new ArrayList<>();
 
     private int mScreenWidth;
     private int mScreenHeight;
     private void init(Context context) {
         mPaint = new Paint();
         mPath = new Path();
         // 抗鋸齒
         mPaint.setAntiAlias( true );
         // 防抖動
         mPaint.setDither( true );
 
         mContext = context;
         getScreenParams();
         initPoints();
         initMidPoints( this .mPoints);
         initMidMidPoints( this .mMidPoints);
         initControlPoints( this .mPoints, this .mMidPoints , this .mMidMidPoints);
 
     }</point></point></point></point></code>

第一個函數獲取屏幕寬高就不說了。緊接着初始化了初始點、中點、中點的中點、控制點。咱們一個個的跟進。首先是初始點。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<code class = "hljs java" >    /** 添加即將要穿越的點 */
     private void initPoints() {
         int pointWidthSpace = mScreenWidth / 5 ;
         int pointHeightSpace = 100 ;
         for ( int i = 0 ; i < 5 ; i++) {
             Point point;
             // 一高一低五個點
             if (i% 2 != 0 ) {
                 point = new Point(( int ) (pointWidthSpace*(i + 0.5 )), mScreenHeight/ 2 - pointHeightSpace);
             } else {
                 point = new Point(( int ) (pointWidthSpace*(i + 0.5 )), mScreenHeight/ 2 );
             }
             mPoints.add(point);
         }
     }</code>

這裏循環建立了一高一低五個點,並添加到List mPoints中。上文說道圖一到圖二隻需修改這裏的初始點便可。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<code class = "hljs java" >    /** 初始化中點集合 */
     private void initMidPoints(List<point> points) {
         for ( int i = 0 ; i < points.size(); i++) {
             Point midPoint = null ;
             if (i == points.size()- 1 ){
                 return ;
             } else {
                 midPoint = new Point((points.get(i).x + points.get(i + 1 ).x)/ 2 , (points.get(i).y + points.get(i + 1 ).y)/ 2 );
             }
             mMidPoints.add(midPoint);
         }
     }
 
     /** 初始化中點的中點集合 */
     private void initMidMidPoints(List<point> midPoints){
         for ( int i = 0 ; i < midPoints.size(); i++) {
             Point midMidPoint = null ;
             if (i == midPoints.size()- 1 ){
                 return ;
             } else {
                 midMidPoint = new Point((midPoints.get(i).x + midPoints.get(i + 1 ).x)/ 2 , (midPoints.get(i).y + midPoints.get(i + 1 ).y)/ 2 );
             }
             mMidMidPoints.add(midMidPoint);
         }
     }</point></point></code>

這裏算出中點集合以及中點的中點集合,小學數學題沒什麼好說的。惟一須要注意的是他們數量的差異。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code class = "hljs avrasm" >    /** 初始化控制點集合 */
     private void initControlPoints(List<point> points, List<point> midPoints, List<point> midMidPoints){
         for ( int i = 0 ; i < points.size(); i ++){
             if (i == 0 || i == points.size()- 1 ){
                 continue ;
             } else {
                 Point before = new Point();
                 Point after = new Point();
                 before.x = points.get(i).x - midMidPoints.get(i - 1 ).x + midPoints.get(i - 1 ).x;
                 before.y = points.get(i).y - midMidPoints.get(i - 1 ).y + midPoints.get(i - 1 ).y;
                 after.x = points.get(i).x - midMidPoints.get(i - 1 ).x + midPoints.get(i).x;
                 after.y = points.get(i).y - midMidPoints.get(i - 1 ).y + midPoints.get(i).y;
                 mControlPoints.add(before);
                 mControlPoints.add(after);
             }
         }
     }</point></point></point></code>

你們須要注意下這個方法的計算過程。以圖一(P2,P4, P6,P8)爲例。如今P二、P四、P6的座標是已知的。根據因爲(P8, P2)線段由(P4, P6)線段平移而來,因此可得以下結論:P2 - P6 = P8 - P4 。即P8 = P2 - P6 + P4。其他同理。

畫輔助點以及對比折線圖

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<code class = "hljs mel" >    @Override
     protected void onDraw(Canvas canvas) {
         super .onDraw(canvas);
         // ***********************************************************
         // ************* 貝塞爾進階--曲滑穿越已知點 **********************
         // ***********************************************************
 
         // 畫原始點
         drawPoints(canvas);
         // 畫穿越原始點的折線
         drawCrossPointsBrokenLine(canvas);
         // 畫中間點
         drawMidPoints(canvas);
         // 畫中間點的中間點
         drawMidMidPoints(canvas);
         // 畫控制點
         drawControlPoints(canvas);
         // 畫貝塞爾曲線
         drawBezier(canvas);
 
     }</code>

能夠看到,在畫貝塞爾曲線以前咱們畫了一系列的輔助點,還有和貝塞爾曲線做對比的折線圖。效果如圖一。輔助點的座標全都獲得了,基本的畫畫就比較簡單了。有能力的可跳過下面這段,直接進入drawBezier(canvas)方法。基本的畫畫這裏只貼代碼,若有疑問可評論或者私信。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<code class = "hljs java" >    /** 畫原始點 */
     private void drawPoints(Canvas canvas) {
         mPaint.setStrokeWidth(POINTWIDTH);
         for ( int i = 0 ; i < mPoints.size(); i++) {
             canvas.drawPoint(mPoints.get(i).x, mPoints.get(i).y, mPaint);
         }
     }
 
     /** 畫穿越原始點的折線 */
     private void drawCrossPointsBrokenLine(Canvas canvas) {
         mPaint.setStrokeWidth(LINEWIDTH);
         mPaint.setColor(Color.RED);
         // 重置路徑
         mPath.reset();
         // 畫穿越原始點的折線
         mPath.moveTo(mPoints.get( 0 ).x, mPoints.get( 0 ).y);
         for ( int i = 0 ; i < mPoints.size(); i++) {
             mPath.lineTo(mPoints.get(i).x, mPoints.get(i).y);
         }
         canvas.drawPath(mPath, mPaint);
     }
 
     /** 畫中間點 */
     private void drawMidPoints(Canvas canvas) {
         mPaint.setStrokeWidth(POINTWIDTH);
         mPaint.setColor(Color.BLUE);
         for ( int i = 0 ; i < mMidPoints.size(); i++) {
             canvas.drawPoint(mMidPoints.get(i).x, mMidPoints.get(i).y, mPaint);
         }
     }
 
     /** 畫中間點的中間點 */
     private void drawMidMidPoints(Canvas canvas) {
         mPaint.setColor(Color.YELLOW);
         for ( int i = 0 ; i < mMidMidPoints.size(); i++) {
             canvas.drawPoint(mMidMidPoints.get(i).x, mMidMidPoints.get(i).y, mPaint);
         }
 
     }
 
     /** 畫控制點 */
     private void drawControlPoints(Canvas canvas) {
         mPaint.setColor(Color.GRAY);
         // 畫控制點
         for ( int i = 0 ; i < mControlPoints.size(); i++) {
             canvas.drawPoint(mControlPoints.get(i).x, mControlPoints.get(i).y, mPaint);
         }
     }
</code>

畫貝塞爾曲線

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<code class = "hljs avrasm" >    /** 畫貝塞爾曲線 */
     private void drawBezier(Canvas canvas) {
         mPaint.setStrokeWidth(LINEWIDTH);
         mPaint.setColor(Color.BLACK);
         // 重置路徑
         mPath.reset();
         for ( int i = 0 ; i < mPoints.size(); i++){
             if (i == 0 ){ // 第一條爲二階貝塞爾
                 mPath.moveTo(mPoints.get(i).x, mPoints.get(i).y); // 起點
                 mPath.quadTo(mControlPoints.get(i).x, mControlPoints.get(i).y, // 控制點
                         mPoints.get(i + 1 ).x,mPoints.get(i + 1 ).y);
             } else if (i < mPoints.size() - 2 ){ // 三階貝塞爾
                 mPath.cubicTo(mControlPoints.get( 2 *i- 1 ).x,mControlPoints.get( 2 *i- 1 ).y, // 控制點
                         mControlPoints.get( 2 *i).x,mControlPoints.get( 2 *i).y, // 控制點
                         mPoints.get(i+ 1 ).x,mPoints.get(i+ 1 ).y); // 終點
             } else if (i == mPoints.size() - 2 ){ // 最後一條爲二階貝塞爾
                 mPath.moveTo(mPoints.get(i).x, mPoints.get(i).y); // 起點
                 mPath.quadTo(mControlPoints.get(mControlPoints.size()- 1 ).x,mControlPoints.get(mControlPoints.size()- 1 ).y,
                         mPoints.get(i+ 1 ).x,mPoints.get(i+ 1 ).y); // 終點
             }
         }
         canvas.drawPath(mPath,mPaint);
     }
</code>

註釋太詳細,都沒什麼好寫的了。不過這裏須要注意判斷裏面的條件,對起點和終點的判斷必定要理解。要否則極可能會送你一個ArrayIndexOutOfBoundsException。

結束

貝塞爾曲線能夠實現不少絢麗的效果,難的不是貝塞爾,而是good idea。

 

 

3、使用

轉自:

研究一下貝塞爾曲線.

 

[java]  view plain  copy
 
  1. /** 
  2.      * 三階貝塞爾方程 
  3.      */  
  4.     private class BeizerEvaluator implements TypeEvaluator<PointF> {  
  5.   
  6.         private PointF point1;  
  7.         private PointF point2;  
  8.   
  9.         private PointF pointF;  
  10.   
  11.         public BeizerEvaluator(PointF point1, PointF point2) {  
  12.             this.point1 = point1;  
  13.             this.point2 = point2;  
  14.         }  
  15.   
  16.         @Override  
  17.         public PointF evaluate(float time, PointF start, PointF end) {  
  18.             float timeLeft = 1.0f - time;  
  19.             pointF = new PointF();//結果  
  20.   
  21.             PointF point0 = start;//起點  
  22.   
  23.             PointF point3 = end;//終點  
  24.             pointF.x = timeLeft * timeLeft * timeLeft * (point0.x)  
  25.                     + 3 * timeLeft * timeLeft * time * (point1.x)  
  26.                     + 3 * timeLeft * time * time * (point2.x)  
  27.                     + time * time * time * (point3.x);  
  28.   
  29.             pointF.y = timeLeft * timeLeft * timeLeft * (point0.y)  
  30.                     + 3 * timeLeft * timeLeft * time * (point1.y)  
  31.                     + 3 * timeLeft * time * time * (point2.y)  
  32.                     + time * time * time * (point3.y);  
  33.             return pointF;  
  34.         }  
  35.     }  

 

 

 

[java]  view plain  copy
 
  1. //初始化一個BezierEvaluator  
  2.         BeizerEvaluator evaluator = new BeizerEvaluator(getPointF(1), getPointF(2));  
  3.         ValueAnimator animator = ValueAnimator.ofObject(evaluatornew PointF(rand.nextInt(getWidth()), 0), new PointF(rand.nextInt(getWidth()), mHeight - dHeight));//隨機  
  4.         animator.addUpdateListener(new BezierListenr(tag));  
  5.         animator.setInterpolator(interpolators[rand.nextInt(3)]);  
  6.         animator.setTarget(tag);  
  7.         animator.setDuration(3000);  



 


而後在須要更新的時候去Update設置imageVIew的路徑:

 

[java]  view plain  copy
 
  1. private class BezierListenr implements ValueAnimator.AnimatorUpdateListener {  
  2.   
  3.         private View target;  
  4.   
  5.         public BezierListenr(View target) {  
  6.             this.target = target;  
  7.         }  
  8.   
  9.         @Override  
  10.         public void onAnimationUpdate(ValueAnimator animation) {  
  11.             PointF pointF = (PointF) animation.getAnimatedValue();  
  12.             ViewHelper.setX(target, pointF.x);  
  13.             ViewHelper.setY(target, pointF.y);  
  14.             ViewHelper.setAlpha(target, 1 - animation.getAnimatedFraction());  
  15.         }  
  16.     }  

 

GitHub:https://github.com/q422013/BezierFlower

相關文章
相關標籤/搜索