贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!

一、原理

转自:http://www.2cto.com/kf/201401/275838.html

Android动画学习Demo(3) 沿着贝塞尔曲线移动的Property Animation

Property Animation中最重要,最基础的一个类就是ValueAnimator了。Property Animation利用ValueAnimator来跟踪记录对象属性已经变化了多长时间及当前这个时间点的值。

而在ValueAnimator中,又封装了两个类:

1)TimeInterpolator,也称插值器,是来计算当前动画运动的一个跟时间有关系的比例因子。

2)TypeEvaluator,这个就是利用TimeInterpolator计算出来的因子来算出当前动画运行到的位置。

这样讲太抽象了,我们还是先用自然语言来描述一下整个动画的过程吧。

动画的原理,其实就是一帧帧的画面顺着时间顺序,在我们眼中形成视觉残留的效果。所以在动画中,时间的概念是很重要的,只有时间的变化,才能形成动画效果。

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的实现机制。 下载 
嗯,这一篇文章大概就是这样了,大家如果有兴趣了解Property Animation的应用的话,可以看一下Android动画学习Demo(2) 关于Property Animation的用法及总结

最后还是要提醒一下,Property Animation是3.0以后才支持的,如果大家想在3.0之前去应用这些属性的话,可以去下载jake wharton的nineoldandroids包,基本上都可以直接将方法套上,不过据我实验,还是有某些方法,比如 PropertyValuesHolder就会有些bug出现的。我把这个包也放在这里吧,
点击NineoldAndroids下载

二、自定义贝塞尔曲线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,线性贝塞尔曲线只是一条两点之间的直线。这条线由下式给出:

二次方贝塞尔曲线

二次方贝塞尔曲线的路径由给定点P0、P1、P2的函数B(t)追踪:

三次方贝塞尔曲线

P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝塞尔曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1或P2;公式如下:

N次方贝塞尔曲线

身为三维生物超出三维我很方,这里只给示例图。想具体了解的同学请左转度娘。

就当没看过上面

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动态清理是不是就出来了呢?有兴趣的可以自己拓展下。

以一个三阶贝塞尔结束

天气预报曲线图示例

(图一)

(图二)

概述

要想得到上图的效果,需要二阶贝塞尔和三阶贝塞尔配合。具体表现为,第一段和最后一段曲线为二阶贝塞尔,中间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)为例。现在P2、P4、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。

三、使用

转自:

研究一下贝塞尔曲线.

[java] view plain copy

  1. /**
  2. * 三阶贝塞尔方程
  3. */
  4. private class BeizerEvaluator implements TypeEvaluator<PointF> {
  5. private PointF point1;
  6. private PointF point2;
  7. private PointF pointF;
  8. public BeizerEvaluator(PointF point1, PointF point2) {
  9. this.point1 = point1;
  10. this.point2 = point2;
  11. }
  12. @Override
  13. public PointF evaluate(float time, PointF start, PointF end) {
  14. float timeLeft = 1.0f - time;
  15. pointF = new PointF();//结果
  16. PointF point0 = start;//起点
  17. PointF point3 = end;//终点
  18. pointF.x = timeLeft * timeLeft * timeLeft * (point0.x)  
  19.                     + 3 * timeLeft * timeLeft * time * (point1.x)  
  20.                     + 3 * timeLeft * time * time * (point2.x)  
  21.                     + time * time * time * (point3.x);  
  22.   
  23.             pointF.y = timeLeft * timeLeft * timeLeft * (point0.y)  
  24.                     + 3 * timeLeft * timeLeft * time * (point1.y)  
  25.                     + 3 * timeLeft * time * time * (point2.y)  
  26.                     + time * time * time * (point3.y);  
  27. return pointF;
  28. }
  29. }

[java] view plain copy

  1. //初始化一个BezierEvaluator
  2. BeizerEvaluator evaluator = new BeizerEvaluator(getPointF(1), getPointF(2));  
  3. ValueAnimator animator = ValueAnimator.ofObject(evaluator, new 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. private View target;
  3. public BezierListenr(View target) {
  4. this.target = target;
  5. }
  6. @Override
  7. public void onAnimationUpdate(ValueAnimator animation) {
  8. PointF pointF = (PointF) animation.getAnimatedValue();
  9. ViewHelper.setX(target, pointF.x);
  10. ViewHelper.setY(target, pointF.y);
  11. ViewHelper.setAlpha(target, 1 - animation.getAnimatedFraction());
  12. }
  13. }

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

时间: 2024-10-16 13:29:44

贝塞尔曲线:原理、自定义贝塞尔曲线View、使用!!!的相关文章

贝塞尔曲线原理(转载)

