CSS製做環形進度條

參考來源

Radial progress indicator using CSS》,該文核心是用純CSS來作一個環形的進度條。純css的意思就是連百分比這種數字,都是css生成的。文章做者採起的方式是生成100個span標籤,而後爲這100個標籤生成100段css代碼(用less生成,代碼量倒不大,只是生成的代碼量會很大),不知道有沒有更NB更省資源的css方案。
而個人需求很簡單,只須要學習怎麼畫環進進度條便可,進度掌控固然得由js來通知(好比下載進度,或者音樂播放進度)
對E文沒有恐懼感的話,建議直接看原做者的文,我這不是全文翻譯,而是本身的練習。css

注:爲了方便,我只對chrome寫了樣式,因此若是要運行我在jsfiddle裏面寫的例子,最好選webkit系的瀏覽器html

起步

原做者講解得很詳細,咱們直接跳過吧,本身學習css3關於動畫的部分,總之,先成功一個例子再說:css3

html

<div class="radial-progress">
  <div class="circle">
    <div class="mask">
      <div class="fill"></div>
    </div>
  </div>
</div>

一個容器,一個一環形元素,再加一個mask,和fill,後面介紹web

css

爲了直接在jsfiddle上使用,我也用less吧,畢竟只要一句js聲明便可客戶端解析(插一句,工程項目建議用sass,由於有compass這個庫,大大減輕工做量)chrome

@size:120px;
@bgcolor:#ddd;
@bgcolor1:#cc7;
@duration:1s;
*{margin:0;padding:0;}
.radial-progress{
    margin:30px 50px;
    position: relative;
.comm(){
    width:@size;
    height:@size;
    position: absolute;
    top:0;
    left:0;
    border-radius: 50%;
}
.circle{
    .comm;
    background: @bgcolor;
    .mask{
        clip:rect(0,@size,@size,@size/2);
        .comm;
    }
    .fill{
        background: @bgcolor1; 
        clip:rect(0,@size/2,@size,0); 
        transition:-webkit-transform @duration;.comm;}
    }
}

js

$('head style[type="text/css"]').attr('type', 'text/less');
less.refreshStyles();   
$(function(){
    var m=$(".fill"),el=$("#deg").val(60),cur=$("#cur");
    $("#start").click(function(){
        var deg=el.val()||60;
        m.css("transform","rotate("+deg+"deg)");
        cur.text(deg);
        el.val(Math.ceil(Math.random()*180));
    });
    $("#start").click();
});

