自定義view實(shí)現(xiàn)超萌動(dòng)感小炸彈

聲明:本文章獨(dú)家授權(quán)微信公眾號(hào)碼個(gè)蛋原創(chuàng)推文

Hello,小伙伴們粗蔚,我回來(lái)了刽虹。這些日子有的小伙伴問(wèn)我怎么沒(méi)有更新了挚冤。這個(gè)其實(shí)是有原因,首先庶近,最近有點(diǎn)忙鸭巴。其次沒(méi)有看到什么覺(jué)得好玩的動(dòng)畫!最后拦盹,就是我更新過(guò)了!溪椎!ThreadLocal源碼完全解析普舆,只是你們?cè)创a不感冒,然后你們忽略了P6痢U勇隆!歉秫!忽略了6曷濉!!還有轧膘,我其實(shí)有更新一個(gè)薄荷卷尺钞螟,只是覺(jué)得有點(diǎn)簡(jiǎn)單,而且還像也有什么好講的谎碍,所以只是上傳到github鳞滨,沒(méi)有文章。
客套話已經(jīng)完了蟆淀,現(xiàn)在開始我們的超萌動(dòng)感小炸彈之旅拯啦。
首先,我還是先感謝一下作者熔任,設(shè)計(jì)出這么棒的動(dòng)畫0础!設(shè)計(jì)出處點(diǎn)我疑苔。
效果如下甫匹,Amazing:

preview.gif

再來(lái)看android的實(shí)現(xiàn)效果。

android實(shí)現(xiàn)

下面我們和自定義view實(shí)現(xiàn)超萌動(dòng)感天氣小太陽(yáng)一樣夯巷,開始解析動(dòng)畫H汀(沒(méi)看過(guò)天氣小太陽(yáng)的朋友可以先去看天氣小太陽(yáng),有些天氣小太陽(yáng)講過(guò)的套路將不再講趁餐,同時(shí)需要掌握path喷兼、camera、貝塞爾曲線等后雷,不然部分代碼可能會(huì)引起不適)季惯。

我們先把靜態(tài)view繪制出來(lái),然后再實(shí)現(xiàn)動(dòng)畫臀突,Let's go勉抓。

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

可以看到地板其實(shí)就是一條直線。然后中間兩個(gè)缺口候学。這要個(gè)么實(shí)現(xiàn)呢藕筋?看到小太陽(yáng)的小伙伴可能都會(huì)說(shuō),這很簡(jiǎn)單梳码。只要畫一線直線然后覆蓋兩個(gè)白的區(qū)間就可以了隐圾。的確這可以實(shí)現(xiàn),但是仔細(xì)觀察可以發(fā)現(xiàn)下方的缺口是兩個(gè)半圓加矩形實(shí)現(xiàn)的掰茶,這樣的話就有點(diǎn)麻煩暇藏,而且不方便缺口位置的移動(dòng)。那有什么簡(jiǎn)單的方法呢濒蒋?有盐碱,那就是使用Path進(jìn)行繪畫一條直線,然后通過(guò)設(shè)置圓筆頭,再設(shè)置DashPathEffect(實(shí)現(xiàn)虛線瓮顽,一段畫县好,一段不畫的效果,可以自由控制各段長(zhǎng)度)來(lái)實(shí)現(xiàn)間隔(本view的缺口都是使用此特性實(shí)現(xiàn)的趣倾,不熟悉的小伙伴可以去看一下)聘惦,代碼如下:

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

image.png

仔細(xì)一看!聰明的你一定會(huì)說(shuō)太簡(jiǎn)單了儒恋,這不就是一個(gè)圓然后再用DashPathEffect實(shí)現(xiàn)缺口不就可以了I埔铩!嗯诫尽,對(duì)禀酱,就是這樣的。直接放代碼:

        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();

簡(jiǎn)單牧嫉!簡(jiǎn)單的不能再簡(jiǎn)單了剂跟,下面看身體

3.身體
image.png

可以發(fā)現(xiàn)身體其實(shí)也就是一個(gè)圓,然后加上左上角的高光酣藻。那么高光是怎么實(shí)現(xiàn)的呢曹洽?
三個(gè)點(diǎn)的高光,很簡(jiǎn)單的辽剧,用Path畫弧送淆,然后使用DashPathEffect效果,完美怕轿。
那么另一個(gè)高光呢偷崩?看圖。

image.png

