自定義view實現(xiàn)超萌動感小炸彈

聲明:本文章獨家授權微信公眾號碼個蛋原創(chuàng)推文

Hello,小伙伴們,我回來了。這些日子有的小伙伴問我怎么沒有更新了。這個其實是有原因,首先,最近有點忙。其次沒有看到什么覺得好玩的動畫!最后,就是我更新過了!!ThreadLocal源碼完全解析,只是你們源碼不感冒,然后你們忽略了!!!!忽略了!!!還有,我其實有更新一個薄荷卷尺,只是覺得有點簡單,而且還像也有什么好講的,所以只是上傳到github,沒有文章。
客套話已經完了,現(xiàn)在開始我們的超萌動感小炸彈之旅
首先,我還是先感謝一下作者,設計出這么棒的動畫!!設計出處點我
效果如下,Amazing:

preview.gif

再來看android的實現(xiàn)效果。

android實現(xiàn)

下面我們和自定義view實現(xiàn)超萌動感天氣小太陽一樣,開始解析動畫!(沒看過天氣小太陽的朋友可以先去看天氣小太陽,有些天氣小太陽講過的套路將不再講,同時需要掌握pathcamera、貝塞爾曲線等,不然部分代碼可能會引起不適)。

我們先把靜態(tài)view繪制出來,然后再實現(xiàn)動畫,Let's go。

靜態(tài)效果
1.地板
image.png

可以看到地板其實就是一條直線。然后中間兩個缺口。這要個么實現(xiàn)呢?看到小太陽的小伙伴可能都會說,這很簡單。只要畫一線直線然后覆蓋兩個白的區(qū)間就可以了。的確這可以實現(xiàn),但是仔細觀察可以發(fā)現(xiàn)下方的缺口是兩個半圓加矩形實現(xiàn)的,這樣的話就有點麻煩,而且不方便缺口位置的移動。那有什么簡單的方法呢?有,那就是使用Path進行繪畫一條直線,然后通過設置圓筆頭,再設置DashPathEffect(實現(xiàn)虛線,一段畫,一段不畫的效果,可以自由控制各段長度)來實現(xiàn)間隔(本view的缺口都是使用此特性實現(xiàn)的,不熟悉的小伙伴可以去看一下),代碼如下:

        float[] groundEffectFloat=new float[]  {bombLineWidth/4,bombLineWidth/2+bombLineWidth,bombLineWidth*2,bombLineWidth/3*2+bombLineWidth,getMeasuredWidth(),0};//設置畫與不畫所占長度
        groundDashPathEffect=new DashPathEffect(groundEffectFloat,0);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(bombLineColor);
        mPaint.setPathEffect(groundDashPathEffect);//設置虛線效果
        mPath.reset();
        mPath.moveTo(bombLineWidth/2,getMeasuredHeight()-bombLineWidth/2);
        mPath.lineTo(getMeasuredWidth()-bombLineWidth/2,getMeasuredHeight()-  
        bombLineWidth/2);
        canvas.drawPath(mPath,mPaint);
2.身體的邊框

image.png

仔細一看!聰明的你一定會說太簡單了,這不就是一個圓然后再用DashPathEffect實現(xiàn)缺口不就可以了!!嗯,對,就是這樣的。直接放代碼:

        mPaint.setPathEffect(bodyDashPathEffect);
        mPaint.setColor(bombLineColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius,   
        Path.Direction.CW);
        canvas.drawPath(mPath,mPaint);
        canvas.restore();

簡單!簡單的不能再簡單了,下面看身體

3.身體
image.png

可以發(fā)現(xiàn)身體其實也就是一個圓,然后加上左上角的高光。那么高光是怎么實現(xiàn)的呢?
三個點的高光,很簡單的,用Path畫弧,然后使用DashPathEffect效果,完美。
那么另一個高光呢?看圖。

image.png

可以看到就是條圓弧和一個路徑合成的,然后裁剪保持圓內。路徑的形成就是取弧度的兩個點,然后用貝塞爾曲線進行繪制,控制點位于弧度中分線中(下圖紅點)

image.png
image.png

代碼如下:(部分代碼,左上角高光的,其它的請查看源碼)

       //左上角的光邊
        mPaint.setPathEffect(null);
        mRectF.set(bombCenterX-bodyRadius+bombLineWidth/2,bombCenterY-bodyRadius+bombLineWidth/2
        ,bombCenterX+bodyRadius-bombLineWidth/2,getMeasuredHeight()-bombLineWidth-bombLineWidth/2);
        canvas.drawArc(mRectF,160,100,false,mPaint);
        //拼接光邊
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2, Path.Direction.CCW);
        canvas.save();
        canvas.clipPath(mPath);//裁剪圓內

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(lightColor);
        canvas.drawPath(mBodyLightPath,mPaint);
        canvas.restore();
4.臉
image.png

大家可以看到,好你有點復雜的,其實還好。這里是因為使用了Z軸旋轉,看起來有點復雜,那我們移到中間。

image.png

好像簡單了,眼睛和酒窩簡單,4個圓!!嘴巴,這個。。。這個好像有點惡心啊。其實不然,看圖。

image.png
image.png

