<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android實(shí)現(xiàn)炫酷跳動(dòng)的閃屏Logo標(biāo)題

          共 6790字,需瀏覽 14分鐘

           ·

          2021-06-06 23:23

          分析


          在日常開發(fā)中,經(jīng)常會(huì)遇到各種視覺效果,有的效果可能一眼看去會(huì)讓人覺得很復(fù)雜,但是我們必須明確一點(diǎn):


          所有復(fù)雜動(dòng)效都是可以分解成單一的基礎(chǔ)動(dòng)作,比如縮放,平移,旋轉(zhuǎn)這些基礎(chǔ)單元,然后將所有基礎(chǔ)單元?jiǎng)幼鬟M(jìn)行組合,就會(huì)產(chǎn)生讓人眼前一亮的視覺動(dòng)效。


          首先看下下圖效果:



          按照上面我們提到的思路進(jìn)行分解:


          1. Logo的名稱LitePlayer被拆分為單個(gè)文字

          2. 所有文字隨機(jī)打散在屏幕各個(gè)位置

          3. 中間的Logo被隱藏

          4. Logo文字從隨機(jī)位置平移到頁面固定位置

          5. 中間的Logo圖片逐漸顯示,并且附帶從下往上平移一小段位移

          6. Logo被打散的文字組合成名稱

          7. Logo組合成名稱后,有個(gè)漸變的光暈照射效果從左往右移動(dòng)

          8. 動(dòng)畫結(jié)束


          當(dāng)我們把動(dòng)畫拆解后,就可以針對每個(gè)拆解單元去構(gòu)造實(shí)現(xiàn)方案了。


          實(shí)現(xiàn)

          首先我們先對logo文字動(dòng)畫進(jìn)行實(shí)現(xiàn):

          1、首先對于數(shù)據(jù)來源,我們期望傳入一個(gè)logo的字符串,內(nèi)部將字符串拆解為單個(gè)文字?jǐn)?shù)組:

          // fill the text to arrayprivate void fillLogoTextArray(String logoName) {    if (TextUtils.isEmpty(logoName)) {        return;    }    if (mLogoTexts.size() > 0) {        mLogoTexts.clear();    }    for (int i = 0; i < logoName.length(); i++) {        char c = logoName.charAt(i);        mLogoTexts.put(i, String.valueOf(c));    }}


          2、所有文字需要隨機(jī)打散在屏幕各個(gè)位置,因?yàn)樯婕暗阶鴺?biāo),我們可以在onSizeChanged中進(jìn)行l(wèi)ogo文字隨機(jī)位置的初始化,同時(shí)我們構(gòu)建兩個(gè)集合存儲(chǔ)每個(gè)文字被打散和組合后的坐標(biāo)狀態(tài):

              // 最終合成logo后的坐標(biāo)    private SparseArray<PointF> mQuietPoints = new SparseArray<>();    // logo被隨機(jī)打散的坐標(biāo)    private SparseArray<PointF> mRadonPoints = new SparseArray<>();
          @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; initLogoCoordinate(); }
          private void initLogoCoordinate() { float centerY = mHeight / 2f + mPaint.getTextSize() / 2 + mLogoOffset; // calculate the final xy of the text float totalLength = 0; for (int i = 0; i < mLogoTexts.size(); i++) { String str = mLogoTexts.get(i); float currentLength = mPaint.measureText(str); if (i != mLogoTexts.size() - 1) { totalLength += currentLength + mTextPadding; } else { totalLength += currentLength; } } // the draw width of the logo must small than the width of this AnimLogoView if (totalLength > mWidth) { throw new IllegalStateException("This view can not display all text of logoName, please change text size."); } float startX = (mWidth - totalLength) / 2; if (mQuietPoints.size() > 0) { mQuietPoints.clear(); } for (int i = 0; i < mLogoTexts.size(); i++) { String str = mLogoTexts.get(i); float currentLength = mPaint.measureText(str); mQuietPoints.put(i, new PointF(startX, centerY)); startX += currentLength + mTextPadding; } // generate random start xy of the text if (mRadonPoints.size() > 0) { mRadonPoints.clear(); } // 構(gòu)建隨機(jī)初始坐標(biāo) for (int i = 0; i < mLogoTexts.size(); i++) { mRadonPoints.put(i, new PointF((float) Math.random() * mWidth, (float) Math.random() * mHeight)); } }


          3、構(gòu)建動(dòng)畫過程,定義一個(gè)屬性動(dòng)畫從0-1計(jì)算進(jìn)度,在動(dòng)畫過程通過重繪實(shí)現(xiàn)文字從凌亂打散的坐標(biāo)到最終組合坐標(biāo)進(jìn)行移動(dòng):

          // init the translation animationprivate void initOffsetAnimation() {    mOffsetAnimator = ValueAnimator.ofFloat(0, 1);    mOffsetAnimator.setDuration(mOffsetDuration);    mOffsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());    mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {        @Override        public void onAnimationUpdate(ValueAnimator animation) {            if (mQuietPoints.size() <= 0 || mRadonPoints.size() <= 0) {                return;            }            mOffsetAnimProgress = (float) animation.getAnimatedValue();            invalidate();        }    });}
          @Overrideprotected void onDraw(Canvas canvas) { if (!isOffsetAnimEnd) {// offset animation mPaint.setAlpha((int) Math.min(255, 255 * mOffsetAnimProgress + 100)); for (int i = 0; i < mQuietPoints.size(); i++) { PointF quietP = mQuietPoints.get(i); PointF radonP = mRadonPoints.get(i); float x = radonP.x + (quietP.x - radonP.x) * mOffsetAnimProgress; float y = radonP.y + (quietP.y - radonP.y) * mOffsetAnimProgress; canvas.drawText(mLogoTexts.get(i), x, y, mPaint); } }}


          4、此時(shí)我們已經(jīng)把logo文字動(dòng)畫實(shí)現(xiàn)了,接下來看我們拆解的第7步,還有個(gè)光照效果。對于這種光照效果,首選方案是通過Gradient+Shader實(shí)現(xiàn)。


          因?yàn)槔L制漸變也涉及到坐標(biāo),所以動(dòng)畫的初始化我們也放到了onSizeChanged中進(jìn)行:

          @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {    super.onSizeChanged(w, h, oldw, oldh);    mWidth = w;    mHeight = h;    initLogoCoordinate();// 初始化坐標(biāo)動(dòng)畫    initGradientAnimation(w);// 初始化漸變動(dòng)畫}
          // init the gradient animationprivate void initGradientAnimation(int width) { mGradientAnimator = ValueAnimator.ofInt(0, 2 * width); if (mGradientListener != null) { mGradientAnimator.addListener(mGradientListener); } mGradientAnimator.setDuration(mGradientDuration); mGradientAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mMatrixTranslate = (int) animation.getAnimatedValue(); invalidate(); } }); mLinearGradient = new LinearGradient(-width, 0, 0, 0, new int[]{mTextColor, mGradientColor, mTextColor}, new float[]{0, 0.5f, 1}, Shader.TileMode.CLAMP); mGradientMatrix = new Matrix();}


          5、漸變動(dòng)畫是在文字移動(dòng)動(dòng)畫結(jié)束后自動(dòng)播放的,所以我們可以在初始化文字移動(dòng)動(dòng)畫時(shí)對動(dòng)畫結(jié)束進(jìn)行監(jiān)聽處理,同時(shí)在繪制onDraw中對文字進(jìn)行繪制:

          // init the translation animationprivate void initOffsetAnimation() {    ...    // 初始化移動(dòng)動(dòng)畫    ...    mOffsetAnimator.addListener(new AnimatorListenerAdapter() {
          @Override public void onAnimationEnd(Animator animation) { if (mGradientAnimator != null && isShowGradient) { isOffsetAnimEnd = true; mPaint.setShader(mLinearGradient); mGradientAnimator.start(); } } });}
          @Overrideprotected void onDraw(Canvas canvas) { if (!isOffsetAnimEnd) {// offset animation ... // 文字移動(dòng)動(dòng)畫 ... } else {// gradient animation for (int i = 0; i < mQuietPoints.size(); i++) { PointF quietP = mQuietPoints.get(i); canvas.drawText(mLogoTexts.get(i), quietP.x, quietP.y, mPaint); } mGradientMatrix.setTranslate(mMatrixTranslate, 0); mLinearGradient.setLocalMatrix(mGradientMatrix); }}


          6、到此,文字動(dòng)畫已經(jīng)實(shí)現(xiàn)了。剩下來就是一些自定義屬性的定義,對外提供一些屬性的setter和getter方法了,同時(shí)需要考慮在頁面生命周期過程中動(dòng)畫的資源釋放。


          好了,看下我們實(shí)現(xiàn)的效果:



          7、對于上面Logo圖片的動(dòng)畫可以單獨(dú)對一個(gè)ImageView進(jìn)行平移+透明度動(dòng)畫實(shí)現(xiàn),這里就不花篇幅去描述了。


          自定義View我相信大部分同學(xué)都已經(jīng)掌握熟練,但是對于復(fù)雜動(dòng)畫,是否能夠?qū)⑦@些熟練的能力用在刀刃上呢,也許會(huì)有部分同學(xué)看到一個(gè)華麗的效果就不知所措了。


          本文沒有對動(dòng)畫進(jìn)行深入的分析,也沒涉及到復(fù)雜的數(shù)據(jù)運(yùn)算,只是通過一個(gè)簡單的例子,闡述了一種通用的動(dòng)效分析實(shí)現(xiàn)的方式,通過這種思維方式,你可以很清晰的了解自己每一步的實(shí)現(xiàn)以及目標(biāo)。


          最后總結(jié)一下,對于自定義動(dòng)效而言,我們首先可以讓UI提供最終視覺效果,通過工具進(jìn)行單幀解析,觀察其中的每一幀之間的動(dòng)作關(guān)系,將其拆解為一個(gè)個(gè)基礎(chǔ)單元。


          接著針對每個(gè)單元步驟進(jìn)行實(shí)現(xiàn),最后整合到一起,就能夠?qū)崿F(xiàn)一個(gè)連貫的效果了。這是一種思想,當(dāng)你熟練掌握這種思想后,還需要對一些數(shù)學(xué)知識(shí)有一定的了解,比如三角函數(shù),矩陣運(yùn)算等等。只要培養(yǎng)好這兩方面能力,日常開發(fā)中,任何復(fù)雜的動(dòng)效都不足以為懼。


          源碼地址:

          https://github.com/seagazer/animlogoview


          到這里就結(jié)束啦。

          瀏覽 107
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  乱子伦国产精品www | 黑人久久久 | 综合二区三区 | sm免费观看 | 中国男女一级黄色操逼在线视频 |