<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>

          這交互炸了!炫酷跳動的閃屏Logo標(biāo)題

          共 15582字,需瀏覽 32分鐘

           ·

          2021-05-09 14:43

           微信改了推送機(jī)制,真愛請星標(biāo)本公號
          公眾號回復(fù)BAT加入BATcoder技術(shù)群

          作者:seagazer

          https://www.jianshu.com/p/629d4056f6eb

          1.分析

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


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


          首先看下下圖效果:



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


          1. Logo的名稱LitePlayer被拆分為單個文字

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

          3. 中間的Logo被隱藏

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

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

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

          7. Logo組合成名稱后,有個漸變的光暈照射效果從左往右移動

          8. 動畫結(jié)束


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

          2.實現(xiàn)

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


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



          // fill the text to array
          private 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ī)打散在屏幕各個位置,因為涉及到坐標(biāo),我們可以在onSizeChanged中進(jìn)行l(wèi)ogo文字隨機(jī)位置的初始化,同時我們構(gòu)建兩個集合存儲每個文字被打散和組合后的坐標(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)建動畫過程,定義一個屬性動畫從0-1計算進(jìn)度,在動畫過程通過重繪實現(xiàn)文字從凌亂打散的坐標(biāo)到最終組合坐標(biāo)進(jìn)行移動:



          // init the translation animation
          private void initOffsetAnimation() {
              mOffsetAnimator = ValueAnimator.ofFloat(01);
              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();
                  }
              });
          }

          @Override
          protected void onDraw(Canvas canvas
          {
              if (!isOffsetAnimEnd) {// offset animation
                  mPaint.setAlpha((int) Math.min(255255 * 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. 此時我們已經(jīng)把logo文字動畫實現(xiàn)了,接下來看我們拆解的第7步,還有個光照效果。對于這種光照效果,首選方案是通過Gradient+Shader實現(xiàn)。


          因為繪制漸變也涉及到坐標(biāo),所以動畫的初始化我們也放到了onSizeChanged中進(jìn)行:



          @Override
          protected void onSizeChanged(int w, int h, int oldw, int oldh) {
              super.onSizeChanged(w, h, oldw, oldh);
              mWidth = w;
              mHeight = h;
              initLogoCoordinate();// 初始化坐標(biāo)動畫
              initGradientAnimation(w);// 初始化漸變動畫
          }

          // init the gradient animation
          private void initGradientAnimation(int width) {
              mGradientAnimator = ValueAnimator.ofInt(02 * 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, 000new int[]{mTextColor, mGradientColor, mTextColor},
                      new float[]{00.5f1}, Shader.TileMode.CLAMP);
              mGradientMatrix = new Matrix();
          }



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



          // init the translation animation
          private void initOffsetAnimation() {
              ...
              // 初始化移動動畫
              ...
              mOffsetAnimator.addListener(new AnimatorListenerAdapter() {

                  @Override
                  public void onAnimationEnd(Animator animation
          {
                      if (mGradientAnimator != null && isShowGradient) {
                          isOffsetAnimEnd = true;
                          mPaint.setShader(mLinearGradient);
                          mGradientAnimator.start();
                      }
                  }
              });
          }

          @Override
          protected void onDraw(Canvas canvas
          {
              if (!isOffsetAnimEnd) {// offset animation
                  ...
                  // 文字移動動畫
                  ...
              } 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. 到此,文字動畫已經(jīng)實現(xiàn)了。剩下來就是一些自定義屬性的定義,對外提供一些屬性的setter和getter方法了,同時需要考慮在頁面生命周期過程中動畫的資源釋放。


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



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


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


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


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


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


          附項目源碼地址: 

          https://github.com/seagazer/animlogoview



          ·················END·················

          推薦閱讀

          ? 耗時2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!

          ? 『BATcoder』做了多年安卓還沒編譯過源碼?一個視頻帶你玩轉(zhuǎn)!

          ? 引入Jetpack架構(gòu)后,你的App會發(fā)生哪些變化?(建議收藏)

          ? 重生!進(jìn)階三部曲第一部《Android進(jìn)階之光》第2版 出版!

          BATcoder技術(shù)群,讓一部分人先進(jìn)大廠

          大家,我是劉望舒,騰訊云最具價值專家TVP,著有暢銷書《Android進(jìn)階之光》《Android進(jìn)階解密》《Android進(jìn)階指北》,蟬聯(lián)四屆電子工業(yè)出版社年度優(yōu)秀作者,谷歌開發(fā)者社區(qū)特邀講師。

          前華為面試官,現(xiàn)大廠技術(shù)負(fù)責(zé)人。


          想要加入 BATcoder技術(shù)群,公號回復(fù)BAT 即可。

          為了防止失聯(lián),歡迎關(guān)注我的小號


                   
            微信改了推送機(jī)制,真愛請星標(biāo)本公號??
          瀏覽 38
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  美女高潮视频免费在线观看 | 午夜精品男人的天堂 | 丝袜美腿av | 久久另类TS人妖一区二区 | 人人妻人人澡欧美91精品 |