談到貝塞爾曲線偎漫,很多人會覺得高逼格、復雜有缆、頭疼象踊,實則不然,貝塞爾曲線經(jīng)過android封裝棚壁,已經(jīng)顯得嬌俏可愛杯矩,簡單好用,之前一些紅極一時的效果也均是由其打造袖外,比如QQ的“一鍵退潮”效果史隆、電子書曲面翻頁效果...... 現(xiàn)在咱們就用貝塞爾曲線一起從0到1打造一個擁有極致體驗、清秀靈動的GABottleLoading效果曼验;
好了泌射,不多吹NB了粘姜,老規(guī)矩先上一個原始效果圖:
如果你想看 [GAStudio Github主頁][2],請戳[這里][2]熔酷;
如果你想看 [GAStudio][3]更多[技術文章][3]孤紧,請戳[這里][3];
歡迎加入GAStudio交流QQ群: 277582728;
[github地址:][1]https://github.com/Ajian-studio/GABottleLoading
看到這個效果纯陨,估計有人開噴:
“我擦坛芽,聽你吹半天NB留储,這個效果老子兩年前就看過了翼抠,github上早有了,垃圾......”
此時获讳,沉穩(wěn)優(yōu)雅阴颖、帥氣逼人的GA哥在github上通過關鍵字搜索,兩個實現(xiàn)赫然出現(xiàn)在我的面前丐膝,看來天不助我量愧,然后通過查看他們的實現(xiàn),發(fā)現(xiàn)其中一個實現(xiàn)的很棒帅矗,老實說GA哥都沒有信心實現(xiàn)到如此完美偎肃,but看過之后發(fā)現(xiàn)他是直接加載gif,fuck浑此, too young, too simple, 另一個呢累颂?通過代碼實現(xiàn),現(xiàn)在讓我們瞻仰下他實現(xiàn)的效果:
從效果上來看凛俱,基本實現(xiàn)了瓶身紊馏、波紋晃動,但真正復雜的氣泡與動態(tài)并傾斜的水面的脫離過程卻并未實現(xiàn)蒲犬,而這才是GA哥感興趣的地方朱监,此時天空響起一聲驚雷,GA哥閃亮登場原叮;
首先赫编,讓我們一起分解下這個動效,簡單來看奋隶,該效果可以分成以下幾個部分:
1.水滴從水面彈出和融入擂送;
2.水面的波動;
3.瓶身的繪制达布;
接下來团甲,咱們一起從以上三個內容逐個處理:
一、水滴從水面彈出和融入
正式分析之前黍聂,請和GA哥一起通過一個慢鏡頭看看其中一個水滴脫離水面的過程;
我擦躺苦,不看不知道身腻,沒想到過程如此細膩!
是不是突然感覺這個動效沒那么簡單了匹厘?是不是有點難度了嘀趟?尤其是水滴離開水面的過程中,水面還在不斷的變動愈诚,而整個水滴彈出和融入的過程都需要和水面柔和爽朗的連接;
俗話說她按,“擒賊先擒王”,咱們第一步就來搞定水滴的粘連出入過程炕柔!
首先我們來看一下過程分解圖(請關注左邊的水滴):
由上圖可以觀察到酌泰,到(4)的時候,水滴已經(jīng)完全脫離了水面匕累,只不過存在一定的粘連(由于水的張力)陵刹;
接下來我們分析下水滴脫離水面的過程,為了更好的說明欢嘿,將水面簡化為一個靜態(tài)的斜面衰琐,這樣更加直觀;
那么原始模型如下圖炼蹦,其中斜線代表水面羡宙,圓代表水滴,是不是很簡單掐隐?
接下來我們需要考慮狗热,如何處理水滴和水面的粘連效果,標題既然叫做貝塞爾曲線打造極致GABottleLoading效果瑟枫,咱們肯定是使用貝塞爾曲線這一神器了斗搞,既然使用貝塞爾曲線,那么不用多說慷妙,就需要考慮起始點僻焚、終點、控制點這些核心數(shù)據(jù)膝擂;
從效果來看虑啤,起始點咱們可以直接從水滴上取,然而具體取到何處呢架馋?咱們可以以水滴冒出水面的高度為基準狞山,然后定義一個適當比例,以該比例計算具體數(shù)據(jù)點叉寂;
既然起始點在水滴上萍启,那么終點毋庸置疑在水面上,具體取于何處呢?咱們可以采用如下方式(至于為何要這么取則是GA哥的思路):
使用一個矩形框框住水滴區(qū)域勘纯,使水滴距離左右兩邊 L1局服、L2,并且L1 == L2 ;
此時矩形框與水面形成交點w1驳遵、w2淫奔,咱們可以直接選取如圖所示w1、w2作為兩個終點堤结,這兩個點即表示水滴由于張力而形成的拖尾和水面的接觸點唆迁;
經(jīng)過以上思路,咱們畫出以下圖示:
看上面這張圖竞穷,其中L3為水面上點w1和w2的連接線唐责,L4為經(jīng)過圓心并且和L3垂直的直線,wd為L3與L4的交點来庭,妒蔚,Ct為圓最頂端數(shù)據(jù)點,C1月弛、C2為垂直于L4的L5與圓環(huán)的交點;
咱們將輔助線都去掉,那么就得到如下的圖:
在圖上科盛,C1帽衙、C2為起始點,W1贞绵、W2為終點厉萝;
好了,起始點榨崩、終點咱們定下了確定的方法谴垫,控制點呢?
且繼續(xù)看下圖母蛛;
圖中L6翩剪、L9分別為點w1及w2所在的水面的切線,L7彩郊、L8分別為C1及C2處的切線前弯,q1為L6與L7的交點、q2為L8與L9的交點秫逝,q1與q2則為咱們找的兩個控制點恕出;
到此,包括起始點违帆、終點浙巫、控制點在內的貝塞爾曲線所需的核心數(shù)據(jù)咱們就都找到了,如下圖所示的6個點刷后;
6個點的畴、6個點廉油、6個點,重要的事情說三遍......
然后咱們利用以上6個點繪制兩條二階貝塞爾曲線苗傅,形成相應拖尾粘連效果抒线,具體效果圖如下:
我們把不需要的點去除,并填充上顏色渣慕,看看最后的效果:
那么這部分整體效果的結果如何嘶炭?請看!
這個不夠直觀逊桦?那讓我們加上輔助點眨猎,請看!
- 圓上的白色的點從左往右分別是c1强经、c2睡陪,分別表示拖尾與圓的接觸點,即貝塞爾曲線的起始點匿情;
- 圓兩側兩側紅色的點從左往右為分別為w1兰迫、w2,表示拖尾與水面的接觸點炬称,即貝塞爾曲線的終點;
- 藍色點從左往右分別為q1汁果、q2,分別表示左右兩側的控制點玲躯;
最后据德,圓完全脫離水面如下圖:
圓拖著拖尾上移:
最后拖尾斷裂:
最后水滴完全脫離,水面恢復平靜:
讓我們一起來看看整個過程:
ok跷车,到此棘利,水滴從水面彈出和融入的思路分析就此結束;
二朽缴、水面的波動分析
同樣善玫,咱們先看下原始效果圖:
github上已實現(xiàn)效果圖:
沒有對比就沒有傷害,看起來是不是覺得原始效果圖要柔和自然很多不铆?那么到底是什么原因呢蝌焚?GA哥分析主要是以下兩點原因:
- 1.波動效果,原效果圖是一個減速過程誓斥,當水波達到最高點的時候速度變?yōu)榱?只洒,而對比圖是一直勻速的過程;
- 2.上面流動的水和下面靜止的水的連接處理有差異劳坑;
當然毕谴,以GA哥的尿性肯定是以原效果圖為目標,而當GA哥在PS中采用三階貝塞爾曲線去擬合的時候,發(fā)現(xiàn)還是存在一定的瑕疵涝开,不能完全的擬合上;
最終循帐,最右邊的連接弧度采用上圖所示數(shù)據(jù)作為參數(shù),實現(xiàn)的效果如下:
我擦舀武,連接處不夠柔順拄养,此時GA哥采用了以下處理方案;
將波動的水面抬高银舱,和底部靜止的水面保持一定的距離瘪匿,然后采用二階貝塞爾曲線將兩者的連接處進行連接:
恩,上圖的效果還是可以接受的寻馏,讓我們看看動起來什么效果:
- 作者注:恩棋弥,此時如GA哥的秀發(fā)般飄逸了! - GAStudio哥
三诚欠、瓶身的繪制
瓶身繪制就是一個字——"扣"顽染,扣細節(jié),然后達到各個接觸點比較完美的連接;
讓我們從左半部分的上方開始講起轰绵。
瓶口處的的彎角粉寞,一開始GA哥也是認為是一個半圓(180度),然后再連接直線藏澳。然而這樣做效果不是很好仁锯,所以GA哥采用四分之一圓環(huán)(90度),然后連接45度的直線翔悠,最后連接垂直的瓶嘴直線,效果如下圖:
- 路人甲:納尼野芒?怎么有這么明顯的棱角蓄愁?作為細致的GA哥,依舊沒有放過這個細節(jié)狞悲。
- 路人乙:敢問GA哥有什么有效的方案撮抓?使用Paint.Cap.ROUND?
- GA哥:No, No, No, Paint.Cap.ROUND 只對line的頭部有效摇锋,GA哥采用了將Paint的PathEffect設置成了CornerPathEffect丹拯,那么就會將直線和直線間的連接處,自動做了圓滑處理荸恕。
那么效果如何呢乖酬?請看下圖:
臥槽,好帥融求,好柔滑有木有咬像!
此外,瓶身連接處(如下圖紅色標注處)也是需要注意的,要么計算準確县昂,完美的連接肮柜;要么索性有一點缺口,采用arcTo繪制瓶身倒彰,此時缺口會自動連接上直線审洞,再加上之前配置的CornerPathEffect,就會使得該處顯得自然:
接著瓶身采用圓弧繪制待讳,從多少到多少角度都需要進行計算;
最后底部采用直線直接連接;
- GA哥: 恩芒澜,你們是不是覺得GA哥會說,右半部分和左半部分采用一樣的思路耙箍?
- 路人甲: 難道不是嗎撰糠?難道GA哥有啥高招?
- GA哥: 恩辩昆,GA哥這個人比較懶阅酪,所以什么事都想偷懶;
恩汁针,我們都不用仔細觀察术辐,這個瓶子是左右對稱的!所以直接反轉過來就行了哇施无!
關鍵代碼如下:
Matrix matrix = new Matrix();
camera.save();
camera.rotateY(HALF_FULL_ANGLE);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-mViewRectF.centerX(), -mViewRectF.centerY());
matrix.postTranslate(mViewRectF.centerX(), mViewRectF.centerY());
Path rightBottlePath = new Path();
rightBottlePath.addPath(mBottlePath);
mBottlePath.addPath(rightBottlePath, matrix);```
### 總結
---
> * GA哥:總結辉词?總結啥呢?
> * 路人甲:看看效果唄猾骡,看你吹了這么久瑞躺,哈哈!
> * 路人乙:贊同樓上的;
> * 路人丙:贊同樓上的;
> * 路人缎讼搿:贊同樓上的;
> * ...
> * GA哥:盛情難卻哇幢哨,那就獻丑了!
![](http://upload-images.jianshu.io/upload_images/4083252-41e36c9ba39631e1?imageMogr2/auto-orient/strip)
**最后嫂便,附上GAStudio技術交流群和Github捞镰,喜歡的話歡迎follow和star:**
![](http://upload-images.jianshu.io/upload_images/4083252-5e507cb5ccb44847?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
[github地址:][1] https://github.com/Ajian-studio/GABottleLoading
**如果你想看 [GAStudio Github主頁][2],請戳[這里][2]毙替;**
**如果你想看 [GAStudio][3] 更多[技術文章][3]岸售,請戳[這里][3];**
**歡迎加入GAStudio交流QQ群: 277582728;**
[1]: https://github.com/Ajian-studio/GABottleLoading
[2]: https://github.com/Ajian-studio
[3]: http://blog.csdn.net/tianjian4592