從一個漸變圓角進度條淺出畫一個圓
開始
這一切需要從一個(簡單)的需求開始,在最開始對設計第一眼看到這張圖的時候,感覺挺簡單的嘛,直接用echarts餅圖模擬出來一個就好了

echarts
然后上echarts試了一下發現實現不出來了

設計圖這邊采用的是錐形漸變,而echarts只有線性漸變和徑向漸變。
css
然后准備換種方案,css就有錐形漸變,然后通過conic-gradient加上mask畫出了一個漸變的環形然后可以再通過剪裁實現出進度的展示。
但是存在兩個問題,一個是conic-gradient屬性兼容性不好ie和火狐都不支持,二個是后來發現了還存在一個需求進度條的兩端需要有圓角,然后這種實現方式就不行了。
其實在寫這篇文章的時候才想到一個方法就是在兩端加上兩個半圓形,不過得計算半圓形的位置。
Canvas & SVG
在我的理解中在頁面上作圖總共有四種方式。
- dom+css
- Canvas
- SVG
- WebGL
WebGL是一頭霧水還是試試Canvas和SVG吧,因為更熟悉Canvas一些,我這邊就采用Canvas來試試。
Canvas可以輕松的實現圓角和環形,但是他的api里面居然沒有錐形漸變
然后就想着嘗試手動來實現一個錐形漸變,然后查閱資料看到了一篇文章手把手教你畫圓錐漸變,就是相當於畫圓嘛,我們可以通過一條線一條線的畫從而畫出一個圓,然后把兩端漸變的顏色通過計算找到中間畫圓的每一條線的顏色組合起來就是一個漸變的效果了。
然后問題就是給你兩個色值怎么計算中間的線段的顏色,其實對於rgba的顏色我們可以看到他是由四個數字組成的,那我把這四個數字分別求出四組長度相同且組內間隔相同的中間值那就可以得到顏色的中間值了,然后在搭配上張老師硬核的色值轉換JS HEX十六進制與RGB,HSL顏色的相互轉換那就可以實現出我們想要的效果了。
通過一個開始顏色和一個結束顏色,默認是rgba的顏色,num是分段數,就可以求出中間每一段的顏色了
// 把顏色分段
const beginColor = begin.slice(5, -1).split(',').map(item => Number(item))
const endColor = end.slice(5, -1).split(',').map(item => Number(item))
// 分段后的顏色儲存在這個數組
const middleColor = [[], [], [], []]
// 循環rgba四種
beginColor.forEach((item, index) => {
// 當前的值每段顏色之間的間隔
const differ = (endColor[index] - item) / (num - 1)
// 循環分段數的次數
for(let i = 0; i< num; i++) {
// 每次加上這個間隔
middleColor[index].push((item + differ * i).toFixed(2))
}
})
console.log(middleColor)
console.log(num)
然后繪制的話就是一段一段的畫了,麻煩的地方就是計算每次從多少的角度畫到多少的角度
for(let i = 0; i< num; i++) {
ctx.beginPath()
// 這里是每次繪制的過程
// 每次繪制一段小圓弧
// 最后一段只需要畫一段就好
if(i === num - 1)
ctx.arc( 150 * dpr,150 * dpr, 100 * dpr, (Math.PI * 2 * value) / num * i, (Math.PI * 2 * value) / num * (i + 1));
else
ctx.arc( 150 * dpr,150 * dpr, 100 * dpr, (Math.PI * 2 * value) / num * i, (Math.PI * 2 * value) / num * (i + 2));
ctx.lineWidth = 60;
// ctx.lineCap = "round";
ctx.strokeStyle= `rgba(${middleColor[0][i]}, ${middleColor[1][i]}, ${middleColor[2][i]}, ${middleColor[3][i]})`;
ctx.stroke();
ctx.closePath()
}
結果非常的順利,就是自己想要的結果,具體怎么一段一段的畫,怎么求出顏色的中間值看這里
優化
其實把繪制的過程放慢看

就是這個過程,每次畫一段,每段不同的顏色組合起來就是一個漸變色,然后分段數再加多一點就會靠上去很流暢。
在完成后發現了幾個問題,首先是在分段數很少的時候就會出現一塊一塊的間隔

就像這樣,我大概分析了一下,猜測這個間隔出現的原因應該是我計算每一段的角度的時候肯定有除不盡的,我就四舍五入了,應該就會產生一些小間隔。
然后我就覺得把分段數提高應該就會好一些,然后就發現分段數高間隔會生成類似於摩爾紋的東西

然后就開始思考怎么去消除,最后想到了一種方案就是在每次繪制的時候繪制兩段的長度,然后移動只移動一段的長度,就會下一段覆蓋在上一段,就不會有間隔了,然后顏色漸變也還是一段一段的不會有影響。

然后還有一個問題就是有鋸齒,不清楚,解決方案也很簡單,就是把你的畫布放大指定倍數,然后半徑也放大同樣的倍數,最后dom的高寬不變,就會讓繪制的圖形更加的清晰。
總結
到此這個問題就算是解決了,然后我還順便寫了一個庫,大家有興趣的可以去使用一下,我還加上了數字,動畫也可以支持多段漸變gradient-ring-progress
通過這次的需求我收獲到了,做東西需要完全的去了解了需求再去確定實現方案然后再動手,實現方案其實有非常多種,我們需要找到的是最合適的解決方案。最后抄襲張老師的一句話
然而一個人的積累總是有限,而創意總是無限的,因此一定還有其他更好更妙更簡單的實現,歡迎分享歡迎指教!