其實就是一個圓然后再加上一個路徑圖就可以實現(xiàn),紅點表示的是控制點。空心點表示節(jié)點。細心的朋友可能發(fā)現(xiàn),不對啊。舌頭下面不全是紅的,和嘴巴是分開的。這里只需要把嘴巴按比例縮小,然后和嘴巴做個Xfermode就可以了。部分代碼:

        //畫舌頭 圓和嘴巴的縮放相交,mpath是嘴巴的路徑
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        canvas.drawPath(mPath,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mPaint.setColor(Color.parseColor("#f34671"));
        canvas.drawCircle(bombCenterX,mouthY+(mouthMaxY-mouthY)/8+bodyRadius/(5-1.4f*mouthOffsetPercent),bodyRadius/5,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.scale(0.8f,0.8f,bombCenterX,(mouthMaxY+mouthY)/2);
        canvas.drawPath(mPath,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);
5.臉上的陰影(不知道叫什么,暫時稱為陰影遮罩)
image.png

一看,個別好事的小伙伴說,你不會又讓我用貝塞爾曲線畫吧!這個不好找啊!!冷靜冷靜,這個實現(xiàn)如下:

image.png

如此簡單,兩個圓取紅圓未相交的部分。

//兩個圓相交產生陰影
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(bombShadowColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        canvas.translate(-bodyRadius/5,-bodyRadius/5);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);
6.頭
image.png

小伙伴又要說了。這個不好畫,不好畫!!冷靜冷靜。這個其實更簡單。只要把頭放在身體的后面一層就可以了。看圖:

image.png

代碼:

太簡單,我不想貼了,假裝我是代碼
7.引線
image.png

這個引線,其實也就是一線曲線,貝塞爾曲線繼續(xù)上場(不解釋,不懂的請面壁去)。

image.png
8.爆炸效果
image.png

簡單的不太再簡單了,4個圓,半徑從大到小畫,中間然后挖空。so easy!!

        int save = canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        float distance = maxBlastCircleRadius/12;
        //畫圓
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius,mPaint);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance,mPaint);
        mPaint.setColor(bombLineColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*2,mPaint);
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*3,mPaint);
        //掏空
        if (blastCircleRadiusPercent >0.65) {
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            canvas.drawCircle(bombCenterX, circleY, currentBlastCircleRadius - maxBlastCircleRadius * 0.65f, mPaint);
            mPaint.setXfermode(null);
        }
        canvas.restoreToCount(save);

到這里,我們已經完成了一半,那就是小炸彈的顯示,現(xiàn)在到了動畫的時間了!再次出場


android實現(xiàn)
9.臉左右移動動畫

可以看到左右移動,在移動的時間然后我們只需要在畫臉的時間加一個偏移,然后在移動的過程中,會發(fā)現(xiàn)臉會繞炸彈身體的中心旋轉。所以代碼如下

        canvas.save();
        mCamera.save();
        mCamera.rotate(bombTBRotate,0,-bombLRRotate/3);
        mMatrix.reset();
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        mMatrix.preTranslate(-bombCenterX,-(bombCenterY));
        mMatrix.postTranslate(bombCenterX,bombCenterY);
        mMatrix.postTranslate(faceLROffset,faceTBOffset);
        canvas.setMatrix(mMatrix);

使用camera,進行z軸的旋轉,然后再進行translate左右移動,然后使用valueanimator動畫對變偏移進行設置,搞定!在移動過程中,可以發(fā)現(xiàn)眼睛有瞇下的效果。這個很簡單,可以把眼睛用橢圓來實現(xiàn),保持寬度不變,改變高度就可以了。

image.png
10.身體頭部引線左右旋轉

這個就更簡單了,只需要在畫之前用camera旋轉變換獲取martix,然后對canvas進行變換。

11.臉部上下移動

首先和臉部左右移動一樣,使用matrix.translate進行上下移動。眼睛的變換也一樣。后面的眼睛放大效果,就是在變成圓的眼睛的時候,放大圓的半徑。
嘴巴的變換就相對比較復雜!看圖,高能預警,我也不知道我講不講得清楚!!!!

image.png

這是剛才畫嘴巴的圖!!!嘴巴動畫有兩個部分!!(以下語句可能會引起不適)

  • 第一部分嘴角往兩邊移動,嘴巴變扁。這里我們需要把ab兩點用屬性動畫往兩邊移動(兩邊的拐角點同樣移動),c點往上方移動,然后回到原始位置。
  • 第二部分是ab兩點往中間靠攏,直到ab重合,同時ab兩點往上移,de的控制點同時拉長,直到形成一個橢圓。

不理解!!不理解再好好想象一下,空間想象能力。要是還想不懂,那就算了。畢竟平時用得不多。

12.炸彈引線,點燃效果

炸彈引線效果同樣分兩個部分

  • 一個是引線變短,可以根據(jù)PathMeasure,獲取Path的比例Path(比如70%Path),這樣我們就可以通過ValueAnimator用一個01的比例來繪制引線變短的效果
      //mHeadLinePath是引線的完整Path
       mPathMeasure.setPath(mHeadLinePath,false);
        mPath.reset();
      mPathMeasure.getSegment(0,mPathMeasure.getLength()*headLinePercent,mPath,true);//根據(jù)比例獲取對應比例的引線
        canvas.drawPath(mPath,mPaint);
  • 第二部分是點燃的效果。其實就是一個金色的實心圓,然后一個紅色的圓邊框,中間白色,三個圓按不同的速率和極限做放大縮小動畫 (這里原設計還加入了變色的功能,金色圓會變色,可以用ArgbEvaluator實現(xiàn))。
image.png
13.爆炸動畫

和引線動畫類型,4個圓做放大縮小動畫,只是到一定的大小后,然后圓小漏空,并且漏空逐漸放大。

14.結語

好了,我們的超萌動感小炸彈到這里就結束了。希望小伙伴們能有所收獲,掌握更多自定義view的套路,更多分析方法,我們下次見。

源碼點我

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容