Android粒子篇之文字的粒子化運動

張風捷特烈發表於2019-03-03

零、前言

1.第一次接觸粒子是在html5的canvas,說是html的canvas,倒不如說是JavaScript的canvas,畢竟核心都在js。
2.經過長久的醞釀,感覺Java實現粒子運動好像也不是什麼難事,Android粒子篇將用Android作為視口,帶你領略粒子的炫酷。
3.關於效能方面,我想只要合理控制粒子的消失,還是可以接受的。只要不是無限級別,和遊戲比起來,這點效能九牛一毛啦。
4.粒子效果的核心有三個點:收集粒子、更改粒子、顯示粒子
5.為了純粹,本文只實現下圖的粒子效果:

粒子效果

一、文字的粒子化思路

1.資源準備

經過我的思索,既然可以用二維陣列實現數字的粒子化:見:Android原生繪圖之炫酷倒數計時
那麼一個Bitmap不是天然包含一個二維的畫素陣列嗎?二話不說,將圖片調成黑字無底,遍歷新增。

準備圖片.png

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();
}
複製程式碼

二、粒子的運動思路

結核運動學的一點知識,讓小球擁有位移,速度,加速度的模擬,來實現運動,這裡不過多贅述
我的這篇文章講得非常細緻

thank_you.gif
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–捷特 &安徽

相關文章