基於 D3.js 繪製動態進度條

前言

在網站頁面加載以及表單提交時,常使用進度條表達加載過程來優化用戶體驗,常見的進度條有矩形進度條和圓形進度條,以下圖所示:javascript

咱們常用svg或canvas來實現動態圖形的繪製,但繪製過程相對較繁瑣。對於直觀漂亮的進度條,社區也有提供成熟的方案例如highcharts/ECharts等等,但基於配置的開發方式終究沒法實現100%的自定義繪製。本文將帶你使用D3.js從零一步一步實現動態進度條,並分享代碼邏輯原理。css

基礎要求

  • 瞭解svg如何繪製基礎圖形
  • 瞭解D3.js v4版本
  • 瞭解如何使用D3.js (v4)繪製svg的基礎圖形

繪製圓形進度條

對於一個圓形進度條,咱們先對其進行任務拆分:html

  • 繪製嵌套圓弧
  • 圓心處的實時數據展現
  • 展示動畫
  • 美化

1.繪製嵌套圓弧

對於圓形,svg提供現成的circle標籤供使用,可是其劣勢在於,對於圓形進度條使用circle能夠知足,但對圖形進一步擴展時好比繪製半圓,circle的處理就棘手了。D3.js提供arc相關API對圓形的繪製方法進行了封裝:java

var arc = d3.arc()
            .innerRadius(180)
            .outerRadius(240)
            //.startAngle(0)
            //.endAngle(Math.PI)

arc(); // "M0,-100A100,100,0,0,1,100,0L0,0Z"
複製代碼

上述代碼實現了對兩個嵌套圓的繪製邏輯,d3.arc()返回一個圓弧構造函數,並經過鏈式調用設置內圓與外圓的半徑大小,起始角度與結束角度。執行arc()構造函數便可得到用於綁定在<path>上的路徑數據。完整代碼以下:git

<!--html-->
<svg width="960" height="500"></svg>

<script> var arcGenerator = d3.arc().innerRadius(80).outerRadius(100).startAngle(0); var picture = d3.select('svg').append('g').attr('transform','translate(480,250)'); </script>
複製代碼

上述代碼實現了2個步驟:github

  • 1.生成將0度做爲起點的圓弧構造器arcGenerator
  • 2.設置transform圖形偏移量,令圖形在畫布中央

目前畫布上尚未任何元素,接下來咱們實際圖形的繪製。canvas

var backGround = picture.append("path")
        .datum({endAngle: 2 * Math.PI})
        .style("fill", "#FDF5E6")
        .attr("d", arcGenerator);
複製代碼

咱們對畫布picture添加<path>元素,依據endAngle()特性,使用datum()方法將{endAngle:Math.PI}也就是終點角度綁定到<path>元素上,並將圓弧構造器賦值給path路徑d。這樣就生成了指定背景顏色的圓弧,實際圖形以下:數組

第一個圓弧畫好了,那麼依據svg的層級關係z-index,所謂的進度條其實就是覆蓋在第一層圓弧之上的第二層圓弧。同理可得:app

var upperGround = picture.append('path')
        .datum({endAngle:Math.PI / 2})
        .style('fill','#FFC125')
        .attr('d',arcGenerator)
複製代碼

代碼運行後可得:dom

2.圓心處的實時數據展現

第一部分咱們已經實現了基於兩個path的嵌套圓。第二部分咱們來實現圓心處的實時數據展現。 在進度條進行加載時,咱們在圓心處添加數據來表達當前的加載進度,使用<text>標籤作展現便可:

var dataText = g.append('text')
        .text(12)
        .attr('text-anchor','middle')
        .attr('dominant-baseline','middle')
        .attr('font-size','38px')
複製代碼

暫時將數據設置爲12,並設置水平居中和垂直居中,效果以下圖:

3.展示動畫

經過1,2兩部份內容咱們已經知道了:

  • 繪製進度條的實質是改變上層弧的角度
  • 當弧度是時爲整圓,當弧度是π時爲半圓
  • 圓形中的數據即爲當前弧度相對的百分比

綜上咱們只要改變弧度值和數值同時設定改變過程所需時長便可實現所謂"動畫"。在ECharts提供的官方實例中,經過setInterval來實現每隔固定一段時間進行數據更新,其實在D3.js中一樣提供了相似方法來實現相似setInterval的功能:

d3.interval(function(){
    foreground.transition().duration(750).attrTween('d',function(d){
        var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
        return function(t){
            d.endAngle = compute(t);
            return arcGenerator(d);
        }
        
    })
},1000)
複製代碼

對這段代碼進行拆解:

  • d3.interval()方法提供了setInterval()的功能
  • selection.transition.duration()設置了當前DOM屬性過渡變化爲指定DOM屬性的過程所需時間,毫秒爲單位
  • transation.attrTween爲插值功能API,那麼何謂插值?

歸納來講,在給定的離散數據中補插函數,可使這條連續函數經過所有數據點。舉個例子,給定一個div,想實現其背景顏色的從左邊紅(red)到右邊綠(green)的線性漸變,每一區域的色值該如何計算呢?只需:

