用css動態實現圓環百分比分配——初探css3動畫

最近的小程序項目有個設計圖要求作一個圓環,兩種顏色分配,分別表明可用金額和凍結金額。要是就直接這麼顯示,感受好像挺沒水平??因而我決定作個動態!css

在mdn把新特性gradients(漸變)、transitions(過渡)、 animations(動畫) 都看了一遍,不由感嘆css牛逼!這三個新特性加上canvas,彷彿一瞬間有了正面剛js的能耐。用js很難過渡得那麼完美,並且瀏覽器的css渲染明顯比用js性能好得多。
而後看了張鑫旭(傳說中玩轉css的那個男人)的一篇關於圓環的博文,拍案叫絕。連接=>3種純CSS實現中間鏤空的12色彩虹漸變圓環方法
只能說服氣!除了靈活運用各類css特性以外,鑫大佬最讓我佩服的是他的創造性思惟。會讓你不由感嘆:臥槽,還有這種操做?!想到了高中物理老師每次裝完逼講的一句話:思想有多遠,就能走多遠。html

雖然demo跟個人需求不太同樣,問題仍是沒有解決,但我認真看完以後仍是學會了不少,對我後面的代碼幫助很大。鑫大佬這篇博文的重點仍是在漸變,而我須要動態平緩連續得實現顏色的分配,好比本來整個環是綠色,而後慢慢地60%被紅色佔了,並且整個過程要平滑。跟個人需求最接近的就是倒計時那個demo,linear-gradient線性漸變實現的多彩圓環demo,但不是連續的過程,而是經過剪裁,每次剪30度。css3

看了其餘一些博客分享,好像也沒有找到合適的,那沒辦法了...只能本身想一個!
由於再寫這個demo的時候,發現小程序和H5在css表現上仍是有些差別(具體有哪些差別,在文末總結),因此仍是貼H5代碼好了。web

很少說,直接上代碼canvas


代碼部分

//html部分
<div class="circle">
  <div class="circle-left"></div>
  <div class="circle-right"></div>
  <div class="circle-bottom-left"></div>
  <div class="circle-bottom-right"></div>
</div>
<div class="info">¥4500/¥5000</div>
//css部分
.circle {
  -webkit-mask: radial-gradient(transparent 150px, #000 150px);
  width: 400px;
  height: 400px;
  overflow: hidden;
  border-radius: 50%;
  position: relative;
}

.circle-left {
  width: 50%;
  height: 100%;
  background: #24B39B;
  transform-origin: 100% 50%;
  position: absolute;
  left: 0;
  z-index: 0;
}

.circle-right {
  width: 50%;
  height: 100%;
  background: #24B39B;
  transition: transform 1s linear;
  transform-origin: 0% 50%;
  position: absolute;
  right: 0;
  z-index: 2;
}

.circle-bottom-left {
  width: 50%;
  height: 100%;
  background: rgb(234, 67, 15);
  position: absolute;
  left: 0;
  z-index: -1;
}

.circle-bottom-right {
  width: 50%;
  height: 100%;
  background: rgb(234, 67, 15);
  position: absolute;
  right: 0;
  z-index: 1;
}

.info {
  width: 400px;
  height: 400px;
  line-height: 400px;
  text-align: center;
  margin-top: -400px;  
}
//js代碼
window.onload = function () {
  var red = 4500, total = 5000 //紅色區域表明的金額和總金額
  var percent = red / total
  var right = document.getElementsByClassName('circle-right')[0]
  var left = document.getElementsByClassName('circle-left')[0]
  if (percent <= 0.5) {  //紅色區域不超過一半
    right.style.transform = `rotate(${percent * 360}deg)`
  } else {    //紅色區域超過一半的狀況,重點部分
    right.style.transform = `rotate(180deg)`
    right.style.transition = `opacity 0s step-end 1s, transform 1s linear` //timing-function須要設爲linear來達到視覺上的平緩過渡
    right.style.opacity = 0

    left.style.transition = `transform ${(percent - 0.5) / 0.5}s linear 1s`
    left.style.transform = `rotate(${percent * 360 - 180}deg)`
  }
}

效果圖
圖片描述小程序

思路

st=>start: 開始
e=>end: 結束
con=>condition: degree<=180?
op1=>operation: 右綠旋轉
op2=>operation: 右綠旋轉180度,opacity變爲0,而後左綠旋轉

st->con
con(yes)->op1->e
con(no)->op2->e

難點在於紅色區域大於一半的狀況,左右綠色半圓的銜接,過渡要天然,不能讓人看出什麼明顯的破綻。
**這種狀況個人作法是:4個半圓(紅綠各兩個)的z-index設爲左紅<左綠<右紅<右綠
兩個綠半圓的transform的time-function(時間函數)統一設爲linear(線性)。右綠旋轉180度(1秒)後opacity當即變成0(時間函數step-end),這樣就不會擋住左紅露出。而後左綠開始轉(transform延遲1秒執行,由於要等待右綠轉完),它轉的時間要根據度數動態控制,好比總共要轉270度,右綠轉了180度,因此左綠只須要轉90度。這就好辦了,爲了保持右綠的旋轉速度,時間和度數要成比例,右綠轉180度用1s,左綠轉90度只能用0.5s瀏覽器

優勢

1. 不須要js代碼動態實現動畫(js只用來計算度數和觸發transition)
2. 由於對js幾乎沒什麼依賴,瀏覽器內核直接渲染,性能較好,過渡天然
3. 代碼量不多

不足

1. 由於是css3的屬性,兼容不會太好
2. 時間函數只能用線性linear,用默認的ease(不勻速)會銜接不上
3. 只能兩種顏色分佈,再加一種的話行不通

有更好辦法實現相同效果的大佬,歡迎留言!微信

問題探究&解決

雖然效果圖gif畫質有點感人,但仍是能夠發現一個問題:內環邊緣明顯很粗糙!這個要怎麼解決呢?
中間這個透明遮罩的代碼是`-webkit-mask: radial-gradient(transparent 150px, #000 150px);
個人作法就是把transparent 150px改爲transparent 148px,就是說空出一兩個像素點,讓粗糙的部分虛化。
至於爲何會出現粗糙,額。。。我以爲是150px這一層上了太多顏色,加上原本畫弧圈就沒有防鋸齒處理,色素點可能會擁擠,加重了鋸齒狀這種效果。具體是什麼緣由,或者有更好的解決辦法,歡迎大佬指教。微信開發

修改後的效果圖
圖片描述
是否是明顯好不少~wordpress

上文提到的小程序的css和h5的差別,通過再一次的實驗,發現不是小程序內核渲染的問題,應該是微信開發者工具顯示的問題。。。但願儘快能獲得改善,否則對開發人員影響挺大的

clipboard.png這個info的盒子margin-top負數在工具中顯示翻不上去,但內容50000上去了.
clipboard.png過了幾秒再點(啥都沒幹),這個info的盒子又跑到這裏來

clipboard.png爲了驗證這個info的盒子到底有沒有上去,我加了一個紅色的盒子,發現並無被info盒子擠掉

clipboard.png取消info的margin-top屬性,紅色的盒子被擠掉,內容50000也下來了

終於!!!原來都是開發者工具擺的烏龍,其實info盒子一直在上面,只是調試不能正常顯示他的位置。。。

話說回來,小程序不能獲取DOM節點操做DOM,忽然以爲只能數據驅動,不能操做DOM節點有時也挺麻煩的,transition那些須要動態改的樣式只能寫到style了。。。

最後,看好小程序,但願各類問題能儘快完善,愈來愈好。

相關文章
相關標籤/搜索