贝塞尔曲线 - 花束直播点赞效果

作者: 你也不知道 | 来源:发表于2020-05-31 09:29 被阅读0次

    1. 效果


    花束直播点赞效果

    先说一下这种效果都用到了哪些东西:
    1.自定义View的一些基础;
    2.随机数的使用;
    3.插补器的使用;
    4.属性动画的一些高级用法
    5.贝塞尔曲线应用到属性动画

    2.分析和实现


    2.1效果实现:

    可以基本分为两个部分,具体看实现
      1.点击START ANIM按钮的时候,底部出现一个ImageView它的drawable是随机的,并伴着缩放和透明度的变化;
      2.等第一步的动画执行完后开始向上移动,移动的轨迹是一个曲线,我们要用到贝塞尔曲线公式去不断的改变图片的位置。

    2.2分步实现:

    1.自定义View继承RelativeLayout,初始化一些基本的参数:

    public class LoveLayout extends RelativeLayout {
        private Drawable mRed, mYellow, mBlue;
        private Drawable[] mDrawables;
        private Interpolator[] mInterpolators;
        private int mDrawableHeight, mDrawableWidth;
        private int mWidth, mHeight;
        private LayoutParams params;
        private Random mRandom = new Random();
    
        public LoveLayout(Context context) {
            this(context, null);
        }
    
        public LoveLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        private void init() {
            initDrawable();
            initInterpolator();
            // 初始化params
            params = new LayoutParams(mDrawableWidth, mDrawableHeight);
            // 父容器水平居中
            params.addRule(CENTER_HORIZONTAL, TRUE);
            // 父容器的底部
            params.addRule(ALIGN_PARENT_BOTTOM, TRUE);
        }
    
        /**
         * 初始化几种插补器
         */
        private void initInterpolator() {
            mInterpolators = new Interpolator[4];
            mInterpolators[0] = new LinearInterpolator();// 线性
            mInterpolators[1] = new AccelerateDecelerateInterpolator();// 先加速后减速
            mInterpolators[2] = new AccelerateInterpolator();// 加速
            mInterpolators[3] = new DecelerateInterpolator();// 减速
        }
    
        private void initDrawable() {
            mRed = getResources().getDrawable(R.drawable.pl_red);
            mYellow = getResources().getDrawable(R.drawable.pl_yellow);
            mBlue = getResources().getDrawable(R.drawable.pl_blue);
    
            mDrawables = new Drawable[3];
            mDrawables[0] = mRed;
            mDrawables[1] = mYellow;
            mDrawables[2] = mBlue;
    
            // 得到图片的实际宽高
            mDrawableWidth = mRed.getIntrinsicWidth();
            mDrawableHeight = mRed.getIntrinsicHeight();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = getMeasuredWidth();
            mHeight = getMeasuredHeight();
        }
    }
    

    2.当点击START NIMA的时候不断的往容器里面添加随机的ImageView,然后开始执行透明度的缩放的动画,这里用属性动画ObjectAnimator.ofFloat();

        /**
        * 添加点赞图片
        **/
        public void addLove() {
            final ImageView loveIv = new ImageView(getContext());
            loveIv.setImageDrawable(mDrawables[mRandom.nextInt(mDrawables.length)]);
            loveIv.setLayoutParams(params);
            addView(loveIv);
    
            // 最终的属性动画集合
            AnimatorSet finalSet = getAnimatorSet(loveIv);
            finalSet.start();
        }
    
        /**
         * 构造三个属性动画
         */
        private AnimatorSet getAnimatorSet(ImageView loveIv) {
            // 1.alpha动画
            ObjectAnimator alpha = ObjectAnimator
                    .ofFloat(loveIv, "alpha", 0.3f, 1f);
    
            // 2.缩放动画
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(loveIv, "scaleX", 0.2f,
                    1f);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(loveIv, "scaleY", 0.2f,
                    1f);
            // 刚刚进入动画集合
            AnimatorSet enter = new AnimatorSet();
            enter.setDuration(500);
            enter.playTogether(alpha, scaleX, scaleY);
            enter.setTarget(loveIv);
            return enter;
        }
    

    这里我们可以试着看看效果,是不是可以伴随着动画添加到容器底部了。

    3.利用三次方贝塞尔曲线,来不断的修改ImageView的位置,这里我们要看看这个方程,我们要继承这个估值器TypeEvaluator,至于贝塞尔是大学高数中的东西了,可以简单分析一下:
      

       贝塞尔曲线方程

    整个曲线我们可以简单的理解为这个S路曲线,S也会有四个点:
    1.P0最下面的这个起点,也就是我们刚刚添加进来最下方居中的这个点 ((mWidth - mDrawableWidth) / 2, mHeight- mDrawableHeight)
    2.P1是下半部分抛物线的顶点,这里是随机;
    3.P2是上半部分抛物线的顶点,这里也是随机;
    4.P3是最上面位置的终点(mRandom.nextInt(mWidth), 0),也就是最上面的这个点;
    5.t的范围是[0,1],我们确定这四个点之后就开始套公式了:

    // 贝塞尔估值器
    public class BezierEvaluator implements TypeEvaluator<PointF> {
        private PointF point1, point2;
    
        public BezierEvaluator(PointF pointF1, PointF pointF2) {
            this.point1 = pointF1;
            this.point2 = pointF2;
        }
    
        @Override
        public PointF evaluate(float t, PointF point0, PointF point3) {
            // t百分比, 0~1
            PointF point = new PointF();
            point.x = point0.x * (1 - t) * (1 - t) * (1 - t) //
                    + 3 * point1.x * t * (1 - t) * (1 - t)//
                    + 3 * point2.x * t * t * (1 - t)//
                    + point3.x * t * t * t;//
            
            point.y = point0.y * (1 - t) * (1 - t) * (1 - t) //
                    + 3 * point1.y * t * (1 - t) * (1 - t)//
                    + 3 * point2.y * t * t * (1 - t)//
                    + point3.y * t * t * t;//
            // 套用上面的公式把点返回
            return point;
        }
    
    }
    

    4.利用属性动画这个方法ValueAnimator.ofObject(),然后添加监听不断的改变ImageView的x,y坐标值,当然还有一些后续工作如动画执行完后将ImageView移除释放资源等等。

        /**
         * 贝塞尔曲线动画(核心,不断的修改ImageView的坐标ponintF(x,y) )
         */
        private ValueAnimator getBezierValueAnimator(final ImageView loveIv) {
            // 曲线的两个顶点
            PointF pointF2 = getPonitF(2);
            PointF pointF1 = getPonitF(1);
            // 起点位置
            PointF pointF0 = new PointF((mWidth - mDrawableWidth) / 2, mHeight
                    - mDrawableHeight);
            // 结束的位置
            PointF pointF3 = new PointF(mRandom.nextInt(mWidth), 0);
            // 估值器Evaluator,来控制view的行驶路径(不断的修改point.x,point.y)
            BezierEvaluator evaluator = new BezierEvaluator(pointF1, pointF2);
            // 属性动画不仅仅改变View的属性,还可以改变自定义的属性
            ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointF0,
                    pointF3);
            animator.addUpdateListener(new AnimatorUpdateListener() {
    
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    // 不断改变ImageView的x,y的值
                    PointF pointF = (PointF) animation.getAnimatedValue();
                    loveIv.setX(pointF.x);
                    loveIv.setY(pointF.y);
                    loveIv.setAlpha(1 - animation.getAnimatedFraction() + 0.1f);// 得到百分比
                }
            });
            animator.setTarget(loveIv);
            animator.setDuration(3000);
            return animator;
        }
    

    所有分享大纲:Android进阶之旅 - 自定义View篇

    视频讲解地址:http://pan.baidu.com/s/1nvwqaA9

    相关文章

      网友评论

        本文标题:贝塞尔曲线 - 花束直播点赞效果

        本文链接:https://www.haomeiwen.com/subject/asllzhtx.html