|
相信很多朋友和我一樣很喜歡QQ上“一鍵退朝”的功能,就是把紅點(diǎn)從它原本的地方拉走,消息提醒也就沒(méi)有了。 ![]() 一鍵退朝功能示意圖 直到如今我還是覺(jué)得這個(gè)功能很酷炫!于是想自己實(shí)現(xiàn)一番,經(jīng)過(guò)一番調(diào)查知道拉伸其實(shí)就是由兩個(gè)圓加上兩條貝塞爾曲線組成的形狀。 來(lái)看看騰訊設(shè)計(jì)師是怎么設(shè)計(jì)出來(lái)的吧:《QQ手機(jī)版 5.0“一鍵下班”設(shè)計(jì)小結(jié)》 看完了這個(gè)對(duì)實(shí)現(xiàn)思路有很大的幫助,可是我還是不能知道具體是怎么計(jì)算實(shí)現(xiàn)的,網(wǎng)上大部分的教程都是假想成了兩個(gè)同樣大小的圓來(lái)計(jì)算,這太取巧了!因?yàn)橥瑯哟笮〉膱A兩條外公切線是平行的,同一個(gè)圓上的公切點(diǎn)相連是會(huì)垂直于連心線的,但是大小不同的圓并沒(méi)有這個(gè)特殊性! 另外網(wǎng)上也有很多仿照的項(xiàng)目,可是看算法看得頭都大了也不明白為什么是這樣算的!經(jīng)過(guò)兩天的研究,把初中數(shù)學(xué)(圓、三角函數(shù)等相關(guān)知識(shí))好好復(fù)習(xí)了一遍,終于搞清楚了其中算法,現(xiàn)在跟我一起來(lái)看看吧! 1.得到連心線通過(guò)觀察可以發(fā)現(xiàn),在“一鍵退朝”這個(gè)功能當(dāng)中,有一個(gè)小圓固定在原來(lái)坐標(biāo)位置不動(dòng)的,只是半徑會(huì)發(fā)生變化,另一個(gè)大圓是跟隨著我們手指滑動(dòng)到屏幕的位置來(lái)確定圓心坐標(biāo)的,一般大圓的半徑是固定的。 建立兩圓的相對(duì)坐標(biāo)系:
假設(shè)某一個(gè)時(shí)刻,兩圓的狀態(tài)如圖,我們現(xiàn)在可以確定的是小圓的圓心坐標(biāo) O 為(startX, startY),大圓的圓心坐標(biāo) P0 為 (x0, y0),以及小圓的半徑 r 和大圓的半徑 R 。 那么首先可以把連心線求出來(lái)!也就是 O P0 的距離。 ![]() 2.求切點(diǎn)坐標(biāo)復(fù)習(xí)一下初中數(shù)學(xué):
我們?cè)僮饕粡堄泄芯€的圖: 切點(diǎn)為 P1、P2、P3、P4,我們現(xiàn)在目的就要求出這四個(gè)點(diǎn),然后就能夠在程序中畫(huà)出切線。 整個(gè)算法最難的地方恐怕就是求這四個(gè)點(diǎn)了,我們需要借助作圖來(lái)幫助計(jì)算,這之前還需要先復(fù)習(xí)下定理:
![]() 再作幾個(gè)輔助點(diǎn) A、B、C、D,AB 表示以大圓圓心為原點(diǎn)的坐標(biāo)系的 x 軸的兩端,CD 表示以小圓圓心為原點(diǎn)的坐標(biāo)系的 x 軸的兩端, ![]() 3.求剩下兩個(gè)切點(diǎn)的坐標(biāo)一開(kāi)始我以為 P3、P4 的算法和 P1、P2 一樣,就是把上面的減號(hào)換成加號(hào)就可以了。可是后來(lái)驗(yàn)證后發(fā)現(xiàn)不對(duì), P3、P4 不能直接使用 β 進(jìn)行運(yùn)算。
如上圖作輔助線。 ![]() 4.畫(huà)貝塞爾曲線把四個(gè)切點(diǎn)坐標(biāo)求出來(lái)了,后面就簡(jiǎn)單了,現(xiàn)在就是以切線為原軸,畫(huà)貝塞爾曲線了,不過(guò)我們還缺少一個(gè)控制點(diǎn)的坐標(biāo)。 4.1 科普貝塞爾怕有不清楚貝塞爾曲線的朋友,我科普一下先,簡(jiǎn)單來(lái)說(shuō)就是求一段平滑曲線的公式。 如果我們把畫(huà)一條直線分為進(jìn)度100%的話,那么當(dāng)進(jìn)度為0%,12%,58%,74%時(shí),畫(huà)線的狀態(tài)為(注意紅色部分末的黑色端點(diǎn),灰色部分為路徑指示) ![]() ![]() ![]() 那么把所有時(shí)刻的黑點(diǎn)連接起來(lái)就構(gòu)成了直線: ![]() 這個(gè)概念應(yīng)該比較容易接受,好了繼續(xù)。 二次貝塞爾曲線(最簡(jiǎn)單的貝塞爾曲線)的作法首先需要兩個(gè)點(diǎn)確定一條直線,另外在直線外確定一點(diǎn)(即控制點(diǎn)),然后此時(shí)三點(diǎn)會(huì)形成三個(gè)線段,即下圖的P0 P2、P0 P1和 P1 P2$(其實(shí)不用關(guān)注 P0 P2) ![]() 這只是進(jìn)度為0時(shí)候的狀態(tài),按照上面概念,當(dāng)進(jìn)度 t 從 0 變化到 100 時(shí)的某一個(gè)時(shí)刻,比如 30, 66 ,99,那么各個(gè)時(shí)刻 P0 P1 和 P1 P2 的狀態(tài)為 ![]() t=30 ![]() t=66 ![]() t=99 可以發(fā)現(xiàn),在 P0 P1 和 P1 P2 上有一直運(yùn)動(dòng)的兩個(gè)點(diǎn),我們將這兩個(gè)點(diǎn)連接起來(lái)又形成一段新的線段,而在不同時(shí)刻,在這個(gè)新線段上同樣會(huì)有一個(gè)運(yùn)動(dòng)的點(diǎn),這個(gè)點(diǎn)也遵守 t 的變化。 ![]() t=30 ![]() t=66 ![]() t=99 把所有時(shí)刻的黃色點(diǎn)連接起來(lái),就形成了二階貝塞爾曲線。 ![]() 還不能理解的可以看下這個(gè)視頻 - > 《bezier curve原理》- > 只要看就好,聽(tīng)不懂英文的可以把聲音關(guān)掉。 費(fèi)這么大勁把二階貝塞爾講了一遍,我們這里其實(shí)也只用到了二階,高階我就不講了,一通百通。 4.2.尋找控制點(diǎn)那么現(xiàn)在線段已經(jīng)能確定了,就是兩條公切線線段(P1P2、P3P4),那么控制點(diǎn)在哪呢? 這個(gè)其實(shí)有點(diǎn)靠猜了=。= 一開(kāi)始我覺(jué)得應(yīng)該在連心線的中點(diǎn),其實(shí)實(shí)現(xiàn)后效果也還行,后來(lái)參照騰訊設(shè)計(jì)師的想法效果更好,他令 P1P2 的控制點(diǎn)為 P1P4 的中點(diǎn),令 P3P4 的控制點(diǎn)為 P2P3 的中點(diǎn)。 ![]() 軟件實(shí)現(xiàn)效果對(duì)比(上邊控制點(diǎn)是連心線的中點(diǎn),下邊是騰訊設(shè)計(jì)師提出的控制點(diǎn)): ![]() ![]() 我個(gè)人覺(jué)得下邊效果更好,也不得不佩服TX設(shè)計(jì)師的聰明才智,讓我自己想可能永遠(yuǎn)也想不到。 至于求 P1P4 和 P2P3 的中點(diǎn)不難吧?連四個(gè)坐標(biāo)點(diǎn)都求出來(lái)了,直接算就可以了! --- 源碼地址:https://github.com/Xieyupeng520/AZMetaBall(還會(huì)不斷完善的,求星星^3^) ![]() |
|
|
來(lái)自: leafit > 《201511-12》