Android 貝塞爾曲線實戰之網易雲音樂鯨雲特效

做者:哈哈將 -個推 Android 高級開發工程師git

前言程序員

APP開發市場已經告別「野蠻生長」時代,人們再也不知足於APP外形創新,而將目光轉向全方面的用戶體驗上。在這過程當中,動效化做爲移動互聯網產品的新趨勢,如何實現酷炫絲滑的動畫效果已然成爲開發者們的新課題。實現方式其實很簡單。本文將爲你剖析理論基礎以及具體應用。我們平常使用的 APP 的時候,出現的不少酷炫動畫k可能都是有着貝塞爾曲線的身影。看完這篇文章,你的App也能夠達到酷炫吊炸天的動畫效果。github

先看兩個例子:canvas

  1. 手機 QQ 未讀消息紅點拖拽效果。

  1. 小說閱讀 APP 的翻頁效果。

簡介微信

在開始實戰以前,咱們仍是先了解下理論基礎。動畫的終極武器就是貝塞爾曲線了。它是一條光滑的曲線,依據四個位置任意的點座標繪製而成。1962年,法國工程師皮埃爾·貝塞爾(Pierre Bézier)率先研究出這種矢量繪製曲線的方法並給出了詳細的計算公式,應用於汽車的主體設計。所以,人們將按照此種公式繪製的曲線命名爲貝塞爾曲線。ide

核心思想函數

貝塞爾曲線是計算機圖形學中運用得最多的參數曲線之一。它經過控制曲線上的四個點(起始點、終止點以及兩個相互分離的中間點)來創造、編輯圖形。其中起重要做用的是位於曲線中央的控制線。這條線是虛擬的,中間與貝塞爾曲線交叉,兩端是控制端點。移動兩端的端點時貝塞爾曲線能夠改變曲線的曲率(彎曲的程度);移動中間點(也就是移動虛擬的控制線)時,貝塞爾曲線在起始點和終止點鎖定的狀況下作均勻移動。注意:貝塞爾曲線上的全部控制點、節點都可編輯。工具

原理post

這裏面有個通用公式,這個公式已經有前輩幫咱們總結好了。動畫

其中 P0 爲起點,Pn 爲中點,Pi 爲控制點。

一階貝塞爾曲線

一階這個比較簡單,由於沒有在網上找到能夠直接輸入數學公式的工具,就手工推導了下。

最後的公式爲 B(t)=(1-t)Po+tP1,t->[0,1]

二階貝塞爾曲線

先看動畫效果。

關注紅線部分,這條線就是咱們單位時間內運行的貝塞爾曲線效果圖。這條紅線其實是由無數個點組成,隨着 t 的不斷變化,紅線的轉換過程很是的順滑。

最後獲得的公式以下:

貝塞爾曲線的繪製,不管多少階(一階除外),均須要逐級降階,最終降至一階。在 「二階貝塞爾曲線解析」 這段文字中,從 第一步 到 第二步 的過程就是在降階。貝塞爾曲線最終的路徑是由 一階基線 上游走的紅色小點造成的。

三階貝塞爾曲線

有了二階的推導過程,三階的推導就容易多啦。因爲篇幅限制,推導過程這裏再也不展開,你們有興趣的話能夠自行推導下。

最後的紅色曲線是由藍色一階曲線得到的,而藍色一階曲線又是由綠色一階曲線得到,最後的綠色一階曲線則是最外的 P0,P1,P2,P3構成的。動畫效果爲:

四階貝塞爾曲線

五階貝塞爾曲線

結論 咱們發現原來貝塞爾曲線上的點與高數中二項式展開同樣,對於每一個線段上的點通過控制點進行切面操做,而連續的兩點之間是無限接近的,因此在繪製的過程當中會出現很是絲滑地過分。

貝塞爾曲線在 Android 上的使用

在Android 中使用貝塞爾曲線比較簡單,Android 已經內置了貝塞爾曲線的 API,開發者能夠直接予以調用。主要有兩個 API 。

quadTo

