别的先不說了,先上效果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CXoxWbh5WNXpFdk1mYshWblZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TMyAjM1QjM2EDMxcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
之前看到很多貝塞爾的動畫效果,都很漂亮,之前有需要用到類似的效果,就先寫了下來。
在百度搜尋了下, 公式是這樣子的:
二次方公式
二次方貝茲曲線的路徑由給定點P0、P1、P2的函數B(t)追蹤:
我也就照着公式 ,來一個簡單的代碼:
package com.mj.animapp.anim;
import android.annotation.SuppressLint;
import android.graphics.PointF;
import com.nineoldandroids.animation.TypeEvaluator;
@SuppressLint("NewApi")
public class BezierEvaluatorBear implements TypeEvaluator<PointF> {
/**
* 二次方貝塞爾曲線 B(t)=(1-t)^2*P0+2*t*(1-t)*P1+t^2*P2,t∈[0,1] P0,是我們的起點, P2是終點,
* P1是途徑的點 而t則是我們的一個因子,取值範圍是0-1
*/
private PointF pointF1;// 中間的點
public BezierEvaluatorBear(PointF pointF1) {
this.pointF1 = pointF1;
}
/**
* 開始和結束的點,這個公式是固定
*/
@Override
public PointF evaluate(float fraction, PointF pointF0, PointF pointF2) {
PointF pointF = new PointF();
pointF.x = (float) ((pointF0.x * (Math.pow((1 - fraction), 2))) + 2
* pointF1.x * fraction * (1 - fraction) + pointF2.x
* Math.pow(fraction, 2));
pointF.y = (float) ((pointF0.y * (Math.pow((1 - fraction), 2))) + 2
* pointF1.y * fraction * (1 - fraction) + pointF2.y
* Math.pow(fraction, 2));
return pointF;
}
}
公式是實作了 , 但是怎麼實作整個動畫的過程呢?
也是同樣的先上代碼:
@OnClick(R.id.llLeftRate)
public void llLeftRateOnlick(View view) {
int[] starRect = new int[2];
ivBean.getLocationOnScreen(starRect);
int[] endRect = new int[2];
rlZhangBean.getLocationOnScreen(endRect);
int starx = starRect[0];
int stary = starRect[1] - statusBarHeight;
int endx = 0;
int endy = 0;
for (int i = 0; i < ANMIBEANCOUNT; i++) {
endx = endRect[0]
+ (int) (Math.random() * (rlZhangBean.getWidth() + 1));
endy = (endRect[1] - statusBarHeight)
+ (int) (Math.random() * (rlZhangBean.getHeight() + 1));
mAnmiView.addBezierView(new PointF(starx, stary),
new PointF(endx - 50, endy + 100), new PointF(endx,
endy));
}
ObjectAnimator objAnim = rotationAnim(llLeftRate, 1f);
objAnim.setStartDelay(1000);
objAnim.start();
}
在這個點選事件中我們可以看出,要執行整個動畫我們需要 開始的位置 p0, 中間幅度的位置 p1, 結束的位置p2。
怎麼生成很多個小豆子呢, 就是使用自定義Layout , addview後,執行自定義的動畫。看一下源碼大家就會明白很多了。
public void addBezierView(PointF pointStart, PointF pointMid, PointF pointEnd) {
this.pointStart = pointStart;
this.pointMid = pointMid;
this.pointEnd = pointEnd;
this.pointMid.set(pointMid.x + mWidth / 2, pointMid.y - mHeight);
this.pointEnd.set(pointEnd.x - mWidth / 2, pointEnd.y - mHeight);
ImageView view = new ImageView(getContext());
int nextInt = mRandom.nextInt(loves.length - 1);
view.setImageDrawable(loves[nextInt]);
view.setLayoutParams(mParams);
addView(view);
ViewCompat.setX(view, pointStart.x);
ViewCompat.setY(view, pointStart.y);
AnimatorSet matorSet = getAnimatorSet(view);
// 設定插補器.
matorSet.setInterpolator(new AccelerateDecelerateInterpolator());
matorSet.start();
}
順便說下,怎麼實作不同的位置顯示不同的效果呢?
// 給動畫添加一個動畫的進度監聽;在動畫執行的過程中動态的改變view的位置;
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
ViewCompat.setX(view, pointF.x);
ViewCompat.setY(view, pointF.y);
if (animation.getAnimatedFraction() <= 0.6f) {
ViewCompat.setScaleX(view,
0.6f + animation.getAnimatedFraction());
ViewCompat.setScaleY(view,
0.6f + animation.getAnimatedFraction());
} else {
ViewCompat.setScaleX(view,
1.2f - (animation.getAnimatedFraction() - 0.6f));
ViewCompat.setScaleY(view,
1.2f - (animation.getAnimatedFraction() - 0.6f));
}
// 設定view的透明度,達到動畫執行過程view逐漸透明效果;
}
});
這個地方使用了放大縮小,來嘗試實作有點3d效果的抛物線。
最後說明下,最後面的抖動效果我使用了nineoldandroid 來實作抖動的效果
public ObjectAnimator rotationAnim(View view, float shakeFactor) {
PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofKeyframe(
"scaleX",
Keyframe.ofFloat(0f, 1f),
Keyframe.ofFloat(.1f, .9f),
Keyframe.ofFloat(.2f, .9f),
Keyframe.ofFloat(.3f, 1.0f),// 1.1f
Keyframe.ofFloat(.4f, 1.0f), Keyframe.ofFloat(.5f, 1.0f),
Keyframe.ofFloat(.6f, 1.0f), Keyframe.ofFloat(.7f, 1.0f),
Keyframe.ofFloat(.8f, 1.0f), Keyframe.ofFloat(.9f, 1.0f),
Keyframe.ofFloat(1f, 1f));
PropertyValuesHolder pvhScaleY = PropertyValuesHolder.ofKeyframe(
"scaleY", Keyframe.ofFloat(0f, 1f), Keyframe.ofFloat(.1f, .9f),
Keyframe.ofFloat(.2f, .9f), Keyframe.ofFloat(.3f, 1.0f),
Keyframe.ofFloat(.4f, 1.0f), Keyframe.ofFloat(.5f, 1.0f),
Keyframe.ofFloat(.6f, 1.0f), Keyframe.ofFloat(.7f, 1.0f),
Keyframe.ofFloat(.8f, 1.0f), Keyframe.ofFloat(.9f, 1.0f),
Keyframe.ofFloat(1f, 1f));
PropertyValuesHolder pvhRotate = PropertyValuesHolder.ofKeyframe(
"rotation", Keyframe.ofFloat(0f, 0f),
Keyframe.ofFloat(.1f, -3f * shakeFactor),
Keyframe.ofFloat(.2f, -3f * shakeFactor),
Keyframe.ofFloat(.3f, 3f * shakeFactor),
Keyframe.ofFloat(.4f, -3f * shakeFactor),
Keyframe.ofFloat(.5f, 3f * shakeFactor),
Keyframe.ofFloat(.6f, -3f * shakeFactor),
Keyframe.ofFloat(.7f, 3f * shakeFactor),
Keyframe.ofFloat(.8f, -3f * shakeFactor),
Keyframe.ofFloat(.9f, 3f * shakeFactor),
Keyframe.ofFloat(1f, 0));
return ObjectAnimator.ofPropertyValuesHolder(view, pvhScaleX,
pvhScaleY, pvhRotate).setDuration(1000);
}
這個項目目前已經我已經放在github 上 ,歡迎大家star 點選打開連結https://github.com/zasdsd/BezierAmin