ReactNative圓形進度條 ART Path arcTo 圓弧實現(xiàn)

首先來吐槽一番
Facebook 對ReactNative添加了ART庫后 竟然沒有官方文檔說明這個咋用 簡直可怕.... 并不是每個來做RN開發(fā)都是懂得前端常常使用SVG Path 這些東西的啊 大哥

同時相比于Android 等開發(fā) RN還比較新 導致的結果是遇到問題后 能用的資料少啊绢淀。评抚。僵腺。這種情況下 連最應該有的文檔都沒有 吐血三升先钥勋。。。

试读。警儒。心里苦。袄简。腥放。

所以我就查了一下RN開發(fā)的繪圖庫ART 發(fā)現(xiàn)不是canvas之類的(Android中用canvas習慣了) 最可怕的是網上只有一些簡單的關于ART的說明 和一個大兄弟封裝了一個SVG基于ART的庫 就和在前端使用SVG那樣使用來繪制 我不想這樣使用 就只能自己摸索著開始

在開始畫圓弧進度條之前 我們可能要先熟悉一下SVG Path(RN的path內部工作也是一樣的) 是如何工作的 以及 里面的一些參數(shù)的具體作用 這里 因為是對SVG Path的介紹 采用React.js畫出一些效果 來看看path是如何繪制出我們想要的圓弧的 我這里也就根據(jù)幾個效果簡單說一下 具體可以自己去查相關的只是 SVG Path 關鍵詞搜索就行

Path 圓弧繪制

d="M100,0 A50,50 0 0,1 100,100"

看看這段字符串 寓意
M開頭表示在坐標軸中的起始位置x,y
A表示即將繪制圓弧 后面是繪制圓弧半徑的意思 一個是x軸的半徑 一個是y軸的半徑 其實就是在用弧度把這兩個點連起來的時候 x比y大 x方向就長一些 如果x=y=radius 就是一個圓的弧度
這里應該涉及到橢圓的相關知識 具體我也不太懂 大概猜是這個意思
繼續(xù) A50,50 0 0,1 100,100 這是一個完整圓弧的全部參數(shù)
先說最后的100,100 這個是結束點的坐標

150,70 結束坐標

0 0,1 第一個0是r軸的選擇角度還是啥來著 默認是0不管他 具體我也不太清楚別人都是這么說的
第二個0是畫大弧度還是小弧度 的意思 兩個點可以畫出弧度最小和最大 看圖
0,1->小弧度

1,1->大弧度

再看0,1中的1 這個是鏡像的意思 默認是順時針方向 比如0->小弧度的這個圖 是順時針的情況 現(xiàn)在將1改成0 看圖
0,0-->情況

1,0-->情況

還有一個漸變的使用 順便也貼一個圖吧
漸變效果

好了 A后面的幾個參數(shù)的意思 這幾個圖應該能看懂了 如果我這里沒有解釋很清楚 (我也是剛會 可能解釋不太清楚 ) 可以自己再去查查SVG Path相關的知識點


現(xiàn)在理解了這個Path的相關參數(shù)的含義 后面我們看RN的art的時候也就很好理解了 我們現(xiàn)在來看RN的ART怎么使用
從 ‘react-native中引入 ART’ ART中有一些模塊
直接進去ReatART源碼

ART源碼提供的功能模塊

東西還挺多的 這里我們用到Surface (Group(Shape(Path)))
然后這里我看見他有一個類 LinearGradient 這個應該是用來實現(xiàn)剛剛React中那種漸變效果的 但是這里我很努力地去把他這個和Shape對接起來
對于LinearGradient 這個對象 需要一個Colors集合 這個Color對象也是ReatART提供的
這里我不知道怎么構造這個LinearGradient對象 求教哪位會的 麻煩告訴我一下
LinearGradient

LinearGradient對象傳入最后會走這個方法

現(xiàn)在轉移到圓弧的進度條的中心思想來 先思考圓弧進度條怎么畫

現(xiàn)在假設我們某個頂部的點是起始點(100,0)(top) 半徑50
這樣如果我們來畫一個圓 四個最特殊的點的坐標如下
left-->(0,50)
bottom->(100,200)
right->(150,50)

