前幾天七夕情人節,街上滿是狗糧,連電影選座也來蹭熱點,被單身狗雨和愛心雨刷屏了。這效果看著不錯,實現起來也不難,決定自己仿一波。
開始之前先上效果圖:
細節拆分
1、每一個view從頂部落到底部,然後從底部消失。
2、每一個view在X軸上的初始隨機位置開始落下。
3、每一個view隨機初始角度,並且落下過程中旋轉。
4、每一個view初始落下的高度隨機,但最終都會從底部消失。
實現步驟
首先,實現一個view的下落過程,這裡我內建了一個屬性動畫。
// 效果係數
private float rainyFraction;
public void startAnimator() {
if (objectAnimator == null) {
objectAnimator = ObjectAnimator.ofFloat(this, "rainyFraction", 0, 1);
objectAnimator.setDuration(6000);
}
objectAnimator.start();
}
public float getRainyFraction() {
return rainyFraction;
}
public void setRainyFraction(float rainyFraction) {
this.rainyFraction = rainyFraction;
invalidate();
}
複製程式碼
然後重寫onSizeChanged和onDraw方法,得到整個view的寬高,再來繪製bitmap
float dx;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// 重置matrix,必須重置不然會疊加
matrix.reset();
dx = width/2;
float dy = height * rainyFraction;
matrix.postTranslate(width/2, dy);
canvas.drawBitmap(bitmap, matrix, paint);
canvas.restore();
}
複製程式碼
落下的效果有了,現在考慮在X軸隨機位置的問題,獲取初始X軸位置的方法如下:
// 減去圖片大小是為了不超出
float dx = (float) ((width - imageSize) * Math.random());
複製程式碼
把matrix的dx替換就行了,下來圖片旋轉可以用matrix.postRotate(degrees, px, py)這個方法,計算出初始角度就ok了
// dx和dy都在上面計算過,把這兩句程式碼加上去就好
int angle = (int) (Math.random() * 360);
// 加上落下的係數,就可以實現落下並旋轉了
matrix.postRotate(180 * rainyFraction + angle, imageSize / 2 + dx, imageSize / 2 + dy);
複製程式碼
最後把隨機初始高度的計算也簡單
// 距離頂部的最大高度設為自身高度,獲取隨機值。
float tempHeight = (float) (height * Math.random());
// 這裡的最大高度,就是在頂部的上面的距離,最後圖片的高度,而這個高度+height就是整個落下的總高度,後面做多個圖落下的時候用到
maxHeight = Math.max(tempHeight, maxHeight);
複製程式碼
好了,到這裡一個圖的落下就完成了。接下來就是多個圖的效果,怎麼做?for迴圈!這裡我是準備了三個list儲存對應的初始X位置、Y位置以及初始的角度,看程式碼:
public void initCount() {
widthArr.clear();
heightArr.clear();
angles.clear();
for (int i = 0; i < num; i++) {
// 隨機橫向x的位置
widthArr.add((float) ((width - imageSize) * Math.random()));
float tempHeight = (float) (height * Math.random());
// 隨機的距離頂部的高度,是負值。加上imageSize是為了保證從頂部開始落下
heightArr.add(-(imageSize + tempHeight));
maxHeight = Math.max(tempHeight, maxHeight);
// 隨機初始角度
angles.add((int) (Math.random() * 360));
}
}
複製程式碼
同樣在onDraw方法中也是for循壞使用這些值
for (int i = 0; i < num; i++) {
// 重置matrix,必須重置不然會疊加
if (num != widthArr.size()) {
break;
}
matrix.reset();
// 當前的高度 = 隨機的初始高度(負值) + (view的高度 + 最高的狗頭所在的高度 == 整個動畫的高度)*係數
// 加上2 * imageSize是為了保證狗頭都落下底部外
float dy = heightArr.get(i) + (maxHeight + height + 2 * imageSize) * rainyFraction;
matrix.postTranslate(widthArr.get(i), dy);
// 以狗頭為中心旋轉
matrix.postRotate(angles.get(i) + 180 * rainyFraction, imageSize / 2 + widthArr.get(i), imageSize / 2 + dy);
canvas.drawBitmap(bitmap, matrix, paint);
}
複製程式碼
最後在activity使用RainyView
rainyView.initCount();
rainyView.startAnimator();
複製程式碼
這個效果的完成到這裡就結束了。
寫在最後
這是GitHub地址單身狗雨,完整程式碼可以從上面fork下來。喜歡的給個star唄,謝謝。
另外說一下,這個下落的效果很粗暴,哈哈。而且沒有把落下的速度計算出來,每個view的速度都一樣的。不過這些都沒關係,重要的是學習到裡面的精髓。