利用Canvas和requestAnimationFrame API繪制圓形進度條動畫

平臺:React

相關(guān)技術(shù): Canvas API ,?requestAnimationFrame API


剛?cè)肼毑痪孟丝兀拥揭粋€需求,關(guān)于重構(gòu)課程列表,撇去蛋疼的看源碼修改原有的顯示邏輯之外磷斧,當時最令我印象深刻的是這個圓形進度條的制作過程动分。

在看到這個需求時毅糟,我首先想到能不能用css3進行實現(xiàn),畢竟從性能來說css3是較好的刺啦,但是發(fā)現(xiàn)這樣最后尾巴的小點沒辦法解決留特,而且技術(shù)難度貌似不小,最終部門小伙伴說你可以用canvas嘗試一下,并直接給我一個靜態(tài)demo蜕青,感恩~苟蹈,我也趁機研究了一下canvas api的使用。

需求

首先我們拿到這個圖案先拆分為4個部分右核,分別說外圓灰色細圈慧脱,外圓橘色細圈,內(nèi)部橘色圈贺喝,以及尾巴的小點, 下面針對一些核心繪制API進行分析

這里講一下比較蛋疼的最外側(cè)的圓弧和那個點的開發(fā)菱鸥。

最外側(cè)的圓弧核心代碼如下:(取context這種基礎(chǔ)咱就不寫了)

????????context.clearRect(0, 0,直徑, 直徑);

? ? ? ? context.beginPath();? ?<=開始繪制 ,包工頭說開始搬磚了

? ? ? ? context.lineWidth = 任意粗細躏鱼;?<=定義繪制粗細?

? ? ? ? context.strokeStyle = '定義畫圈的顏色';

? ? ? ? context.arc(直徑 / 2,? ?直徑 / 2,? ? 直徑 / 2 - 2*傳入半徑,? ?0,? ? ?傳入需要的百分比數(shù)值 * 0.02 * Math.PI -?0.5 * Math.PI? ? ?false);?<=重要氮采,繪制圓形路徑

? ? ? ? context.stroke(); <=針對strokeStyle部分結(jié)束繪制

? ? ? ? context.closePath();<=結(jié)束路徑 - 包工頭說下班了

這里主要講一下這里運用context.arc的繪制思路,這里請大家跟我回憶w3school上關(guān)于這個API的用法定義

w3school參考圖(1)

前兩個不用說染苛,確定中心點鹊漠,第三個通過傳入一個半徑,通過減去茶行,切分的做法躯概,"漏"出一個傳入值的弧圈,可謂比較討巧畔师,原理看圖好懂點娶靡。


傳入值

而起始角和結(jié)束腳我們需要通過下面這個圖(w3school參考圖(2))說明,起始點0(-0.5 * Math.PI)

結(jié)束點:傳入需要的百分比數(shù)值 * 0.02 * Math.PI -0.5 * Math.PI 看锉,先別急問0.02怎么來的姿锭,分析一下。

其實可以理解為-0.5 * Math.PI(也就是1.5 * Math.PI所在位置)就是起點度陆,因為最大值也就是1.5*PI艾凯,所以這里的增值最多為-0.5 + x = 1.5? ?x = 2,那么? 2/100 = 0.02份/1%懂傀,那么我們上面的公式就是這么來的趾诗。


w3school參考圖(2)

那么上面的圓弧算是開發(fā)完畢了,我們來做一個更難的蹬蚁,那個點的跟蹤計算恃泪。

首先先上核心代碼

context.beginPath();

context.strokeStyle= "#FF9C00";

context.lineWidth= 2;

context.fillStyle= "#FF9C00";

let radian= 傳入進度 / 100 * 2 * Math.PI - 0.5 * Math.PI;

let x= Math.cos(radian)* (大圓弧直徑 / 2 - 2*裁去的半徑)+ 直徑 / 2(圓心x點);

let y= Math.sin(radian)* (大圓弧直徑 / 2 - 2*裁去的半徑)+ 直徑 / 2(圓心y點);;

context.arc(x, y, 0.8 * r, 0, 2 * Math.PI, false);

context.stroke();

context.fill();

context.closePath();

前四行為起手式級別,上面解釋過犀斋,這里不再進行解釋贝乎,我們主要分析第五行到第七行代碼。

這里最難的是怎么找到這個點的位置(x,y)進行擺放叽粹,而繪制這個圓點方法如上如法炮制即可览效,已經(jīng)比較簡單了却舀。

而我們知道數(shù)學(xué)公式里面求圓上的一點的公式

