小米卸載動畫-圖標(biāo)爆炸

前言

前一段事件接觸到了小米手機(jī)的卸載動畫吭产。炫酷效果个从,瞬間爆炸好嗎脉幢?好奇心也瞬間爆炸,憑什么我們的圖標(biāo)就只能移來移去信姓,秉著不服氣的心情鸵隧,我決定仔細(xì)研究研究它是怎么爆炸的。

正文

上面說了很多次圖標(biāo)爆炸意推,那到底是怎么爆炸的呢豆瘫?我們來看效果。


爆炸動畫

這是github上的一個(gè)開源項(xiàng)目菊值,該項(xiàng)目的連接地址為:https://github.com/tyrantgit/ExplosionField

使用動畫

已經(jīng)有大神為我等菜鳥封裝好了git遠(yuǎn)程引用庫外驱,這樣很方便就可以使用圖標(biāo)爆炸功能。

  • 在module的buildGradle添加內(nèi)容
dependencies {
   compile 'tyrantgit:explosionfield:1.0.1'
 }
  • 在程序中調(diào)用
//實(shí)現(xiàn)爆炸動畫
mExplosionField = ExplosionField.attach2Window(this);
mExplosionField.explode(v);

怎么樣腻窒,是不是很昵宇!簡!單儿子!

動畫實(shí)現(xiàn)原理

那么這看起來如此炫酷的動畫到底是如何實(shí)現(xiàn)的呢瓦哎?我們知道調(diào)用mExplosionField.explode(v);可以實(shí)現(xiàn)爆炸動畫。而explode方法是ExplosionField類為我們提供的一個(gè)方法它本身又繼承于View柔逼。首先export方法有什么內(nèi)容:

public void explode(final View view) {
        Rect r = new Rect();
        //getLocalVisibleRect(Rect r)方法可以把視圖的長和寬映射到一個(gè)Rect對象上蒋譬。
        view.getGlobalVisibleRect(r);
        int[] location = new int[2];
        //獲取當(dāng)前坐標(biāo)
        getLocationOnScreen(location);

        //使矩形與view重疊
        r.offset(-location[0], -location[1]);
        r.inset(-mExpandInset[0], -mExpandInset[1]);

        //執(zhí)行view抖動的動畫
        int startDelay = 100;
        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(150);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            Random random = new Random();

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                view.setTranslationX((random.nextFloat() - 0.5f) * view.getWidth() * 0.05f);
                view.setTranslationY((random.nextFloat() - 0.5f) * view.getHeight() * 0.05f);

            }
        });
        animator.start();

        //隱藏view動畫
        view.animate().setDuration(150).setStartDelay(startDelay).scaleX(0f).scaleY(0f).alpha(0f).start();

        //執(zhí)行真正的炸裂動畫
        explode(Utils.createBitmapFromView(view), r, startDelay, ExplosionAnimator.DEFAULT_DURATION);
    }

可以看到,export方法中無非就是將VIew映射到一個(gè)矩形中愉适,然后做抖動動畫犯助。其中explode(Utils.createBitmapFromView(view), r, startDelay, ExplosionAnimator.DEFAULT_DURATION);才是真正調(diào)用完成爆炸的功能。

 public void explode(Bitmap bitmap, Rect bound, long startDelay, long duration) {
        final ExplosionAnimator explosion = new ExplosionAnimator(this, bitmap, bound);
        explosion.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mExplosions.remove(animation);
            }
        });
        explosion.setStartDelay(startDelay);
        explosion.setDuration(duration);
        mExplosions.add(explosion);
        explosion.start();
    }

explode(Utils.createBitmapFromView(view), r, startDelay, ExplosionAnimator.DEFAULT_DURATION);方法中维咸,重點(diǎn)內(nèi)容就是ExplosionAnimator類的實(shí)例化剂买。這個(gè)類是一個(gè)繼承ValueAnimator的類惠爽,它實(shí)現(xiàn)了爆炸動畫的主要內(nèi)容。

public class ExplosionAnimator extends ValueAnimator {

