首页 > 代码库 > 购物车特效-贝塞尔曲线动画(点击添加按钮的进候,产生抛物线动画效果)

购物车特效-贝塞尔曲线动画(点击添加按钮的进候,产生抛物线动画效果)

demo效果:

技术分享



购物车特效原理:

1.从添加按钮获取开始坐标

2.从购物车图标获取结束坐标

3.打气一个视图,添加属性动画ObjectAnimator(缩小),ValueAnimator(路线)

4.动画开始时添加该视图,动画结束删除该视图

5.运动路径使用TypeEvaluator与贝塞尔函数计算


activity布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--贝塞尔曲线动画自定义控件-->
    <custom.BezierAnim
        android:id="@+id/bezier_anim"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!--添加按钮-->
    <Button
        android:id="@+id/bt_good"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_margin="10dp"
        android:text="+" />
    <!--购物车-->
    <ImageView
        android:id="@+id/iv_cart"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:src=http://www.mamicode.com/"@drawable/cart91" />>


移动控件布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="50dp"
    android:layout_height="50dp"
    android:orientation="vertical">

    <ImageView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src=http://www.mamicode.com/"@drawable/coin91" />>

贝塞尔曲线动画的自定义控件编写:

public class BezierAnim extends FrameLayout {
    public BezierAnim(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public BezierAnim(Context context) {
        this(context, null, 0);
    }

    public BezierAnim(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    //PointF等价于float[]数组,里面存放的x和y的值 (point相当于int[])
    private 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]);//将父布局左上角的值赋值给mLocation
    }

    /**
     * 开始贝塞尔动画
     *
     * @param startView    动画从哪个view开始(+号)
     * @param endView      动画在哪个view结束(购物车)
     * @param layoutIdMove 动画作用的移动控件(钱袋子的布局)
     */
    public void startCartAnim(View startView, View endView, int layoutIdMove) {
        //1,开始位置
        int[] startLoc = new int[2];
        startView.getLocationInWindow(startLoc);//获取当前view在屏幕上的坐标
        PointF startF = new PointF(startLoc[0] - mLocation.x, startLoc[1] - mLocation.y);//得到当前view相对于父布局左上角位置的坐标
        // 2,结束位置
        int[] endLoc = new int[2];
        endView.getLocationInWindow(endLoc);
        final PointF endF = new PointF(endLoc[0] - mLocation.x, endLoc[1] - mLocation.y);
        //3.移动控件。inflate()参数:作用布局,参考布局,false
        final View moveView = LayoutInflater.from(getContext()).inflate(layoutIdMove, this, false);
        //开始动画  使用属性动画合集
        AnimatorSet set = new AnimatorSet();
        //缩小动画
        ObjectAnimator scaleXAnim = ObjectAnimator.ofFloat(moveView, "scaleX", 1.0f, 0.1f);
        ObjectAnimator scaleYAnim = ObjectAnimator.ofFloat(moveView, "scaleY", 1.0f, 0.1f);
        //路径动画(baisaier曲线路径,开始坐标,结束坐标)
        ValueAnimator pathAnim = ObjectAnimator.ofObject(beisaier, startF, endF);
        pathAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //更新坐标
                PointF newPointF = (PointF) animation.getAnimatedValue();
                moveView.setX(newPointF.x);
                moveView.setY(newPointF.y);
            }
        });
        //将这些动画放入集合中
        set.playTogether(scaleXAnim, scaleYAnim, pathAnim);
        Animator.AnimatorListener listener = new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                BezierAnim.this.addView(moveView);//加入动画作用的控件
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                BezierAnim.this.removeView(moveView);//移除动画作用的控件
            }

            @Override
            public void onAnimationCancel(Animator animation) {}

            @Override
            public void onAnimationRepeat(Animator animation) {}
        };
        set.addListener(listener);//动画播放监听器
        set.setDuration(1000);  //运动时间
        set.start();            //时间
    }

    //路径计算器
    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);
        }
    };
}


路径计算器:

public class BezierCurve {
    /**
     * 二次贝塞尔曲线插值
     * 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;
    }

    /**
     * 三次贝塞尔曲线插值
     * t:值范围from = 0, to = 1
     */
    public static PointF bezier(float t, PointF point0, PointF point1, PointF point2, PointF point3) {
        float oneMinusT = 1.0f - t;
        PointF point = new PointF();
        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;
    }
}

activity开始动画:

public class CartBazierActivity extends Activity {
    @InjectView(R.id.bt_good)
    Button btGood;
    @InjectView(R.id.iv_cart)
    ImageView ivCart;
    @InjectView(R.id.bezier_anim)
    BezierAnim bezierAnim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cart_bezier);
        ButterKnife.inject(this);
    }

    @OnClick(R.id.bt_good)
    public void bezierMove() {
        Toast.makeText(this, "添加了一件商品", Toast.LENGTH_SHORT).show();
        bezierAnim.startCartAnim(btGood, ivCart, R.layout.moveview);
    }
}

若是startView和endView不在一个界面,可以用EventBus传值


更多:http://www.jb51.net/article/95991.htm



购物车特效-贝塞尔曲线动画(点击添加按钮的进候,产生抛物线动画效果)