购物车特效—贝塞尔曲线
效果图
原理
- 从添加按钮获取开始坐标
- 从购物车获取结束坐标
- 打气一个视图,添加属性动画ObjectAnimator(缩小),ValueAnimator(路线)
- 动画开始时添加一个中间视图,动画结束删除
- 运动路径使用TypeEvaluator与贝塞尔函数计算
开始撸代码
1.重写onLayout方法获取,并获取当前控件在屏幕的坐标
PointF mLocation = new PointF();
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int[] layoutLoc = new int[2];
getLocationInWindow(layoutLoc);
mLocation.set(layoutLoc[0],layoutLoc[1]);//获取当前控件在界面中的位置
}
2.需要提供一个动画方法,让该控件知道动画的开始位置,结束位置,以及在动画过程中需要展示的布局界面
/**
*开始动画方法
* @param startView 开始的控件
* @param endView 结束的控件
* @param layoutMoveId 移动过程中需要显示的界面
*/
public void startCartAnim(View startView ,View endView,int layoutMoveId){
//获取开始的位置
int[] startLoc = new int[2];
startView.getLocationInWindow(startLoc);
PointF startF = new PointF(startLoc[0],startLoc[1]);
//获取结束的位置
int[] endLoc = new int[2];
endView.getLocationInWindow(endLoc);
PointF endF = new PointF(endLoc[0],endLoc[1]);
//打气进来一个动画的view
View moveView = LayoutInflater.from(getContext()).inflate(layoutMoveId,this,false);
//开始动画 使用属性动画集合
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(2000);
//伪代码 动画还没有实现,接下来就实现这三个动画即可
animatorSet.playTogether(scalXAnim,scalYAnim,pathAnim);
animatorSet.start();
}
3.实现相应的动画
1. 缩放动画scalXAnim和scalYAnim
ObjectAnimator scalXAnim = ObjectAnimator.ofFloat(moveView,"ScaleX",1.0f,4.3f);
ObjectAnimator scalYAnim = ObjectAnimator.ofFloat(moveView,"ScaleY",1.0f,4.3f);
2. 抛物线动画,并监听动态更改moveView的位置
ValueAnimator pathAnim=ObjectAnimator.ofObject(beisaier,startF,endF);
//beisaier是估值器,用来计算moveView移动的路径。
pathAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF newPointF= (PointF) animation.getAnimatedValue();
moveView.setX(newPointF.x);
moveView.setY(newPointF.y);
}
});
估值器的实现:
//路径计算器
private TypeEvaluator<PointF> beisaier=new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//贝塞尔曲线
PointF newF=new PointF((startValue.x+endValue.x)/2,0);
return BezierCurve.bezier(fraction,startValue,newF ,endValue);
}
};
核心代码时封装好的BezierCurve工具类。用来计算moveView移动的位置。
/**
* 二次贝塞尔曲线插值
* t:值范围from = 0, to = 1
*/
public static PointF bezier( float t, PointF point0, PointF point1, PointF point2)
{
float oneMinusT = 1.0f - t;
PointF point = new PointF();
point.x = oneMinusT * oneMinusT * point0.x
+ 2 * t * oneMinusT * point1.x
+ t * t * point2.x;
point.y = oneMinusT * oneMinusT * point0.y
+ 2 * t * oneMinusT * point1.y
+ t * t * point2.y;
return point;
}
4.监听动画开始结束,用来在开始的时候添加moveView 在结束的时候移除moveView.
animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
CartAnimView.this.addView(moveView);
}
@Override
public void onAnimationEnd(Animator animation) {
CartAnimView.this.removeView(moveView);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
5.设置时间,开始浪。
animatorSet.setDuration(2000);
animatorSet.start();