講解

  • 要想弧形運動,首先想到了css3的rotate,而且rotate的旋轉原點正好是中心,不須要額外設置,因此咱們選擇了這個屬性。
  • 其次,進度條從無到有,顯然不能像直線進度條同樣,設置寬度和高度便可,咱們只能選擇一個層從另外一個層的遮擋中逐漸出現這種方案,這就是咱們取名叫mask和fill的緣由.
  • 上面就是咱們的實現:把進度條所屬的塊只顯示左半邊,而盛放進度條用的容器,卻只顯示右半邊,(只顯示半邊是怎麼作到的?學習CSS3 clip),這樣,咱們看不到任何進度條所屬的色塊,由於它壓根就沒出如今容器範圍內。
  • 而後咱們讓左邊的進度條色塊進行旋轉,一旦旋轉到右側,色塊天然就能在右側看到了(容器一直在右側,可是無底色,只有進度條有底色,因此核心就是讓進度條色塊經過旋轉,進入到容器所在的位置內
  • 此圖表示左邊色塊大概轉了一百多度轉到了右邊
  • 最後,加上50%的弧度,圓形就出現了
  • 看看效果:http://jsfiddle.net/walker/smMzz/,餅圖是製做成功了

360度

上面的例子作完後,你應該發現這種轉法,最多隻能轉180度啊!好吧,因而咱們如法泡製,畫一個左邊的容j器和右邊的色塊:瀏覽器

html

<div class="radial-progress">
  <div class="circle">
    <div class="mask left">
      <div class="fill"></div>
    </div>
    <div class="mask right">
      <div class="fill"></div>
    </div>
  </div>
</div>

css

@size:120px;
@bgcolor:#ddd;
@bgcolor1:#cc7;
@duration:1s;
*{margin:0;padding:0;}
.radial-progress{margin:30px 50px; position: relative;
    .comm(){
        width:@size;height:@size;position: absolute;top:0;left:0;border-radius: 50%;
    }
    .circle{
         background: @bgcolor;.comm;
        .mask,.fill{.comm;}
        .fill{
            background: @bgcolor1;
            transition:-webkit-transform @duration;
        }
        .left{
            clip:rect(0,@size/2,@size,0);
            .fill{clip:rect(0,@size,@size,@size/2);}
        }
        .right{
            clip:rect(0,@size,@size,@size/2);
            .fill{clip:rect(0,@size/2,@size,0);}
        }
    }
}

js不變,直接運行http://jsfiddle.net/walker/smMzz/1/

顯然不是咱們要的效果sass

改進

這時做者作了大膽的改動,別的進度條方案我還沒來得及研究,總之他這種是很是「彆扭」的,管它呢,先實現,後面的就都不貼代碼了,每一節後面都有我貼的jsfiddle的地址,能夠直接去看源碼。less

  • 首先,去掉了「左,右」蒙板的概念,而是把兩個蒙板都並列擺在右側(一個叫half,一個叫full),蒙板包含的色塊仍然叫fill,這樣js一次就同時旋轉了兩個色塊。
  • 這樣的結果確定是沒有任何變化啊!由於兩個層仍然是疊在一塊兒的。因此,做者又讓.full這個層整個蒙板也旋轉一樣的角度!【注意】,此時兩個蒙板其實已經不重合了。這樣,原本兩個重疊的色塊,由於某一個容器繼續旋轉了一樣的角度,好比30度,視覺上就出現了60度範圍的色塊!
  • 因此說彆扭,是彆扭在,咱們封裝這種進度條,其實最大隻須要旋轉180度。
  • 見結果:http://jsfiddle.net/walker/smMzz/2/
  • 最後,由於事實上是兩個色塊拼接的,拼接處有一條細線(只有在動畫進行的過程當中纔有),仔細看上面連接的演示。因而做者繼續給出解決方案:在half層裏面添加一個色塊,class也是fill,因此也會被js控制進行旋轉,可是這個fix的fill是直接按2倍角度旋轉的,這樣在旋轉的過程當中,由於速度的不一樣,它就擋住了那條白線(這裏才須要對js進行一點修改,2倍旋轉fix層)。
  • 若是對用戶體驗沒這麼關心(怎麼可能!),其實這件事能夠不作的。修改後見:http://jsfiddle.net/walker/smMzz/3/

把餅圖改爲環圖

至此已經大功告成,咱們中間再添加一個跟底色同樣的div把它變圓不就能夠了嗎?
對的,順便還加了點內陰影外陰影,這樣就有3D甜甜圈的效果了:http://jsfiddle.net/walker/smMzz/4/

若是要看代碼,就注意一下,這一步只是添加了一個.inset和一個.shadow,對應的css看源碼。dom

咱們這裏就不要3D了,簡化一下,把3D啊,陰影啊,都去掉:http://jsfiddle.net/walker/smMzz/5/函數

模擬進度條

至此,我參考的老外原文已經和我下面的東西不沾邊了,感興趣他怎麼用純css來實現動態進度條的可繼續在原文觀看,我這裏基本上是js部分了,目的是讓進度條響應當前進度

  • 咱們添加一個div來放數字,位置就在.inset裏面,以百分號表示
  • 咱們封裝一個js方法,見源碼的runprocess方法,其中process其實就是把前面onclick的內容給提取了出來,只須要傳入一個100之內的數字
  • 而run方法則包含了必定業務邏輯此處的邏輯是傳入一個開始進度和結束進度,我就每1%調用一次進度條(真實的業務邏輯通常爲:我監測下載進度,或歌曲播放進度,一旦有變化,就通知這個函數更改UI)
  • 講解得比較抽象,運行一下結果再看代碼:http://jsfiddle.net/walker/smMzz/6/
  • 補充一句,前面之因此要有1秒的動畫切換時間,純粹爲了好看(各類js生成的圖表,也是爲了展現這個生成過程,好看),而咱們用來作「被通知」的進度條的時候,就不必了,好比你如今在89%,要跳到90%,那就直接轉到90%便可,而不是須要這1S的轉場時間。由於事實上咱們已經有這個時間了:下載的時間,歌曲播放的時間。因此我把transition去掉了。

模擬播放器

上面的例子是模擬下載或者上傳進度條。什麼意思?不論是上傳,仍是下載,進度只是一個「狀態」,你不能手動更改這個狀態,而播放器則不一樣,你更改這進度條的百分比,應該能影響歌曲或影片從哪一個時間點開始播放,所以,咱們須要響應點擊事件,同時還要會計算點擊位置的角度:

      • 咱們選擇.circle做爲點擊事件的響應對象,由於不受.mask.fill元素是否可見的影響。並把其鼠標狀態改成手形
      • 一旦進度條出現,就會覆蓋在.circle上面,因此咱們又要把.mask.fill這兩個層設爲鼠標穿透(用pointer-events:none實現),以避免點擊不到.circle元素
      • 計算角度的函數見源碼,我也是網上搜的,有效。
      • 角度轉化成百分比,調用run函數,it works
      • 最後,乾脆替換一個播放器按鈕
      • 見示例: http://jsfiddle.net/walker/smMzz/7/
相關文章
相關標籤/搜索