Path path = new Path(); path.moveTo(startX, startY); path.quadTo(eventX, eventY, endX, endY); canvas.drawPath(path, paint);

其中 (startX,startY) 爲起點,(endX,endY)爲終點,而 (eventX,eventY)即爲控制點了。

cubicTo

Path path = new Path(); path.moveTo(startX, startY); path.cubicTo(leftX, leftY, rightX, rightY, endX, endY); canvas.drawPath(path, paint);

調用此方法便可畫出一條三階貝塞爾曲線。(startX,startY)爲起點,(endX,endY)爲終點,而(leftX,leftY)與(rightX,rightY) 爲兩個控制點了。

多階貝塞爾曲線: Android 系統最高只能畫出三階的貝塞爾曲線,那麼想畫出更高階的怎麼辦呢?其實也很簡單。若是真的須要使用高階的曲線,能夠進行人工降階,降階到 3 級便可。

實戰

終於到實戰環節了,該環節共有兩個demo。一個是貝塞爾曲線擬圓效果,另外一個是仿網易雲音樂裏面的鯨雲效果。

效果實現1:以貝塞爾曲線畫圓爲例

前文總結了貝塞爾曲線的通用公式。在網上瀏覽資料的過程當中咱們發現有這麼一個公式:(4/3)tan(π/(2n)),其意義是由n段三階貝塞爾曲線擬合圓形時,曲線端點到該端點最近的控制點的最佳距離是(4/3)tan(π/(2n))。你們感興趣的話能夠自行推導。推導過程並不複雜,由於貝塞爾曲線有個重要的性質,即曲線方程中t=0.5時的點必定落在圓弧上。只須要把座標系帶入到三階方程式便可。

最後得知當 t=0.5,根據圓形方程式 X^2+Y^2=R^2 ,獲得h=(4/3)(sqrt(2)-1) ≈ 0.552284749831 。有了上述的理論基礎,再去畫圓就很是的輕鬆,咱們先在草稿紙中獲得這麼一個模型。

根據上圖,這個圓是由 4 段三階貝塞爾曲線構成的,分別是 P0->P3,P3->P6,P6->P9,P9->P11。三階貝塞爾曲線的構圖是 Android 內置的,咱們直接調用API 便可,核心代碼以下:

public HeartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context) }

[@Override](https://my.oschina.net/u/1162528)
protected void init(Context context) {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.FILL);

    mPath = new Path();
  //繪製 12 個點。
    mCurPointList = new ArrayList<>();
    mCurPointList.add(new PointF(0, dpToPx(-89)));
    mCurPointList.add(new PointF(dpToPx(50), dpToPx(-89)));
    mCurPointList.add(new PointF(dpToPx(90), dpToPx(-49)));
    mCurPointList.add(new PointF(dpToPx(90), 0));
    mCurPointList.add(new PointF(dpToPx(90), dpToPx(50)));
    mCurPointList.add(new PointF(dpToPx(50), dpToPx(90)));
    mCurPointList.add(new PointF(0, dpToPx(90)));
    mCurPointList.add(new PointF(dpToPx(-49), dpToPx(90)));
    mCurPointList.add(new PointF(dpToPx(-89), dpToPx(50)));
    mCurPointList.add(new PointF(dpToPx(-89), 0));
    mCurPointList.add(new PointF(dpToPx(-89), dpToPx(-49)));
    mCurPointList.add(new PointF(dpToPx(-49), dpToPx(-89)));
}

[@Override](https://my.oschina.net/u/1162528)
protected void onDraw(Canvas canvas) {
    drawCoordinate(canvas);

    canvas.translate(mWidth / 2, mHeight / 2);

    mPath.reset();
    for (int i = 0; i < 4; i++) {
        if (i == 0) {
            mPath.moveTo(mCurPointList.get(i * 3).x, mCurPointList.get(i * 3).y);
        } else {
            mPath.lineTo(mCurPointList.get(i * 3).x, mCurPointList.get(i * 3).y);
        }

        int endPointIndex;
        if (i == 3) {
            endPointIndex = 0;
        } else {
            endPointIndex = i * 3 + 3;
        }

        mPath.cubicTo(mCurPointList.get(i * 3 + 1).x, mCurPointList.get(i * 3 + 1).y,
                mCurPointList.get(i * 3 + 2).x, mCurPointList.get(i * 3 + 2).y,
                mCurPointList.get(endPointIndex).x, mCurPointList.get(endPointIndex).y);
    }

    canvas.drawPath(mPath, mPaint);

}

}

