Android 實(shí)現(xiàn)波浪動(dòng)畫的兩種方式

一、國(guó)際慣例先上效果

水波浪動(dòng)畫截取了部分效果

二、實(shí)現(xiàn)思路

1、貝塞爾曲線實(shí)現(xiàn)

什么是貝塞爾曲線,貝塞爾曲線的原理,二階、三階、四階、五階等等更復(fù)雜的貝塞爾曲線感興趣的自己去了解,這里就不贅述了
Android中繪制貝塞爾曲線的Path類提供了四個(gè)方法
二階貝API:
public void quadTo(float x1, float y1, float x2, float y2)
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
三階API:
public void quadTo(float x1, float y1, float x2, float y2, float x3, float y3)
public void rQuadTo(float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)

推導(dǎo)過程(從其他網(wǎng)站趴的別人的),其中N階貝塞爾曲線可理解為有N條相連的線段,有N+1個(gè)頂點(diǎn):
(1)一階貝塞爾曲線:有頂點(diǎn)A、B,組成線段AB,利用線性插值原理,可得軌跡公式為:Path = (1-t)A + tB.
(2)二階貝塞爾曲線:有頂點(diǎn)A、B、C,組成線段AB、BC,則有:

    M = AB = (1-t)A + tB,
    N = BC = (1-t)B + tC,
    Path = (1-t)M + tN.
    將M和N帶入Path,可得軌跡公式為:Path = (1-t)2A + 2t(1-t)B + t2C.

(3)三階貝塞爾曲線:有頂點(diǎn)A、B、C、D,組成線段AB、BC、CD,則有:

    M = AB = (1-t)A + tB,
    N = BC = (1-t)B + tC,
    Q = CD = (1-t)C + tD,
    S = MN = (1-t)M + tN,
    T = NQ = (1-t)N + tQ,
    Path = (1-t)S + tT,

    將S和T帶入Path,再將M、N和Q帶入Path中的S和T,可得軌跡公式:Path = (1-t)3A + 3t(1-t)2B + 3t2(1-t)C + t3D. 
n階公式

部分代碼

初始化畫筆,通過動(dòng)畫控制從左到右的變量

onDraw方法繪制 注意一定要閉合并且修改畫筆的style屬性

2、正弦函數(shù)實(shí)現(xiàn)

一個(gè)很重要的網(wǎng)站:Des正xuanmos | 圖形計(jì)算器先利用這個(gè)網(wǎng)站套正弦曲線的表達(dá)式為y=Asin(ωx+φ)+k,把正玄圖畫出來(lái)

三條波浪正弦圖

部分代碼

//三條曲線相關(guān)的核心正弦曲線公式
 private fun calcValue(mapX: Float, offset: Float, i: Int): Double {
        var off = offset
        off %= 2f
        val sinFunc = sin(2 * (mapX + i) - off * Math.PI) + 1
        val recessionFunc = perHeight / (4 + (mapX + i).toDouble().pow(2.0))
        return -sinFunc * recessionFunc
    }
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 從canvas層面去除繪制時(shí)鋸齒
        canvas.setDrawFilter(mDrawFilter);
        for (int i = 0; i < getWidth(); i++) {

            // y = A * sin( wx + b) + h ; A: 浪高; w:周期;b:初相;
            float endY = (float) (20 * Math.sin(2 * Math.PI / getWidth() * i + mOffset1) + 300);
            //畫第一條波浪
            canvas.drawLine(i, 600, i, endY, mWavePaint);

            //畫第二條波浪
            float endY2 = (float) (20 * Math.sin(2 * Math.PI / getWidth() * i + mOffset2) + 300);
            canvas.drawLine(i, 600, i, endY2, mWavePaint);
        }
       
        if (mOffset1 > Float.MAX_VALUE - 1) {//防止數(shù)值超過浮點(diǎn)型的最大值
            mOffset1 = 0;
        }
        mOffset1 += mSpeed1;
        
        if (mOffset2 > Float.MAX_VALUE - 1) {//防止數(shù)值超過浮點(diǎn)型的最大值
            mOffset2 = 0;
        }
        mOffset2 += mSpeed2;
        //刷新
        postInvalidate();
    }
內(nèi)存占有率和性能

總結(jié)

因公司項(xiàng)目保密性,大多數(shù)代碼暫時(shí)無(wú)法公開,只展示了一些思路和核心算法,望各位見諒,如果您開發(fā)過程中也用到類似的效果,需要幫忙可臨時(shí)私信我,可適當(dāng)提供部分代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容