仿花束直播點贊效果
這是一個利用貝塞爾曲線實現的仿花束直播的點贊效果,實現該效果涉及到:
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