    static long DEFAULT_DURATION = 0x400;
    private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f);
    private static final float END_VALUE = 1.4f;
    private static final float X = Utils.dp2Px(5);
    private static final float Y = Utils.dp2Px(20);
    private static final float V = Utils.dp2Px(2);
    private static final float W = Utils.dp2Px(1);
    private Paint mPaint;
    private Particle[] mParticles;
    private Rect mBound;
    private View mContainer;

    public ExplosionAnimator(View container, Bitmap bitmap, Rect bound) {
        mPaint = new Paint();
        mBound = new Rect(bound);
        int partLen = 15;
        mParticles = new Particle[partLen * partLen];
        Random random = new Random(System.currentTimeMillis());
        int w = bitmap.getWidth() / (partLen + 2);
        int h = bitmap.getHeight() / (partLen + 2);
        for (int i = 0; i < partLen; i++) {
            for (int j = 0; j < partLen; j++) {
                //GetPixel瞬哼,函數(shù)功能婚肆,該函數(shù)檢索指定坐標(biāo)點(diǎn)的像素的RGB顏色值。
                mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random);
            }
        }
        mContainer = container;
        setFloatValues(0f, END_VALUE);
        setInterpolator(DEFAULT_INTERPOLATOR);
        setDuration(DEFAULT_DURATION);
    }

    private Particle generateParticle(int color, Random random) {
        Particle particle = new Particle();
        particle.color = color;
        particle.radius = V;
        if (random.nextFloat() < 0.2f) {
            //2 - 5
            particle.baseRadius = V + ((X - V) * random.nextFloat());
        } else {
            //1 - 2
            particle.baseRadius = W + ((V - W) * random.nextFloat());
        }

        float nextFloat = random.nextFloat();
        //0.2 - 0.38
        particle.top = mBound.height() * ((0.18f * random.nextFloat()) + 0.2f);
        //0.2 - 0.236 : 0.4 - 0.58
        particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());

        //bottom可能是x軸位移偏移總量
        //-0.9 - 0.9
        particle.bottom = (mBound.height() * (random.nextFloat() - 0.5f)) * 1.8f;
        float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
        particle.bottom = f;

        particle.mag = 4.0f * particle.top / particle.bottom;
        particle.neg = (-particle.mag) / particle.bottom;

        f = mBound.centerX() + (Y * (random.nextFloat() - 0.5f));
        particle.baseCx = f;
        particle.cx = f;
        f = mBound.centerY() + (Y * (random.nextFloat() - 0.5f));
        particle.baseCy = f;
        particle.cy = f;
        particle.life = END_VALUE / 10 * random.nextFloat();
        particle.overflow = 0.4f * random.nextFloat();
        particle.alpha = 1f;
        return particle;
    }

    public boolean draw(Canvas canvas) {
        if (!isStarted()) {
            return false;
        }
        for (Particle particle : mParticles) {
            particle.advance((float) getAnimatedValue());
            if (particle.alpha > 0f) {
                mPaint.setColor(particle.color);
                mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));
                canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);
            }
        }
        mContainer.invalidate();
        return true;
    }

    @Override
    public void start() {
        super.start();
        mContainer.invalidate(mBound);
    }

    private class Particle {
        float alpha;
        int color;
        float cx;
        float cy;
        float radius;
        float baseCx;
        float baseCy;
        float baseRadius;
        float top;
        float bottom;
        float mag;
        float neg;
        float life;
        float overflow;

        //根據(jù)屬性動畫進(jìn)度倒槐,來獲得小圓點(diǎn)當(dāng)前屬性
        public void advance(float factor) {
            float f = 0f;
            float normalization = factor / END_VALUE;

            //這里設(shè)定當(dāng)小圓點(diǎn)動畫進(jìn)行到進(jìn)度旬痹,也就是大于或者小于某一個(gè)臨界值時(shí),置為透明讨越。
            if (normalization < life || normalization > 1f - overflow) {
                alpha = 0f;
                return;
            }
            //計(jì)算出在可顯示區(qū)間的進(jìn)度
            normalization = (normalization - life) / (1f - life - overflow);

            //計(jì)算此時(shí)應(yīng)有的值
            float f2 = normalization * END_VALUE;

            //當(dāng)進(jìn)度超過0.7f時(shí)两残,使小圓點(diǎn)開始變透明。
            if (normalization >= 0.7f) {
                f = (normalization - 0.7f) / 0.3f;
            }
            alpha = 1f - f;

            //小圓點(diǎn)下一個(gè)位移點(diǎn)
            f = bottom * f2;
            cx = baseCx + f;

            cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
            radius = V + (baseRadius - V) * f2;
        }
    }
}

