利用贝塞尔曲线实现动画

   贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。你可以认为他就是我们数学上认识的各种曲线。利用贝塞尔曲线来实现动画,就是利用贝塞尔曲线来作为动画运行的轨迹。
   实现贝塞尔曲线少不了要用到Path类,我们认识Path类,主要从moveTo、lineTo、quadTo三个方法开始,当然Path肯定不止这三个主要方法,但是一般我们只需要这三个方法就能实现我们要的动画,如果你想对Path做更多了解,可以自己私下看看。
1、moveTo(x,y);
该方法用于确定画笔的起点位置, 我们知道(0,0)是作为默认起点的,但是moveTo不光是确定起点,他还起着另一个作用,先不说它是什么作用,moveTo这里留个悬念,等看完lineTo和quadTo,在看看他的作用。

2、lineTo(x,y)
该方法启动连线的作用,即从当前位置连接到(x,y)的位置。

3、quadTo(x0,y0,x1,y1)二次元贝塞尔曲线;
其中(x0,y0)为控制点的坐标,而(x1,y1)为结束点的坐标。
也许你不好理解什么是控制点,咱上一波图帮助理解一下:

如图所示p1就是所谓的控制点

如图所示:p1就是我们所说的控制点,而p0就是当前位置(x0,y0),而p2就是这条曲线的重点,也就是p1作为控制点的终点,其实p0p1和p1p2这两条线就是我们知道的从p1位置作曲线p0p2的两条切线。

补充:quadTo(x0,y0,x1,y1,x2,y2)三次元贝塞尔曲线,这三次贝塞尔曲线中(x0,y0)和(x1,y1)就是该三次曲线的两个控制点。咱在上一波图:

这里写图片描述

现在来解释一下之前讲解moveTo方法留下的坑了。
上代码:
第一种:未使用moveTo(x0,y0);
int width = getWidth();
int height = getHeight();
mPath.lineTo(50, 50);    //默认起始点为(0,0)
mPath.quadTo(width - 5, 0, height - 5, height);
canvas.drawPath(mPath, mPaint);

如图

第一种:使用moveTo(x0,y0);
int width = getWidth();
int height = getHeight();
moveTo(0,0)
mPath.lineTo(50, 50);    //默认起始点为(0,0)
mPath.quadTo(width - 5, 0, height - 5, height);
canvas.drawPath(mPath, mPaint);

这里写图片描述

关于Path类已经有了一个大概认识,但是关于实现动画的一个关键类,我们还需要认识一下PathMeasure,这个类就是测量曲线的
构造方法
PathMeasure()   创建一个空的PathMeasure
PathMeasure(Path path, boolean forceClosed) 创建 PathMeasure 并关联一个指定的Path(Path需要已经创建完成)。

常用方法:
void    setPath(Path path, boolean forceClosed)   关联一个Path
boolean isClosed()   是否闭合
float   getLength()  获取Path的长度
boolean nextContour()    跳转到下一个轮廓
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)     截取片段
boolean getPosTan(float distance, float[] pos, float[] tan)   获取指定长度的位置坐标及该点切线值
boolean getMatrix(float distance, Matrix matrix, int flags)   获取指定长度的位置坐标及该点Matrix
PathMeasure的方法也不多,但是我这里也就不一一讲解了,大家可以看一下以下这个博客

讲述动画实现的关键类PathMeasure(详细讲述)

一言不合上代码:
public class TestActivity extends Activity {

    private RelativeLayout contentRl;
    private ImageView basketball;
    private ImageView basket;
    private Rect ball_rect;
    private Rect ket_rect;
    private float[] mCurrentPosition = new float[2];

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.newmain);
        contentRl = (RelativeLayout) findViewById(R.id.main_rl);
        basketball = (ImageView) findViewById(R.id.basketball);
        basket = (ImageView) findViewById(R.id.basket_ket);
        ball_rect = new Rect();
        ket_rect = new Rect();
    }

    public void onStartThrow(View view) {
        //*getGlobalVisibleRect方法的作用是获取视图在屏幕坐标中的可视区域* 该方法的调用最好在onResume方法之后调用,如果在onCreate()方法中调用,获取的值为空(0,0,0,0)这是因为view的onMeasure方法是在onResume方法调用之后才开始调用的,而onCreate方法中的setContentView(ResId)方法并没有实现view测量。
        basketball.getGlobalVisibleRect(ball_rect);   //获取控件当前位置,类似于
        basket.getGlobalVisibleRect(ket_rect);    
        final ImageView goods = new ImageView(this);
        goods.setImageResource(R.mipmap.baseketball);
        int width = (int) (getResources().getDisplayMetrics().density * 50);
        int height = (int) (getResources().getDisplayMetrics().density * 50);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
        contentRl.addView(goods, params);
        Path path = new Path();

        int startX = ball_rect.left;
        int startY = ball_rect.left;

        int endX = ket_rect.left + basket.getWidth() / 2 - basketball.getWidth() / 2;
        int endY = ket_rect.top + basket.getHeight() / 2 - basketball.getHeight() / 2;

        path.moveTo(startX, startY);
        path.quadTo(endX, startY, endX, endY);
        final PathMeasure pathMeasure = new PathMeasure(path, false);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
        valueAnimator.setDuration(1500);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float value = (float) valueAnimator.getAnimatedValue();
                pathMeasure.getPosTan(value, mCurrentPosition, null);
                goods.setTranslationX(mCurrentPosition[0]);
                goods.setTranslationY(mCurrentPosition[1]);
            }
        });
        valueAnimator.start();
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {

            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
    }
}

运行效果:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_35920289/article/details/77891614