可以看到就是條圓弧和一個(gè)路徑合成的撞羽,然后裁剪保持圓內(nèi)阐斜。路徑的形成就是取弧度的兩個(gè)點(diǎn),然后用貝塞爾曲線進(jìn)行繪制诀紊,控制點(diǎn)位于弧度中分線中(下圖紅點(diǎn))谒出。

image.png
image.png

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

       //左上角的光邊
        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);//裁剪圓內(nèi)

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

大家可以看到到推,好你有點(diǎn)復(fù)雜的,其實(shí)還好惕澎。這里是因?yàn)槭褂昧?code>Z軸旋轉(zhuǎn),看起來(lái)有點(diǎn)復(fù)雜颜骤,那我們移到中間唧喉。

image.png

好像簡(jiǎn)單了,眼睛和酒窩簡(jiǎn)單,4個(gè)圓0诵ⅰ董朝!嘴巴,這個(gè)干跛。子姜。。這個(gè)好像有點(diǎn)惡心啊楼入。其實(shí)不然哥捕,看圖。

image.png
image.png

其實(shí)就是一個(gè)圓然后再加上一個(gè)路徑圖就可以實(shí)現(xiàn)嘉熊,紅點(diǎn)表示的是控制點(diǎn)遥赚。空心點(diǎn)表示節(jié)點(diǎn)阐肤。細(xì)心的朋友可能發(fā)現(xiàn)凫佛,不對(duì)啊。舌頭下面不全是紅的孕惜,和嘴巴是分開的愧薛。這里只需要把嘴巴按比例縮小,然后和嘴巴做個(gè)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.臉上的陰影(不知道叫什么,暫時(shí)稱為陰影遮罩)
image.png

一看碧磅,個(gè)別好事的小伙伴說(shuō)碘箍,你不會(huì)又讓我用貝塞爾曲線畫吧!這個(gè)不好找熬ń肌7崃瘛!冷靜冷靜秆撮,這個(gè)實(shí)現(xiàn)如下:

image.png

如此簡(jiǎn)單四濒,兩個(gè)圓取紅圓未相交的部分。

//兩個(gè)圓相交產(chǎn)生陰影
        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

小伙伴又要說(shuō)了职辨。這個(gè)不好畫盗蟆,不好畫!舒裤!冷靜冷靜喳资。這個(gè)其實(shí)更簡(jiǎn)單。只要把頭放在身體的后面一層就可以了腾供∑偷耍看圖:

image.png

代碼:

太簡(jiǎn)單鲜滩,我不想貼了,假裝我是代碼
7.引線
image.png

這個(gè)引線节值,其實(shí)也就是一線曲線徙硅,貝塞爾曲線繼續(xù)上場(chǎng)(不解釋,不懂的請(qǐng)面壁去)搞疗。

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

簡(jiǎn)單的不太再簡(jiǎn)單了嗓蘑,4個(gè)圓,半徑從大到小畫匿乃,中間然后挖空桩皿。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);

到這里,我們已經(jīng)完成了一半扳埂,那就是小炸彈的顯示业簿,現(xiàn)在到了動(dòng)畫的時(shí)間了!再次出場(chǎng)


android實(shí)現(xiàn)
9.臉左右移動(dòng)動(dòng)畫

可以看到左右移動(dòng)阳懂,在移動(dòng)的時(shí)間然后我們只需要在畫臉的時(shí)間加一個(gè)偏移梅尤,然后在移動(dòng)的過(guò)程中,會(huì)發(fā)現(xiàn)臉會(huì)繞炸彈身體的中心旋轉(zhuǎ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巷燥,進(jìn)行z軸的旋轉(zhuǎn),然后再進(jìn)行translate左右移動(dòng)号枕,然后使用valueanimator動(dòng)畫對(duì)變偏移進(jìn)行設(shè)置缰揪,搞定!在移動(dòng)過(guò)程中葱淳,可以發(fā)現(xiàn)眼睛有瞇下的效果钝腺。這個(gè)很簡(jiǎn)單,可以把眼睛用橢圓來(lái)實(shí)現(xiàn)赞厕,保持寬度不變艳狐,改變高度就可以了。

image.png
10.身體頭部引線左右旋轉(zhuǎn)

這個(gè)就更簡(jiǎn)單了皿桑,只需要在畫之前用camera旋轉(zhuǎn)變換獲取martix毫目,然后對(duì)canvas進(jìn)行變換。

11.臉部上下移動(dòng)

首先和臉部左右移動(dòng)一樣诲侮,使用matrix.translate進(jìn)行上下移動(dòng)镀虐。眼睛的變換也一樣。后面的眼睛放大效果沟绪,就是在變成圓的眼睛的時(shí)候刮便,放大圓的半徑。
嘴巴的變換就相對(duì)比較復(fù)雜绽慈!看圖诺核,高能預(yù)警抄肖,我也不知道我講不講得清楚!=焉薄!裙士!

image.png

這是剛才畫嘴巴的圖H肟汀!腿椎!嘴巴動(dòng)畫有兩個(gè)部分桌硫!!(以下語(yǔ)句可能會(huì)引起不適)

  • 第一部分嘴角往兩邊移動(dòng),嘴巴變扁啃炸。這里我們需要把ab兩點(diǎn)用屬性動(dòng)畫往兩邊移動(dòng)(兩邊的拐角點(diǎn)同樣移動(dòng))铆隘,c點(diǎn)往上方移動(dòng),然后回到原始位置南用。
  • 第二部分是ab兩點(diǎn)往中間靠攏膀钠,直到ab重合,同時(shí)ab兩點(diǎn)往上移裹虫,de的控制點(diǎn)同時(shí)拉長(zhǎng)肿嘲,直到形成一個(gè)橢圓。

不理解V雳窟!不理解再好好想象一下,空間想象能力匣屡。要是還想不懂封救,那就算了。畢竟平時(shí)用得不多捣作。

12.炸彈引線誉结,點(diǎn)燃效果

炸彈引線效果同樣分兩個(gè)部分

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

和引線動(dòng)畫類型岸军,4個(gè)圓做放大縮小動(dòng)畫奋刽,只是到一定的大小后瓦侮,然后圓小漏空,并且漏空逐漸放大佣谐。

14.結(jié)語(yǔ)

好了肚吏,我們的超萌動(dòng)感小炸彈到這里就結(jié)束了。希望小伙伴們能有所收獲狭魂,掌握更多自定義view的套路罚攀,更多分析方法,我們下次見雌澄。

源碼點(diǎn)我

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斋泄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子镐牺,更是在濱河造成了極大的恐慌炫掐,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件睬涧,死亡現(xiàn)場(chǎng)離奇詭異募胃,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宙地,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門摔认,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宅粥,你說(shuō)我怎么就攤上這事参袱。” “怎么了秽梅?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵抹蚀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我企垦,道長(zhǎng)环壤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任钞诡,我火速辦了婚禮郑现,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荧降。我一直安慰自己接箫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布朵诫。 她就那樣靜靜地躺著辛友,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剪返。 梳的紋絲不亂的頭發(fā)上废累,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天邓梅,我揣著相機(jī)與錄音,去河邊找鬼邑滨。 笑死日缨,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的掖看。 我是一名探鬼主播殿遂,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乙各!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起幢竹,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤耳峦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后焕毫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹲坷,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年邑飒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了循签。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疙咸,死狀恐怖县匠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撒轮,我是刑警寧澤乞旦,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站题山,受9級(jí)特大地震影響兰粉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顶瞳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一玖姑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧慨菱,春花似錦焰络、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至洲劣,卻和暖如春备蚓,著一層夾襖步出監(jiān)牢的瞬間课蔬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工郊尝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留二跋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓流昏,卻偏偏與公主長(zhǎng)得像扎即,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子况凉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 前些天谚鄙,在IXUS上看到一個(gè)很贊的動(dòng)畫,一下子就看對(duì)眼了刁绒,于是便決定用Android來(lái)實(shí)現(xiàn)一下闷营,效果如下: 設(shè)計(jì)在...
    鋒ivy閱讀 3,917評(píng)論 16 72
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,182評(píng)論 25 707
  • 【Android 動(dòng)畫】 動(dòng)畫分類補(bǔ)間動(dòng)畫(Tween動(dòng)畫)幀動(dòng)畫(Frame 動(dòng)畫)屬性動(dòng)畫(Property ...
    Rtia閱讀 6,164評(píng)論 1 38
  • Today is December 14, 2017 will pass. When you see this t...
    魁北克的那些事閱讀 484評(píng)論 0 1
  • 突然想起來(lái), 在我很小的時(shí)候知市,奶奶在當(dāng)時(shí)的縣城傻盟,就是現(xiàn)在的鎮(zhèn)里賣水果,這也算是一種生意的嫂丙,對(duì)于家在農(nóng)村的...
    化作煙雨閱讀 274評(píng)論 0 0