圓的四個外圍點

現(xiàn)在我們知道了這四個外圍點坐標 這是特殊點 我們和容易畫出來 現(xiàn)在問題是怎么畫出剩余部分的 比如下圖的 我們想滑到A點
A點的圓弧

想畫到A點我們首先要找到A點的坐標 這里就要用到sin cos來計算 因為角度一定的情況下 確定了radius
那么在這個點就確定了 比如假設紅色部分的角度是30度
那么 A點的坐標 就是 x=100+sin(角度) y=半徑-cos(角度)
角度對于的值

因為sin cos涉及到正負 所以這里我們分層四部分處理
0-90 90-180 180-270 270-360
這里還要提醒一點 因為是圓弧 兩點一個弧度 所以一整整圓不可能一個弧度完成(我是這么理解的 要是可以的話可以告訴我下 這樣我代碼部分也能簡單點) 我們這里就轉個彎 用兩個半圓 左邊半圓180-360 右邊圓0-180 下面是計算公式 也是核心代碼

/**
 * 計算目的坐標位置 右邊 <180度的計算
 * @param progress
 * @param total
 * @param startX
 * @param startY
 */
function calTargetXY(progress, total, startX, startY, radius) {
    let degress = progress / total * 360;
    if (degress > 180) {
        //log(Tag, '強制 degress -> 180');
        degress = 180;
    }
    //log(Tag, "開始位置 " + startX + " " + startY + "  r: " + radius + " degress  " + degress);
    let target = [];
    if (degress <= 90) {
        degress = degress * 2 * Math.PI / 360;
        // log(Tag, "sin " + Math.sin(degress));
        let endx = startX + radius * Math.sin(degress);
        let endy = startY + radius - radius * Math.cos(degress);
        target.push(endx);
        target.push(endy);
        return target;
    }
    else if (degress <= 180) {
        degress = degress - 90;
        degress = degress * 2 * Math.PI / 360;
        //  log(Tag, "sin " + Math.sin(degress));
        let endx = startX + radius * Math.cos(degress);
        let endy = startY + radius + radius * Math.sin(degress);
        target.push(endx);
        target.push(endy);
        return target;
    }
}

/**
 * 左邊圓的計算 >180度的計算
 * @param degress
 * @param startX
 * @param startY
 * @param radius
 */
function calTargetXY1(degress, startX, startY, radius) {
    let target = [];
    //log(Tag, "開始位置1 " + startX + " " + startY + "  r: " + radius + " degress  " + degress);
    if (degress > 360) {
        degress = 360;
    }
    if (degress <= 270) {
        degress = degress - 180;
        degress = degress * 2 * Math.PI / 360;
        //  log(Tag, Math.sin(degress));
        let endx = startX - radius * Math.sin(degress);
        let endy = startY - ( radius - +radius * Math.cos(degress));
        target.push(endx);
        target.push(endy);
        return target;
    } else if (degress <= 360) {
        degress = degress - 270;
        degress = degress * 2 * Math.PI / 360;
        let endx = startX - radius * Math.cos(degress);
        let endy = startY - radius - radius * Math.sin(degress);
        target.push(endx);
        target.push(endy);
        return target;
    }
}

然后用到Art中 我們先看看 Path提供的API 發(fā)現(xiàn)他自己封裝了一層


Path源碼

然后我們發(fā)現(xiàn) 他是繼承了Path 然后自己實現(xiàn)了一些對外方法 我們看真實Path類 發(fā)現(xiàn)一個push方法


Path push方法

看到這里 push會先解析 說明傳進來肯定是一個字符串
而且會帶有m l s A M這些字母 然后這時候 是不是似曾相識 這東西不就是SVG path 里面 我們前面說的那一串字符串嗎 "Mx,y Arx,ry 0 0,1 x1,y1"

這時候 我們看到A 他里面比較怪 順序都是亂來的 我們比對前端的字符串 再來看看他的意思

"Arx,ry 0 0,1 x1,y1"
i=1;
case 'A': 
this.arcTo(p[i+5], p[i+6], p[i], p[i+1], p[i+3], !+p[i+4], p[i+2]); 
break;

