仿花束直播点赞效果

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangwo1991/article/details/79437546

这是一个利用贝塞尔曲线实现的仿花束直播的点赞效果,实现该效果涉及到:

1、Random随机数的使用;

2、ObjectAnimator属性动画及插值器的使用;

3、贝塞尔曲线的使用;

/**
 * Created by Administrator on 2018/1/30.
 * 点赞的效果
 */

public class LoveLayout extends RelativeLayout {
    //随机数
    private Random mRandom;
    //图片资源
    private int[] mImageRes;
    //控件的宽高
    private int mWidth, mHeight;
    //获取图片的宽高
    private int mDrawableWidth, mDrawableHeight;
    //插值器数组
    private Interpolator[] mInterpolator;

    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);
        mRandom = new Random();
        mImageRes = new int[]{R.drawable.pl_blue, R.drawable.pl_red, R.drawable.pl_yellow};
        //获取图片的宽高
        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.pl_blue);
        mDrawableWidth = drawable.getIntrinsicWidth();
        mDrawableHeight = drawable.getIntrinsicHeight();

        mInterpolator = new Interpolator[]{new AccelerateDecelerateInterpolator(), new AccelerateInterpolator(),
                new DecelerateInterpolator(), new LinearInterpolator()};
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取控件的宽高
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }
}

上面这些这是是初始化数据及获取一些图片的宽度和高度,在android属性动画的基本使用(http://blog.csdn.net/wangwo1991/article/details/77424239)这篇博客中有对属性动画的差值器有做说明;接下来在触发动作的时候提供一个addLove(),将效果添加到布局容器中;

/**
  * 添加一个点赞的view
  */
public void addLove() {
    //添加一个imageview在底部
    final ImageView loveIv = new ImageView(getContext());
    //设置图片资源(随机数)
    loveIv.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length - 1)]);
    //添加到底部中心
    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    //设置底部居中
    params.addRule(ALIGN_PARENT_BOTTOM);
    params.addRule(CENTER_HORIZONTAL);
    loveIv.setLayoutParams(params);

    addView(loveIv);
    //添加的效果是:有放大和透明的变化
    AnimatorSet animatorSet = getAimator(loveIv);
    animatorSet.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            //动画执行完毕后,将其移除
            removeView(loveIv);
        }
    });
    animatorSet.start();
}

这里就是创建一个ImageView,随机设置ImageView的背景,并设置其在布局容器中显示的位置,同时给ImageView添加透明度和缩放动画效果;

/**
  * 设置属性动画效果
  * @param loveIv
  * @return
  */
public AnimatorSet getAimator(ImageView loveIv) {
    //添加的效果是:有放大和透明的变化
    AnimatorSet allAnimator = new AnimatorSet();

    AnimatorSet innerAnimator = new AnimatorSet();
    //添加属性动画
    ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(loveIv, "alpha", 0.3f, 1.0f);
    ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(loveIv, "scaleX", 0.3f, 1.0f);
    ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(loveIv, "scaleY", 0.3f, 1.0f);
    //一起执行动画
    innerAnimator.playTogether(alphaAnimator, scaleXAnimator, scaleYAnimator);
    innerAnimator.setDuration(350);
    //运行的路径动画  按顺序执行动画
    allAnimator.playSequentially(innerAnimator, getBezierAnimator(loveIv));
    return allAnimator;
}

这里设置了透明度和缩放动画效果,通过AnimatorSet一起开启动画执行,在执行的时候根据贝塞尔曲线的路径去执行,而不是随意的去执行;

  • 一阶曲线原理:

一阶曲线是没有控制点的,仅有两个数据点(A 和 B),最终效果一个线段。


一阶公式如下:


  • 二阶曲线原理

二阶曲线由两个数据点(A 和 C),一个控制点(B)来描述曲线状态,大致如下:


那么ac之间的红线是怎么生成的呢,让我们了解一下:


在AB线段和BC线段分别去D、E两点,且满足条件


连接DE,取点F,使得: ,这样获取到的点F就是贝塞尔曲线上的一个点,动态图如下:


二阶公式如下:


  • 三阶曲线原理

三阶曲线由两个数据点(A 和 D),两个控制点(B 和 C)来描述曲线状态


动态图如下:


三阶公式如下:


这是一阶贝塞尔、二阶贝塞尔和三阶贝塞尔,当然还有四阶、五阶等,具体网上有很多关于贝塞尔曲线的知识(https://www.cnblogs.com/wjtaigwh/p/6647114.html);这个效果里面用到的是三阶贝塞尔曲线,不过这里需要自定义路径属性动画;

/**
 * Created by Administrator on 2018/1/30.
 * 自定义的路径属性动画
 */

public class LoveTypeEvaluator implements TypeEvaluator<PointF> {
    private PointF point1, point2;

    public LoveTypeEvaluator(PointF point1, PointF point2) {
        this.point1 = point1;
        this.point2 = point2;
    }

    @Override
    public PointF evaluate(float t, PointF point0, PointF point3) {
        //t 的范围是0-1的范围
        //可以开始套公式了
        PointF pointF = new PointF();
        pointF.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;

        pointF.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 pointF;
    }
}

根据构造方法传入的point1和point2使用三阶贝塞尔曲线公式进行路径的绘制;

/**
     * 绘制贝塞尔曲线
     * @param loveIv
     * @return
     */
    private Animator getBezierAnimator(final ImageView loveIv) {
        //确定这四个点
        PointF point0 = new PointF(mWidth / 2 - mDrawableWidth / 2, mHeight - mDrawableHeight);
        //确保p2点的y值一定要大于p2点的y值
        PointF point1 = getPoint(1);
        PointF point2 = getPoint(2);
        PointF point3 = new PointF(mRandom.nextInt(mWidth) - mDrawableWidth, 0);
        LoveTypeEvaluator typeEvaluator = new LoveTypeEvaluator(point1, point2);
        //第一个参数 typeEvaluator  第二个参数就是p0,第三个参数就是p3
        ValueAnimator bezererAnimator = ObjectAnimator.ofObject(typeEvaluator, point0, point3);
        //插值器
        bezererAnimator.setInterpolator(mInterpolator[mRandom.nextInt(mInterpolator.length - 1)]);
        bezererAnimator.setDuration(5000);
        bezererAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
                loveIv.setX(pointF.x);
                loveIv.setY(pointF.y);
                //设置透明度
                float t = animation.getAnimatedFraction();
                loveIv.setAlpha(1 - t + 0.2f);
            }
        });
        return bezererAnimator;
    }

    /**
     * 获取p1和p2
     * @param index
     * @return
     */
    private PointF getPoint(int index) {
        return new PointF(mRandom.nextInt(mWidth) - mDrawableWidth, mRandom.nextInt(mHeight / 2) + (index - 1) * (mHeight / 2));
    }

这样效果就实现了。

源码地址:

https://pan.baidu.com/s/1rakMHcS



猜你喜欢

转载自blog.csdn.net/wangwo1991/article/details/79437546