Android——inhale效果實現以及延伸(動畫&繪製學習分享一)

概述:

本文主要是對drawBitmapMesh的api研究學習,以及介紹模仿mac吸入動效的實現原理。

 

drawBitmapMesh:

使bigmap產生形變,功能與drawVertices類似,區別是drawVertices直接對畫布產生作用。

首先需要看一下api中的參數列表:

其中關鍵參數分別是:

bitmap:需要扭曲的圖像

meshWidth:橫向的格數

meshHeight:縱向的格數

verts:網格交叉點座標數組,長度爲(meshWidth + 1) * (meshHeight + 1) * 2

vertOffset:控制verts數組中從第幾個數組元素開始纔對bitmap進行扭曲

這裏需要注意,根據api的描述在p以下的版本vertOffset和colorOffset不生效,默認爲0

 

通過一個demo來演示具體的使用方法:

1.首先創建一個自定義view,繪製一個bitmap在上面:

繪製的方法:

canvas.drawBitmapMesh(mBitmap, 
       WIDTH, 
       HEIGHT, 
       mVerts, 
       0, null, 0, mPaint);

其中WIDTH爲行數,HEIGHT爲列數,mVerts爲座標數組,暫時隨意傳即可,不會影響目前繪製的圖片。

2.假設將圖片分爲3行3列,則設置WIDTH,HEIGHT=3,根據行列以及圖片的寬高設置座標數組:

private void buildMesh(float mBmpW, float mBmpH) {
    int index = 0;
    for (int y = 0; y <= HEIGHT; ++y) {
        float fy = y * mBmpH / HEIGHT;
        for (int x = 0; x <= WIDTH; ++x) {
            float fx = x * mBmpW / WIDTH;
            setXY(mVerts, index, fx, fy);
            index += 1;
        }
    }
}

並且,在ondraw的時候繪製好分割線,效果如圖:

//畫分割線

mPaint.setStyle(Paint.Style.FILL);
for (int i = 0; i + 1 < mVerts.length / 2; i++) {
    if ((i + WIDTH + 1) * 2 + 1 <= mVerts.length) {
        canvas.drawLine(
                mVerts[i * 2],
                mVerts[i * 2 + 1],
                mVerts[(i + WIDTH + 1) * 2],
                mVerts[(i + WIDTH + 1) * 2 + 1],
                mPaint);
    }
    if (i != 0 && ((i + 1) % (WIDTH + 1) == 0)) {
        continue;
    }
    canvas.drawLine(
            mVerts[i * 2],
            mVerts[i * 2 + 1],
            mVerts[i * 2 + 2],
            mVerts[i * 2 + 3],
            mPaint);
}

3.增加觸控監聽,在手指點擊的位置畫一個小圓圈,並繪製兩條軌跡線,保存path路徑:

繪製的方法就不贅述了,比較簡單。

4.最關鍵的部分,點擊測試的時候,啓動一個進度值動畫,使得進度從0~行數變化,並計算動態的繪製路徑,更新座標數組並刷新視圖(這裏爲了讓視圖連貫,採取了10行10列):

其中最爲關鍵的是座標數組動態計算的方法:

/**
 * 動態計算繪製路徑
 * 1.計算兩條pathmeasure
 * 2.根據動畫index 計算左右兩邊路徑各自的第一個點和最後一個點座標
 * 3.分別計算網格里的每個點位置
 * @param timeIndex
 */
private void buildMeshes(int timeIndex) {
    mFirstPathMeasure.setPath(mFirstPath, false);
    mSecondPathMeasure.setPath(mSecondPath, false);
    int index = 0;
    float[] pos1 = {0.0f, 0.0f};
    float[] pos2 = {0.0f, 0.0f};
    float firstLen = mFirstPathMeasure.getLength();
    float secondLen = mSecondPathMeasure.getLength();
    float len1 = firstLen / HEIGHT;
    float len2 = secondLen / HEIGHT;
    float firstPointDist = timeIndex * len1;    //左邊第一個點長度
    float secondPointDist = timeIndex * len2;  //右邊第一個點長度
    float height = mBmpH;  //圖片高度
    mFirstPathMeasure.getPosTan(firstPointDist, pos1, null);
    mFirstPathMeasure.getPosTan(firstPointDist + height, pos2, null);  //得到第一個點座標和最後一個點座標
    float x1 = pos1[0];
    float x2 = pos2[0];
    float y1 = pos1[1];
    float y2 = pos2[1];
    float FIRST_DIST = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    float FIRST_H = FIRST_DIST / HEIGHT;
    mSecondPathMeasure.getPosTan(secondPointDist, pos1, null);
    mSecondPathMeasure.getPosTan(secondPointDist + height, pos2, null);
    x1 = pos1[0];
    x2 = pos2[0];
    y1 = pos1[1];
    y2 = pos2[1];
    float SECOND_DIST = (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    float SECOND_H = SECOND_DIST / HEIGHT;
    for (int y = 0; y <= HEIGHT; ++y) {
        //得到每一個點的位置
        mFirstPathMeasure.getPosTan(y * FIRST_H + firstPointDist, pos1, null);
        mSecondPathMeasure.getPosTan(y * SECOND_H + secondPointDist, pos2, null);
        float w = pos2[0] - pos1[0];//橫軸最左邊到最右邊的距離
        //左右兩邊的點的位置
        float fx1 = pos1[0];
        float fx2 = pos2[0];
        float fy1 = pos1[1];
        float fy2 = pos2[1];
        //左右兩邊點 x 和 y軸方向的差值
        float dy = fy2 - fy1;
        float dx = fx2 - fx1;
        for (int x = 0; x <= WIDTH; ++x) {
            // y = x * dy / dx
            float fx = x * w / WIDTH;
            //tanα = dy/dx = fy/fx
            float fy = fx * dy / dx;
            mVerts[index * 2 + 0] = fx + fx1;
            mVerts[index * 2 + 1] = fy + fy1;
            index += 1;
        }
    }
}

核心思想還是根據行數等分計算每一個點的座標,並動態更新座標數組,刷新圖形在軌跡線上顯示的位置。

 

總結:

通過drawBitmapMesh形變方法,可以實現一些其他api做不到的效果,比如一些酷炫的吸附,拖拽效果等,以後會慢慢找一些比較複雜的效果來實現練習一下。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

參考:

https://www.jianshu.com/p/51d8dd99d27d( Android:修圖技術之瘦臉效果的實現(drawBitmapMesh))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章