>this.arcTo(p[i+5], p[i+6], p[i], p[i+1], p[i+3], !+p[i+4], p[i+2]); 
其實就是
this.arcTo(p[6], p[7], p[1], p[2], p[4], !+p[5], p[3]); 
也就是
this.arcTo(x1 ,y1, rx, ry, 0, 1, 0); 
0绿语,1秃症,0--》大小弧度,鏡像與否,x軸旋轉啥的默認不管,

然后這里我們發(fā)現(xiàn)其實push就已經能實現(xiàn)工作 只要我們在push里面?zhèn)魅胂胍淖址托?br> 同理 我們直接用arcTo應該也是可以的
讓我們來看看效果 先用push 看效果 畫一個100,20起點 150候址,70,90度的圓弧


直接使用push

好 push 沒問題 那我們在使用arcTo 剛剛上面已經分析過了


使用arcTo

然后看效果 一臉懵逼 臥槽 不按套路出牌
可怕种柑。岗仑。。

artTo異常效果效果

是不是感覺要炸了 我也要炸了 不知道為什么他畫出這個來 理論上來講 兩個點的圓弧 應該不會畫出一個圓來 很奇怪 我也看了很久么看錯有什么不對 希望有會的人告知一下 我們這里用push實現(xiàn)就行 也合理

其實說了那么多 重點實現(xiàn)進度的核心就兩個
一個是根據(jù)角度計算終點坐標
一個是push方法
下面先看看整個的效果吧
因為進度條是一個單獨的組件 中間區(qū)域留了一個位置 可以插入你想插入的View
效果圖中間的數(shù)字動畫使用Animated.createAnimatedComponent實現(xiàn)

  • 中間留空的區(qū)域 是根據(jù)傳入的半徑 獲取到了圓的區(qū)域 在計算中間內切正方形的區(qū)域 在通過絕對布局left top實現(xiàn) 具體可以看代碼


    效果圖

    內容說明
  • 使用方法很簡單


    一個無動畫 一個有動畫
  • 復雜配置


    很多的配置

代碼就不貼了 github有
如果對RN的ART arcTo 圓弧 不太熟悉的 可以看看Demo 希望有幫助

github地址 點我點我 可以的話 給個star

star star star

當你在穿山越嶺的另一邊
我在孤獨的路上沒有盡頭
一輩子有多少的來不及
發(fā)現(xiàn)已經失去
最重要的東西
恍然大悟早已遠去
為何總是在犯錯之后
開始相信錯的是自己

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末聚请,一起剝皮案震驚了整個濱河市荠雕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驶赏,老刑警劉巖炸卑,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異煤傍,居然都是意外死亡盖文,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門蚯姆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來五续,“玉大人,你說我怎么就攤上這事龄恋》蹬粒” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵篙挽,是天一觀的道長荆萤。 經常有香客問我,道長铣卡,這世上最難降的妖魔是什么链韭? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮煮落,結果婚禮上敞峭,老公的妹妹穿的比我還像新娘。我一直安慰自己蝉仇,他們只是感情好旋讹,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著轿衔,像睡著了一般沉迹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上害驹,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天鞭呕,我揣著相機與錄音,去河邊找鬼宛官。 笑死葫松,一個胖子當著我的面吹牛瓦糕,可吹牛的內容都是我干的。 我是一名探鬼主播腋么,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咕娄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了珊擂?” 一聲冷哼從身側響起圣勒,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎未玻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胡控,經...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡扳剿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了昼激。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庇绽。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖橙困,靈堂內的尸體忽然破棺而出瞧掺,到底是詐尸還是另有隱情,我是刑警寧澤凡傅,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布辟狈,位于F島的核電站,受9級特大地震影響夏跷,放射性物質發(fā)生泄漏哼转。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一槽华、第九天 我趴在偏房一處隱蔽的房頂上張望壹蔓。 院中可真熱鬧,春花似錦猫态、人聲如沸佣蓉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勇凭。三九已至,卻和暖如春义辕,著一層夾襖步出監(jiān)牢的瞬間套像,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工终息, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夺巩,地道東北人贞让。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像柳譬,于是被迫代替她去往敵國和親喳张。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內容