最近在做图形学的实验,关于贝塞尔曲线在网上看到一个非常好的资料: 以下内容转载自:https://www.cnblogs.com/hnfxs/p/3148483.html 原理和简单推导(以三阶为例): 设P0.P02.P2是一条抛物线上顺序三个不同的点.过P0和P2点的两切线交于P1点,在P02点的切线交P0P1和P2P1于P01和P11,则如下比例成立: 这是所谓抛物线的三切线定理. 当P0,P2固定,引入参数t,令上述比值为t:(1-t),即有: t从0变到1,第一.二式就分别表示控制二边

Bezier曲线原理—动态解释

Bezier曲线原理 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线.一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的.贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如PhotoShop等.在Flash4中还没有完整的曲线工具,而在Flash5里面已经提供出贝塞尔曲线工具.贝塞尔曲线于19

C#GDI+自定义绘制曲线图表控件DataChart 简单实现

C#GDI+自定义绘制曲线图表控件DataChart 这里只说明在计算刻度和曲线绘制的时候 只提供思路,只是做了下简单的计算,不喜勿喷 还望见谅,高手直接飘过吧.这个要做好,还是需要研究研究算法的,比如刻度随着控件的大小发生改变的时候计算不同的值,根据刻度范围来计算刻度以及刻度值等,这里没有研究,制作简单的绘制,让其知道自定义曲线控件的画法,对于新手来讲应该是有一些帮助的.有时间我把研究过后的算法加上做一个通用可靠一点的控件分享. 例子如下图所示 首先百度一张图片,就按照它的样子来绘制 该图片链

Atitit.struts排除url&#160;的设计and&#160;原理&#160;自定义filter&#160;排除特定url

Atitit.struts排除url 的设计and 原理 自定义filter 排除特定url 1.1. 原理流程1 2. Invoke1 3. StrutsX2 1.1. 原理流程 读取struts配置xml文件内容 得到多个regexpress规则,匹配规则与uri <constant name="struts.action.excludePattern" value="/com.attilax/core/approot_js.jsp,.*\.jsp,/api.jsp

【ROC曲线】关于ROC曲线、PR曲线对于不平衡样本的不敏感性分析说引发的思考

ROC曲线 在网上有很多地方都有说ROC曲线对于正负样本比例不敏感,即正负样本比例的变化不会改变ROC曲线.但是对于PR曲线就不一样了.PR曲线会随着正负样本比例的变化而变化.但是没有一个有十分具体和严谨地对此做出过分析和论证(至少我没有找到). 此处记为结论1: 结论1:PR曲线会随着正负样本比例的变化而变化:但是ROC曲线不会. 此处我就这一问题进行了详细的分析论证,并在这个过程中引发了很多思考. 首先,如何分析这个问题呢? 看下ROC曲线是由TPR和FPR组成的 下面我们这样来分析这个问题

android自定义手势解锁View

有时候为了程序的安全性,我们经常要采取一些安全措施,就像我们常用的支付宝那样,隔一定的时间再回到应用程序时会让用户利用手势去解锁应用程序,最近由于项目需求,也要求做这样一个功能,当用户切出本应用程序15分钟后回来,让用户手势解锁,整个需求的难点就在如何实现这个手势锁,开始一点头绪也没有,没有一点思路去实现这个手势解锁功能,在google了一番后看了一篇非常好的博客后,按照博主的思路的确是可以实现一个十分不错的手势锁View,也参考了下那位大神的代码,下面是我根据他的思路和代码片段实现的一个自定义

[转]Android中自定义样式与View的构造函数中的第三个参数defStyle的意义

转自:http://www.cnblogs.com/angeldevil/p/3479431.html Android中自定义样式与View的构造函数中的第三个参数defStyle的意义 零.序 一.自定义Style 二.在XML中为属性声明属性值 1. 在layout中定义属性 2. 设置Style 3. 通过Theme指定 三.在运行时获取属性值 1. View的第三个构造函数的第三个参数defStyle 2. obtailStyledAttributes 3. Example 四.结论与代

CAD返回曲线上一点在曲线上的参数(网页版)

主要用到函数说明: IMxDrawCurve::GetParamAtPoint 返回曲线上一点在曲线上的参数,具体说明如下: 参数 说明 [in] IMxDrawPoint* point 曲线的点 [out] DOUBLE* pParam 返回曲线上的参数 js代码实现如下:     var ent = mxOcx.GetEntity("选择曲线:");     if (ent == null)         return;     var curve;     if (ent.Ob

Android翻页效果原理实现之曲线的实现

尊重原创转载请注明:From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 侵权必究! 炮兵镇楼 上一节我们通过引入折线实现了页面的折叠翻转效果,有了前面两节的基础呢其实曲线的实现可以变得非常简单,为什么这么说呢?因为曲线无非就是在折线的基础上对Path加入了曲线的实现,进而只是影响了我们的Region区域,而其他的什么事件啊.滑动计算啊之类的几乎都是不变的对吧,说白了就是对现有的折线View进行update改造,虽然是改造,但