到了JS的Math.cos和Math.sin函數(shù),我們查閱文檔可以知道這兩個函數(shù)中的傳入值都是指的“弧度”而非“角度”锤灿,弧度的計算公式為: 2*PI/360*角度挽拔;那么你會說這樣最高豈不是有2*Math.PI ? 還記得我們上面的圖嗎,最高也就是1.5*Math.PI,所以我們這里需要手動減去0.5*Math.PI 但校。因為我們在最上面的需求已經(jīng)可以得到弧度 :傳入進度 / 100 * 2 * Math.PI- 0.5 * Math.PI的公式螃诅,所以通過它,我們可以得到一個類似xx Math.PI的弧度值状囱。

如果要求算出(x1术裸,y1)在(x0,y0)為圓心的圓上亭枷,其所在坐標袭艺,我們可以使用如下的公式得到圓點的位置。

x1 = x0 + r * cos(弧度值)

y1 = y0 + r * sin(弧度值)

那么到此我們終于可以繪制出靜態(tài)的demo圖了叨粘,慢著匹表,你以為就結(jié)束了嗎,naive

UI大佬:"要不咱加個動效?"

好吧~向大佬勢力屈服

那么怎么讓它動起來呢宣鄙?如果你仔細觀察會發(fā)現(xiàn)我們剛剛實現(xiàn)的繪制過程,都是傳入最終參數(shù)percent來讓它完成繪制的默蚌,如果我每一次 precent+1冻晤,會有什么情況?不用你猜绸吸,對的鼻弧,他會一次一次的繪制進度+1,這時候我突然想到"判斷渲染的條件锦茁,動畫攘轩,每次+1",腦海里浮現(xiàn)出面試復(fù)習時經(jīng)常出現(xiàn)的那個巨長的API码俩,requestAnimationFrame度帮,不就是最適合這種場景么.requestAnimationFrame API

實際實現(xiàn)思路就是,初始percent傳0稿存,通過判斷是否達到percent笨篷,如果不是,percent+1繼續(xù)再遞歸調(diào)用,而且他不會產(chǎn)生類似setTimeInterval定時器這種影響頁面性能和事件隊列的副作用瓣履。

一段實現(xiàn)代碼

progressRender(context, length, R, r, percent,slogan){

? ? ? ? ?if(slogan!==0) {

? ? ? ? percent += 1;

? ? ? ? if (percent < this.props.progress) {

? ? ? ? ? ? ? requestAnimationFrame(()=> {

? ? ? ? ? ? ? this.progressRender(context, length, R, r, percent)

? ? ? ? })

? ? ?}

}


最終我們能得到如下率翅,ps:隨便找的一個轉(zhuǎn)gif的網(wǎng)站,感覺卡卡的袖迎,但是實際還好冕臭,比較流暢腺晾。

最終效果

通過這個小需求,不僅有點鍛煉數(shù)學(xué)和邏輯能力辜贵,也有點考驗我們對需求應(yīng)變的機動準備悯蝉,如果需求增加了我們要怎么靈活的實現(xiàn)出來,總之念颈,繼續(xù)加油吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泉粉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子榴芳,更是在濱河造成了極大的恐慌嗡靡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窟感,死亡現(xiàn)場離奇詭異讨彼,居然都是意外死亡护盈,警方通過查閱死者的電腦和手機豆挽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門皿渗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毡代,“玉大人粟焊,你說我怎么就攤上這事轧拄∑缕辏” “怎么了司澎?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵卢佣,是天一觀的道長重荠。 經(jīng)常有香客問我,道長虚茶,這世上最難降的妖魔是什么戈鲁? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮嘹叫,結(jié)果婚禮上婆殿,老公的妹妹穿的比我還像新娘。我一直安慰自己罩扇,他們只是感情好婆芦,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著暮蹂,像睡著了一般寞缝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仰泻,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天荆陆,我揣著相機與錄音,去河邊找鬼集侯。 笑死被啼,一個胖子當著我的面吹牛帜消,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浓体,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼泡挺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了命浴?” 一聲冷哼從身側(cè)響起娄猫,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎生闲,沒想到半個月后媳溺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡碍讯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年悬蔽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捉兴。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝎困,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出倍啥,到底是詐尸還是另有隱情禾乘,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布虽缕,位于F島的核電站盖袭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏彼宠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一弟塞、第九天 我趴在偏房一處隱蔽的房頂上張望凭峡。 院中可真熱鬧,春花似錦决记、人聲如沸摧冀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽索昂。三九已至,卻和暖如春扩借,著一層夾襖步出監(jiān)牢的瞬間椒惨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工潮罪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留康谆,地道東北人领斥。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像沃暗,于是被迫代替她去往敵國和親月洛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容