聲明:本文章獨家授權微信公眾號碼個蛋原創(chuàng)推文
Hello
,小伙伴們,我回來了。這些日子有的小伙伴問我怎么沒有更新了。這個其實是有原因,首先,最近有點忙。其次沒有看到什么覺得好玩的動畫!最后,就是我更新過了!!ThreadLocal
源碼完全解析,只是你們源碼不感冒,然后你們忽略了!!!!忽略了!!!還有,我其實有更新一個薄荷卷尺,只是覺得有點簡單,而且還像也有什么好講的,所以只是上傳到github
,沒有文章。
客套話已經完了,現(xiàn)在開始我們的超萌動感小炸彈之旅。
首先,我還是先感謝一下作者,設計出這么棒的動畫!!設計出處點我。
效果如下,Amazing:
再來看
android
的實現(xiàn)效果。
下面我們和自定義view實現(xiàn)超萌動感天氣小太陽一樣,開始解析動畫!(沒看過天氣小太陽的朋友可以先去看天氣小太陽,有些天氣小太陽講過的套路將不再講,同時需要掌握path
、camera
、貝塞爾曲線等,不然部分代碼可能會引起不適)。
我們先把靜態(tài)view
繪制出來,然后再實現(xiàn)動畫,Let's go。
1.地板
可以看到地板其實就是一條直線。然后中間兩個缺口。這要個么實現(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.身體的邊框
仔細一看!聰明的你一定會說太簡單了,這不就是一個圓然后再用
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.身體
可以發(fā)現(xiàn)身體其實也就是一個圓,然后加上左上角的高光。那么高光是怎么實現(xiàn)的呢?
三個點的高光,很簡單的,用Path
畫弧,然后使用DashPathEffect
效果,完美。
那么另一個高光呢?看圖。
可以看到就是條圓弧和一個路徑合成的,然后裁剪保持圓內。路徑的形成就是取弧度的兩個點,然后用貝塞爾曲線進行繪制,控制點位于弧度中分線中(下圖紅點)。
代碼如下:(部分代碼,左上角高光的,其它的請查看源碼)
//左上角的光邊
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.臉
大家可以看到,好你有點復雜的,其實還好。這里是因為使用了Z
軸旋轉,看起來有點復雜,那我們移到中間。
好像簡單了,眼睛和酒窩簡單,4個圓!!嘴巴,這個。。。這個好像有點惡心啊。其實不然,看圖。
其實就是一個圓然后再加上一個路徑圖就可以實現(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.臉上的陰影(不知道叫什么,暫時稱為陰影遮罩)
一看,個別好事的小伙伴說,你不會又讓我用貝塞爾曲線畫吧!這個不好找啊!!冷靜冷靜,這個實現(xià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.頭
小伙伴又要說了。這個不好畫,不好畫!!冷靜冷靜。這個其實更簡單。只要把頭放在身體的后面一層就可以了。看圖:
代碼:
太簡單,我不想貼了,假裝我是代碼
7.引線
這個引線,其實也就是一線曲線,貝塞爾曲線繼續(xù)上場(不解釋,不懂的請面壁去)。
8.爆炸效果
簡單的不太再簡單了,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)在到了動畫的時間了!再次出場
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),保持寬度不變,改變高度就可以了。
10.身體頭部引線左右旋轉
這個就更簡單了,只需要在畫之前用camera
旋轉變換獲取martix
,然后對canvas
進行變換。
11.臉部上下移動
首先和臉部左右移動一樣,使用matrix.translate
進行上下移動。眼睛的變換也一樣。后面的眼睛放大效果,就是在變成圓的眼睛的時候,放大圓的半徑。
嘴巴的變換就相對比較復雜!看圖,高能預警,我也不知道我講不講得清楚!!!!
這是剛才畫嘴巴的圖!!!嘴巴動畫有兩個部分!!(以下語句可能會引起不適)
- 第一部分嘴角往兩邊移動,嘴巴變扁。這里我們需要把
ab
兩點用屬性動畫往兩邊移動(兩邊的拐角點同樣移動),c
點往上方移動,然后回到原始位置。 - 第二部分是
ab
兩點往中間靠攏,直到ab
重合,同時ab
兩點往上移,de
的控制點同時拉長,直到形成一個橢圓。
不理解!!不理解再好好想象一下,空間想象能力。要是還想不懂,那就算了。畢竟平時用得不多。
12.炸彈引線,點燃效果
炸彈引線效果同樣分兩個部分
- 一個是引線變短,可以根據(jù)
PathMeasure
,獲取Path
的比例Path
(比如70%
的Path
),這樣我們就可以通過ValueAnimator
用一個0
到1
的比例來繪制引線變短的效果
//mHeadLinePath是引線的完整Path
mPathMeasure.setPath(mHeadLinePath,false);
mPath.reset();
mPathMeasure.getSegment(0,mPathMeasure.getLength()*headLinePercent,mPath,true);//根據(jù)比例獲取對應比例的引線
canvas.drawPath(mPath,mPaint);
- 第二部分是點燃的效果。其實就是一個金色的實心圓,然后一個紅色的圓邊框,中間白色,三個圓按不同的速率和極限做放大縮小動畫 (這里原設計還加入了變色的功能,金色圓會變色,可以用
ArgbEvaluator
實現(xiàn))。
13.爆炸動畫
和引線動畫類型,4個圓做放大縮小動畫,只是到一定的大小后,然后圓小漏空,并且漏空逐漸放大。
14.結語
好了,我們的超萌動感小炸彈到這里就結束了。希望小伙伴們能有所收獲,掌握更多自定義view
的套路,更多分析方法,我們下次見。