? ? ? ? 從事移動(dòng)互聯(lián)網(wǎng)開發(fā)已經(jīng)快兩年了,回想當(dāng)初是Android帶我走進(jìn)了移動(dòng)端的世界杨凑,后來由于自身對(duì)用戶體驗(yàn)比較感興趣敷扫,發(fā)現(xiàn)iOS更注重對(duì)圖形渲染以及動(dòng)畫處理,所以轉(zhuǎn)身自學(xué)iOS開發(fā)赚抡,之后也發(fā)布了幾個(gè)iOS的組件庫爬坑。但是Android進(jìn)兩年發(fā)展趨勢猛增,在動(dòng)畫處理上也比以前有所改進(jìn)涂臣,Android3.0之后開啟了GPU硬件加速讓圖形渲染沒以前那么卡頓盾计,特別是5.0之后的material design更是引領(lǐng)潮流售担。所以趁最近工作不忙有時(shí)間,寫了個(gè)彈性動(dòng)畫的ActionMenu署辉,核心原理來自咱們iOS界有動(dòng)畫小王子之稱的kittenyang的這篇博客(特此鳴謝)族铆,廢話不多說了,先上效果圖(gif存在失真哭尝,實(shí)際效果更佳流暢Q彈)
1.核心原理
? ? ? ? 主要知識(shí)點(diǎn)其實(shí)就下面三項(xiàng)
? ? ? ? * Android自定義View以及ViewGroup
? ? ? ? * 利用三階貝塞爾曲線繪制圖形
? ? ? ? * 通過ValueAnimator結(jié)合阻尼振動(dòng)自定義屬性變化
2.各項(xiàng)講解
(1)Android自定義View以及ViewGroup
? ? ? ? 首先我們先做出一個(gè)圓形的彈性動(dòng)畫效果哥攘,先得自定義一個(gè)view,然后重寫onDraw可以用過path和paint任意繪制圖形材鹦,具體細(xì)節(jié)我就不多說了逝淹,不了解的同學(xué)可以查閱相關(guān)資料學(xué)習(xí)。
(2)利用三階貝塞爾曲線繪制圖形
? ? ? ? 貝塞爾曲線是我認(rèn)為一個(gè)比較神奇的東西桶唐,因?yàn)樗旧峡梢岳L制出任何圖形栅葡,有興趣的同學(xué)可以到這個(gè)網(wǎng)站玩一下,那么我們今天需要做的就是用三階貝塞爾曲線畫圓
圖中以A,B,C,D四個(gè)點(diǎn)作為基準(zhǔn)點(diǎn)尤泽,把一個(gè)圓分4次繪制欣簇,以c1-c8作為四次繪制中三階貝塞爾曲線的輔助點(diǎn),各點(diǎn)的位置我們?cè)O(shè)為offSet坯约,由前輩們計(jì)算熊咽,當(dāng)offSet為(圓形直徑/3.6)時(shí)剛好是一個(gè)整圓,后來找到一篇文章介紹了其原理(雖然我看完之后還是一臉懵逼)闹丐。我們?cè)O(shè)置形變系數(shù)為factor(0~1)横殴,設(shè)定圓形拉伸的最大程度為圓形的2/5, 則extra= circleRadius*2*factor/5卿拴,分別列出各點(diǎn)坐標(biāo)
intxA =0,xB =0,xC =0,xD =0,yA =0,yB =0,yC =0,yD =0;
xA =circleRadius;
xB =circleRadius*2+extra;
xC =circleRadius;
xD =0;
yA =extra;
yB =circleRadius;
yC =circleRadius*2-extra;
yD =circleRadius;
offSet=circleRadius*2/3.6f;
mPath.moveTo(xA,yA);
mPath.cubicTo(xA +offSet,yA,xB,yB? -offSet,xB,yB);
mPath.cubicTo(xB,yB +offSet,xC +offSet,yC,xC,yC);
mPath.cubicTo(xC -offSet,yC,xD,yD? +offSet,xD,yD);
mPath.cubicTo(xD,yD -offSet,xA -offSet,yA,xA,yA);
OK滥玷!基本點(diǎn)都繪制完了,直接Run起來
看到一個(gè)紅通通的圓形繪制成功之后第一部大功告成巍棱!
(3)通過ValueAnimator結(jié)合阻尼振動(dòng)自定義屬性變化
? ? ? ? 上面我們已經(jīng)繪制好了一個(gè)圓形惑畴,接下來事情就很簡單了,只需要通過改變形變系數(shù)factor(0~1)的值來改變形變程度就可以了航徙,下面介紹一個(gè)Android的動(dòng)畫API—ValueAnimator如贷,直接通過組件屬性定義的動(dòng)畫效果,然后重寫變化機(jī)制
ValueAnimator valueAnimator = ValueAnimator.ofObject(new FloatEvaluator(time,1,0),1,0);
valueAnimator.addUpdateListener(this);
valueAnimator.setDuration(time);
valueAnimator.start();
@Override
public voidonAnimationUpdate(ValueAnimator animation) {
? ? ? factor= (float)animation.getAnimatedValue();
? ? ? invalidate();
}
由此原理就是通過屬性動(dòng)畫不斷的改變factor從1到0到踏,然后刷新重繪小圓球杠袱,F(xiàn)loatEvaluator就是我們自定義的根據(jù)什么樣的函數(shù)去改變factor,這個(gè)時(shí)候就要介紹到我們高中學(xué)習(xí)的阻尼振動(dòng)函數(shù)了
該函數(shù)可以模擬最真實(shí)的彈性效果
我們60幀為基準(zhǔn)(肉眼看到刷新流暢度的最低標(biāo)準(zhǔn))窝稿,計(jì)算出一列變化factor的值
publicFloatEvaluator(longtime, floatstartValue, floatendValue) {
? ? ? ? sum= (int)time *60/1000;
? ? ? ? float diff = endValue - startValue;
? ? ? ? value=new float[sum];
? ? ? ? float x;
? ? ? ? for(int i = 0; i < sum; i++) {
? ? ? ? x = i *1.0f/sum;
? ? ? ? value[i] = endValue - (float)(diff * Math.pow(Math.E,-1*dampingFactor* x) * Math.cos(velocityFactor* x));
? ? ? ? }
}
其中dampingFactor和velocityFactor為阻力和速度楣富,我這里設(shè)置的是5和30,可以自定義調(diào)節(jié)改變彈性程度伴榔。
基本構(gòu)建完成纹蝴,添加一個(gè)按鈕啟動(dòng)動(dòng)畫試試效果
大功告成庄萎!
3.總結(jié)
? ? ? ? 大家掌握了一個(gè)圓形組件的形變?cè)碇笫O碌木腿菀锥嗔耍肰iewGroup多繪制幾個(gè)擺擺位置就OK了塘安,也可以利用這種原理自己創(chuàng)新構(gòu)思出別的控件糠涛,只要掌握了核心的兩點(diǎn):
? ? ? ? 1.貝塞爾曲線繪制圖形
? ? ? ? 2.利用阻尼函數(shù)以及ValueAnimator自定義形變屬性的值
? ? ? ? 最后貼上源碼地址,有興趣的同學(xué)歡迎star兼犯,共同學(xué)習(xí)探討動(dòng)畫知識(shí)忍捡!