首先來吐槽一番
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 關鍵詞搜索就行
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 這個是結束點的坐標
0 0,1 第一個0是r軸的選擇角度還是啥來著 默認是0不管他 具體我也不太清楚別人都是這么說的
第二個0是畫大弧度還是小弧度 的意思 兩個點可以畫出弧度最小和最大 看圖
再看0,1中的1 這個是鏡像的意思 默認是順時針方向 比如0->小弧度的這個圖 是順時針的情況 現(xiàn)在將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 沒問題 那我們在使用arcTo 剛剛上面已經分析過了
然后看效果 一臉懵逼 臥槽 不按套路出牌
是不是感覺要炸了 我也要炸了 不知道為什么他畫出這個來 理論上來講 兩個點的圓弧 應該不會畫出一個圓來 很奇怪 我也看了很久么看錯有什么不對 希望有會的人告知一下 我們這里用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 希望有幫助
當你在穿山越嶺的另一邊
我在孤獨的路上沒有盡頭
一輩子有多少的來不及
發(fā)現(xiàn)已經失去
最重要的東西
恍然大悟早已遠去
為何總是在犯錯之后
開始相信錯的是自己