贝塞尔曲线(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就是我们所说的控制点,而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的方法也不多,但是我这里也就不一一讲解了,大家可以看一下以下这个博客
一言不合上代码:
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) {
}
});
}
}
运行效果: