聲明:本文章獨(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:
再來(lái)看
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勉抓。
1.地板
可以看到地板其實(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.身體的邊框
仔細(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.身體
可以發(fā)現(xiàn)身體其實(shí)也就是一個(gè)圓,然后加上左上角的高光酣藻。那么高光是怎么實(shí)現(xiàn)的呢曹洽?
三個(gè)點(diǎn)的高光,很簡(jiǎn)單的辽剧,用Path
畫弧送淆,然后使用DashPathEffect
效果,完美怕轿。
那么另一個(gè)高光呢偷崩?看圖。
可以看到就是條圓弧和一個(gè)路徑合成的撞羽,然后裁剪保持圓內(nèi)阐斜。路徑的形成就是取弧度的兩個(gè)點(diǎn),然后用貝塞爾曲線進(jìn)行繪制诀紊,控制點(diǎn)位于弧度中分線中(下圖紅點(diǎn))谒出。
代碼如下:(部分代碼,左上角高光的邻奠,其它的請(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.臉
大家可以看到到推,好你有點(diǎn)復(fù)雜的,其實(shí)還好惕澎。這里是因?yàn)槭褂昧?code>Z軸旋轉(zhuǎn),看起來(lái)有點(diǎn)復(fù)雜颜骤,那我們移到中間唧喉。
好像簡(jiǎn)單了,眼睛和酒窩簡(jiǎn)單,4個(gè)圓0诵ⅰ董朝!嘴巴,這個(gè)干跛。子姜。。這個(gè)好像有點(diǎn)惡心啊楼入。其實(shí)不然哥捕,看圖。
其實(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í)稱為陰影遮罩)
一看碧磅,個(gè)別好事的小伙伴說(shuō)碘箍,你不會(huì)又讓我用貝塞爾曲線畫吧!這個(gè)不好找熬ń肌7崃瘛!冷靜冷靜秆撮,這個(gè)實(shí)現(xiàn)如下:
如此簡(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.頭
小伙伴又要說(shuō)了职辨。這個(gè)不好畫盗蟆,不好畫!舒裤!冷靜冷靜喳资。這個(gè)其實(shí)更簡(jiǎn)單。只要把頭放在身體的后面一層就可以了腾供∑偷耍看圖:
代碼:
太簡(jiǎn)單鲜滩,我不想貼了,假裝我是代碼
7.引線
這個(gè)引線节值,其實(shí)也就是一線曲線徙硅,貝塞爾曲線繼續(xù)上場(chǎng)(不解釋,不懂的請(qǐng)面壁去)搞疗。
8.爆炸效果
簡(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)
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)赞厕,保持寬度不變艳狐,改變高度就可以了。
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ù)警抄肖,我也不知道我講不講得清楚!=焉薄!裙士!
這是剛才畫嘴巴的圖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è)0
到1
的比例來(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))。
13.爆炸動(dòng)畫
和引線動(dòng)畫類型岸军,4個(gè)圓做放大縮小動(dòng)畫奋刽,只是到一定的大小后瓦侮,然后圓小漏空,并且漏空逐漸放大佣谐。
14.結(jié)語(yǔ)
好了肚吏,我們的超萌動(dòng)感小炸彈到這里就結(jié)束了。希望小伙伴們能有所收獲狭魂,掌握更多自定義view
的套路罚攀,更多分析方法,我們下次見雌澄。