零、前言
1.第一次接觸粒子是在html5的canvas,說是html的canvas,倒不如說是JavaScript的canvas,畢竟核心都在js。
2.經過長久的醞釀,感覺Java實現粒子運動好像也不是什麼難事,Android粒子篇將用Android作為視口,帶你領略粒子的炫酷。
3.關於效能方面,我想只要合理控制粒子的消失,還是可以接受的。只要不是無限級別,和遊戲比起來,這點效能九牛一毛啦。
4.粒子效果的核心有三個點:收集粒子、更改粒子、顯示粒子
5.為了純粹,本文只實現下圖的粒子效果:
一、文字的粒子化思路
1.資源準備
經過我的思索,既然可以用二維陣列實現數字的粒子化:見:Android原生繪圖之炫酷倒數計時,
那麼一個Bitmap不是天然包含一個二維的畫素陣列嗎?二話不說,將圖片調成黑字無底,遍歷新增。
2.粒子物件
/**
* 作者:張風捷特烈<br/>
* 時間:2018/11/16 0016:21:51<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:粒子物件
*/
public class Ball implements Cloneable {
public float aX;//加速度
public float aY;//加速度Y
public float vX;//速度X
public float vY;//速度Y
public float x;//點位X
public float y;//點位Y
public int color;//顏色
public float r;//半徑
public long born;//誕生時間
public Ball clone() {
Ball clone = null;
try {
clone = (Ball) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
複製程式碼
3.對粒子的收集
這裡遍歷一下bitmap將所有的黑色畫素收集到粒子集合中:
//成員變數:
private List<Ball> mBalls = new ArrayList<>();//粒子集合
//載入圖片
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.thank_you);
for (int i = 0; i < bitmap.getWidth(); i++) {
for (int j = 0; j < bitmap.getHeight(); j++) {
int pixel = bitmap.getPixel(i, j);
if (pixel < 0) {//此處過濾掉其他顏色,避免全部產生粒子
Ball ball = new Ball();//產生粒子---每個粒子擁有隨機的一些屬性資訊
ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random()
ball.vY = rangeInt(-15, 35);
ball.aY = 0.98f;
ball.x = i * 4;
ball.y = j * 4;
ball.color = pixel;
ball.born = System.currentTimeMillis();
mBalls.add(ball);
}
mColArr[i][j] = bitmap.getPixel(i, j);
}
}
複製程式碼
4.粒子的顯示
也就是將粒子集合中的每個粒子繪製出來,非常簡單
但這時它已經不是文字或圖片了,而是可操縱的粒子,是不是很興奮
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mCoo.x, mCoo.y);
for (Ball ball : mBalls) {//繪製小球集合
mPaint.setColor(ball.color);
canvas.drawCircle(ball.x, ball.y, 2, mPaint);
}
canvas.restore();
}
複製程式碼
二、粒子的運動思路
結核運動學的一點知識,讓小球擁有位移,速度,加速度的模擬,來實現運動,這裡不過多贅述
我的這篇文章講得非常細緻。
1.粒子的狀態更新:
其實也不復雜,就是在恆定時間流下對位移和速度進行運動學的累加
/**
* 更新小球
*/
private void updateBall() {
for (int i = 0; i < mBalls.size(); i++) {
Ball ball = mBalls.get(i);
ball.x += ball.vX;
ball.y += ball.vY;
ball.vY += ball.aY;
ball.vX += ball.aX;
}
}
複製程式碼
2.粒子的湮滅
昨天在思考怎麼能夠更好控制粒子的湮滅呢?
粒子的湮滅說起來就是在一定的條件下將粒子從集合中移除,今早突然靈光一閃,可以用時間啊!
/**
* 更新小球
*/
private void updateBall() {
for (int i = 0; i < mBalls.size(); i++) {
Ball ball = mBalls.get(i);
if (System.currentTimeMillis() - mClickTime > 3000)
mBalls.remove(i);
}
ball.x += ball.vX;
ball.y += ball.vY;
ball.vY += ball.aY;
ball.vX += ball.aX;
}
}
複製程式碼
3.時間數字流:
//初始化時間流ValueAnimator
mAnimator = ValueAnimator.ofFloat(0, 1);
mAnimator.setRepeatCount(-1);
mAnimator.setDuration(2000);
mAnimator.addUpdateListener(animation -> {
updateBall();//更新小球位置
invalidate();
});
複製程式碼
4.點選事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mClickTime = System.currentTimeMillis();//記錄點選時間
mAnimator.start();
break;
}
return true;
}
複製程式碼
這樣粒子運動的效果就實現了,當然你也可以用任意的圖片來進行粒子運動
關於Bitmap的粒子運動會新寫一篇來詳細的論述,敬請期待。
三、粒子動畫結束監聽:
現在到了粒子全部湮滅的監聽了,在一張圖片的所有粒子湮滅後進入下一個圖片:
很容易想到在移除粒子是監聽粒子集合是否為空
1.成員變數準備
private List<Ball> mBalls = new ArrayList<>();//粒子集合
private ValueAnimator mAnimator;//時間流
private long mRunTime;//粒子運動時刻
private boolean isOK;//結束的flag
private int curBitmapIndex = 0;//當前圖片索引
private Bitmap[] mBitmaps;//圖片陣列
複製程式碼
2.圖片粒子化方法方法封裝
/**
* 將一個圖片粒子化
* @param bitmap
*/
public void bitmap2Ball(Bitmap bitmap) {
for (int i = 0; i < bitmap.getWidth(); i++) {
for (int j = 0; j < bitmap.getHeight(); j++) {
int pixel = bitmap.getPixel(i, j);
if (pixel < 0) {//此處過濾掉其他顏色,避免全部產生粒子
Ball ball = new Ball();//產生粒子---每個粒子擁有隨機的一些屬性資訊
ball.vX = (float) (Math.pow(-1, Math.ceil(Math.random() * 1000)) * 20 * Math.random());
ball.vY = rangeInt(-15, 35);
ball.aY = 0.98f;
ball.x = i * 4;
ball.y = j * 4;
ball.color = pixel;
ball.born = System.currentTimeMillis();
mBalls.add(ball);
}
}
}
}
複製程式碼
3.準備一個圖片陣列:
//載入圖片陣列
mBitmaps = new Bitmap[]{
BitmapFactory.decodeResource(getResources(), R.mipmap.thank_you),
BitmapFactory.decodeResource(getResources(), R.mipmap.bye),
BitmapFactory.decodeResource(getResources(), R.mipmap.go_on)
};
bitmap2Ball(mBitmaps[curBitmapIndex]);//初始化第一張圖
複製程式碼
4.監聽圖片碎裂完成,進行下一張
isOK這個flag讓我頭疼了幾分鐘,總算找到了何時的位置
/**
* 更新小球
*/
private void updateBall() {
for (int i = 0; i < mBalls.size(); i++) {
Ball ball = mBalls.get(i);
if (System.currentTimeMillis() - mRunTime > 2000) {
mBalls.remove(i);
}
if (mBalls.isEmpty()) {//表示本張已結束
if (curBitmapIndex == 2) {
mAnimator.end();
return;
}
curBitmapIndex++;
bitmap2Ball(mBitmaps[curBitmapIndex]);
isOK = true;
invalidate();
mRunTime = System.currentTimeMillis();
mAnimator.pause();
}
if (isOK) {//如果本張結束---返回掉
isOK = false;
return;
}
ball.x += ball.vX;
ball.y += ball.vY;
ball.vY += ball.aY;
ball.vX += ball.aX;
}
}
複製程式碼
至此,本文的效果就已經實現了,是不是沒有想象中的那麼複雜,相信你也可以做到
四、後記:
這些天感謝柚子茶的幫助,無以為報,以文記之,祝掘金越來越好,幫助更多的技術開發者。
本文無捷文規範
,不會再做任何修改。專案原始碼見→_→ ~ GitHub ~ ←_←
—————————-2018.11.17–捷特 &安徽