以上為ExplosionAnimator類的所有代碼把跨,也是實(shí)現(xiàn)爆炸動畫的主要內(nèi)容人弓。在其內(nèi)部存在一個(gè)內(nèi)部類Particle,該類盛放了一個(gè)小圓點(diǎn)的所有屬性信息着逐。ExplosionAnimator類的實(shí)例化過程中將傳入的view映射的矩形依照像素為顏色分解成15*15個(gè)小圓點(diǎn)將其屬性數(shù)據(jù)存儲在Particle實(shí)例中崔赌,通過generateParticle()方法使得小圓點(diǎn)按照一定的比例獲得不同的屬性值,決定之后的運(yùn)動軌跡以及透明度變化耸别。
現(xiàn)在已經(jīng)搞清楚健芭,圖標(biāo)是怎么變成很多個(gè)小球的了,最后來看看秀姐,小球運(yùn)動的動畫如何實(shí)現(xiàn)慈迈。
在上面我們有看到ExplosionAnimator實(shí)例化之后緊接著調(diào)用了start()方法。這就要看到ExplosionField類的onDraw()方法了省有。

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (ExplosionAnimator explosion : mExplosions) {
            explosion.draw(canvas);
        }
    }

其中主要內(nèi)容便是調(diào)用了explosion.draw(canvas);

public boolean draw(Canvas canvas) {
        if (!isStarted()) {
            return false;
        }
        for (Particle particle : mParticles) {
            particle.advance((float) getAnimatedValue());
            if (particle.alpha > 0f) {
                mPaint.setColor(particle.color);
                mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));
                canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);
            }
        }
        mContainer.invalidate();
        return true;
    }

這個(gè)方法才真正實(shí)現(xiàn)了對view映射的矩形進(jìn)行操作痒留,根據(jù)getAnimatedValue()動畫的進(jìn)度來進(jìn)行小圓點(diǎn)狀態(tài)的刷新,從而實(shí)現(xiàn)爆炸動畫蠢沿。

總結(jié)

爆炸動畫的實(shí)現(xiàn)伸头,其實(shí)就是一個(gè)屬性動畫的靈活運(yùn)用,使用屬性動畫為我們提供一個(gè)動畫進(jìn)度舷蟀,利用再view的刷新機(jī)制去完成動畫效果恤磷。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市野宜,隨后出現(xiàn)的幾起案子扫步,更是在濱河造成了極大的恐慌,老刑警劉巖速缨,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異代乃,居然都是意外死亡旬牲,警方通過查閱死者的電腦和手機(jī)仿粹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來原茅,“玉大人吭历,你說我怎么就攤上這事±揲伲” “怎么了晌区?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長通贞。 經(jīng)常有香客問我朗若,道長,這世上最難降的妖魔是什么昌罩? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任哭懈,我火速辦了婚禮,結(jié)果婚禮上茎用,老公的妹妹穿的比我還像新娘遣总。我一直安慰自己,他們只是感情好轨功,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布旭斥。 她就那樣靜靜地躺著,像睡著了一般古涧。 火紅的嫁衣襯著肌膚如雪垂券。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天蒿褂,我揣著相機(jī)與錄音圆米,去河邊找鬼。 笑死啄栓,一個(gè)胖子當(dāng)著我的面吹牛娄帖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昙楚,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼近速,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了堪旧?” 一聲冷哼從身側(cè)響起削葱,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淳梦,沒想到半個(gè)月后析砸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爆袍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年首繁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了作郭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弦疮,死狀恐怖夹攒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胁塞,我是刑警寧澤咏尝,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站啸罢,受9級特大地震影響编检,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伺糠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一蒙谓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧训桶,春花似錦累驮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至午绳,卻和暖如春置侍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拦焚。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工蜡坊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赎败。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓秕衙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親僵刮。 傳聞我的和親對象是個(gè)殘疾皇子据忘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容