var compute = d3.interpolate(d3.rgb(255,0,0),d3.rgb(0,255,0));
複製代碼

compute即爲插值函數,參數範圍爲[0,1],只要你輸入該範圍內的數字,那麼compute函數將返回對應的顏色值。這樣的插值有什麼用呢?可看下圖:

假設上圖的div長度width爲100,那麼將[0,100]依比例關係轉化爲[0,10]的範圍數據並輸入compute函數中,便可獲得某一區域對應的顏色。固然,對於線性面積的處理咱們不該該使用離散數據做爲輸入和輸出,因此D3.js提供更方便的線性漸變APId3.linear等,這裏就不展開描述了。

言歸正傳,代碼d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);實現了以下插值範圍:

["當前角度值""隨機角度值"] //表達區間而非數組
複製代碼

然後返回一個參數爲t的函數,那麼該函數的做用是什麼呢?
t參數與d相似,是D3.js內部實現的插值,其範圍爲[0,1]。t參數根據設置的duration()時長自動計算在[0,1]內合適的插值數量,並返回插值結果,實現線性平穩的過渡動畫效果。

完成滾動條的動畫加載效果,咱們接下來寫圓心實時數據的變化邏輯,只要實現簡單的賦值便可,完整代碼以下:

d3.interval(function(){
        foreground.transition().duration(750).attrTween('d',function(d){
            var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
            return function(t){
                d.endAngle = compute(t);
                var data = d.endAngle / Math.PI / 2 * 100;
                //設置數值
                d3.select('text').text(data.toFixed(0) + '%');
                //將新參數傳入,生成新的圓弧構造器
                return arcGenerator(d);
            }
        })
    },2000)
複製代碼

最終效果以下:

4.美化

1,2,3部分咱們實現了最基本的進度條樣式和功能,但樣式看起來仍是很單調的,咱們接下來咱們對進度條進行線性漸變處理。咱們使用D3.js提供的線性插值API:

var colorLinear = d3.scaleLinear().domain([0,100]).range(["#EEE685","#EE3B3B"]);
複製代碼

colorLinear一樣是一個插值函數,咱們輸入[0,100]區間中的數值,就會返回對應["#EEE685","#EE3B3B"]區間內的顏色值。好比當進度條顯示進度爲"80%"時:

var color = colorLinear(80);
//color即爲"80%"對應的色值
複製代碼

實現了顏色取值後,咱們只需在進度條變化時,將原有顏色改變便可:

d3.interval(function(){
        foreground.transition().duration(750).attrTween('d',function(d){
            var compute = d3.interpolate(d.endAngle,Math.random() * Math.PI * 2);
            return function(t){
                d.endAngle = compute(t);
                var data = d.endAngle / Math.PI / 2 * 100;
                //設置數值
                d3.select('text').text(data.toFixed(0) + '%');
                //將新參數傳入,生成新的圓弧構造器
                return arcGenerator(d);
            }
        })
        .styleTween('fill',function(d){
            return function(t){
                var data = d.endAngle / Math.PI / 2 * 100;
                //返回數值對應的色值
                return colorLinear(data);
            }
        })
    },2000)
複製代碼

styleTweenattrTween相似,是實現改變樣式的插值函數。採用鏈式調用的形式同時對進度條數值和顏色的設置便可。最終實現的效果以下:

綜上咱們實現了在不一樣數值下顏色變化的圓形進度條,可經常使用於告警,提醒等業務場景。

繪製矩形進度條

矩形進度條相比圓形進度條簡單了不少,一樣基於插值原理,平滑改變矩形的長度便可。直接上代碼:

<head>
    <style> #slider { height: 20px; width: 20px; background: #2394F5; margin: 15px; } </style>
</head>

<body>
    <div id='slider'></div>
    <script> d3.interval(function(){ d3.select("#slider").transition() .duration(1000) .attrTween("width", function() { var i = d3.interpolate(20, 400); var ci = d3.interpolate('#2394F5', '#BDF436'); var that = this; return function(t) { that.style.width = i(t) + 'px'; that.style.background = ci(t); }; }); },1500) </script>
</body>
複製代碼

實現的效果以下:

總結

基於D3.js繪製進度條的關鍵點在於插值,從而正確地使圖形平滑過渡。若是必定要使用svg或純css實現矩形和圓形的進度條固然也是可行的,但對於路徑和動畫的處理,以及css的書寫要求都複雜了很多。咱們觀察到使用D3.js繪製上述兩種進度條的邏輯代碼幾乎徹底使用js實現,同時代碼量能夠控制在20行左右並可封裝複用,已經很是精煉了,在自定義圖表開發上很是有優點。

對於進度條的衍生版儀表盤圖表,相比基礎進度條增長了刻度描述和指針計算,但萬變不離其宗,只要掌握插值原理和使用,處理相似圖表都將駕輕就熟。

相關文章
相關標籤/搜索