成果展現

效果實現2:以網易雲音樂鯨雲效果爲例

轉換成 GIF,圖片可能會有點失真,但並不妨礙具體實現思路。根據這個 GIF,咱們發現有三點功能須要去完成:

1.背景色與歌曲圖片相搭配,隨圖片的變化而變化;

2.歌曲中間圖片是一張圓形圖片而且能夠自動旋轉;

3.圖形外圈有動感 3D環繞效果。

第一點實現比較簡單。

第二點也不難。咱們能夠把一張圖片裁剪成圓形,也可使用 GitHub 上現有的開源庫,再加上一個屬性動畫代碼。

public void handleRotate(){
    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(ivShowPic, "rotation", 0f, 360f);
    objectAnimator.setDuration(20 * 1000);        
    objectAnimator.setRepeatMode(ValueAnimator.RESTART);        
    objectAnimator.setInterpolator(new LinearInterpolator());        
    objectAnimator.setRepeatCount(-1);        
    objectAnimator.start();
}

看下動感3D環繞效果即轉圈圈。

第三點 如何作跳動旋律特效?!!先不考慮前面兩點需求,咱們逐步分析下跳動旋律特效。動態圖在文章開始部分已經看到了,咱們建議先從靜態圖着手。

咱們猜想可能的實現思路(不表明官方實現思路):該動效外層一圈有 4 條線段在不規則地跳動,每條線的背後是一個圓,每一個圓由 4 條貝塞爾曲線組成。

第一步 先畫個圓。咱們只須要把畫筆的屬性設置成以下屬性,便可畫出一個空心圓。

mPaint.setStyle(Paint.Style.STROKE);

爲達到更順滑的環繞效果,咱們須要不斷調試各條貝塞爾曲線的對應的兩個控制點。具體參數可根據業務場景來定。文中demo僅做參考。

第二步 上文咱們分析過這個圓實際上是由貝塞爾曲線組成的擬圓。在Android系統中是以每秒60幀爲滿幀的,那麼只要將1秒÷60幀,就能得出16毫秒(ms)/幀是滿幀的界限,即每幀快於16ms則爲流暢。因此咱們這邊的刷新頻率設定爲每 80 毫秒刷新一次:

public void onPlay(View view){ scheduledExecutorService = Executors.newScheduledThreadPool(1); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { diyBezierView.post(new Runnable() { @Override public void run() { diyBezierView.play(); } }); } },0,80, TimeUnit.MILLISECONDS); 獲得的效果以下:

第三步 雛形已經完成,後續咱們的作法是再往上添加 2 個圓,看下 3 個圓是怎麼樣的效果。

第四步 最後一步固然是把前面兩點合在一塊兒啦,合一塊兒後就能夠看下最終效果了:

實際效果與預期效果會存在必定的差別,主要緣由在於函數座標以及畫筆的一些屬性問題。以上就是具體的實現思路,供你們參考。

總結

酷炫動畫的實現過程並無咱們想象的那麼複雜。其實,不少複雜特效都是由不一樣的動畫組合而成的,而絲滑般的動態效果則離不開貝塞爾曲線的應用。但願這篇文章能夠幫助到想要作出酷炫絲滑的動態效果的你。

如何獲取實戰 Demo

https://github.com/LiuLei0571/jingyun_breizer(方便的話給程序員小哥哥一個star 哦)

或者也能夠關注【個推技術學院】微信公衆號

(微信號:getuitech)

回覆關鍵詞「曲線」

便可獲取仿鯨雲特效動畫demo!

相關文章
相關標籤/搜索