1. 程式人生 > >D3.js用動畫渲染資料集的顯示

D3.js用動畫渲染資料集的顯示

介紹

資料統計和資料分析離不開資料集。之前幾篇部落格(用D3.js進行醫療資料視覺化 (一)折線圖 (Line Chart) 等)基於的是國家衛生和計劃生育委員會統計資訊中心的資料,可以說是已經經過了加工,提煉總結出來的資料。而本文涉及的是原始資料集。如果按醫療資料來說,可以是電子病歷資料,個人健康資料等。但這兒重點介紹動畫效果的實現,不涉及任何與特定資料集有關的內容。

前一段時間有點閒暇時間,想玩一玩D3.js中的動畫效果。於是立馬想到一個經常碰到的場景:原始資料集的視覺化。目前有很多網站允許使用者上傳資料集,然後提供資料管理甚至分析的服務。在展現原始資料集的時候,大多用表格形式來呈現。一般來說,資料集的載入需要一點時間,所以這個過程如果能做的有趣一點,還是一件挺好玩的事兒。

可做的好玩兒的比如原本資料集(以2維為例)就是行X列規規矩矩排排站好,那進入的時候能不能模擬真實世界中站隊的規則:一隊隊先自己排好,然後集合到一塊固定的場地。再比如,集合完畢後,把特殊的位置標註出來,比如有缺失資料的地方。等等。

於是就做了一個簡單的程式模擬這一過程。效果如下圖。我用Chrome外掛Gif Cat抓的gif,初始狀態不知道為什麼沒抓下來。當個參考吧。

 

原理很簡單,需要原始碼的可以從這兒下載。

稍微展開說下目前的實現。

載入資料 Load Data

關鍵的原始碼見下面。

          var circles = column.selectAll("circle")
               .data(data[j].data)
               .enter()
               .append("circle")
               .transition()
               .duration(1000)
               .each("start", function(d) {
                     if(d.value == 1){
          d3.select(this)
              .attr("fill", "white")
              .attr("r", 2)
              .attr("cx", width);                       
                     }else if(d.value == 0){
                          d3.select(this)
             .attr("fill", "#222")
              .attr("cx", width);  
                     }
     })
     .delay(function() {
          return j * (3000/datalen);
     })
.ease(easefuns[2]);
         
          circles
               .attr("cx", xScale(data[j].name))
               .attr("cy", function(d, i) {
                     return yScale(d.caze);
               })
               .attr("class", function(d){
                     return"circle" + d.value;
               })
               .each("end", function(d) {
                     if(d.value == 1){
          d3.select(this)
              .transition()
              .duration(500)
              .attr("fill", "white")
              .attr("r", 1);                      
                     }else if(d.value == 0){
                          d3.select(this)
              .transition()
              .duration(500)
              .attr("fill", "#222")
              .attr("r", 1);
                     }
     });
 
     }


載入資料時的動畫有幾個關鍵點:

1.    每個點進入螢幕之前的狀態,由circles. each("start",function(d) {…})來決定。比如這兒有值的點就渲染成白色大圓點;而缺失值的點就是隱藏的(顏色與背景色相同)。

2.    點從螢幕邊緣飛到最終位置的節奏,由circles.delay()和circles.ease()共同決定。circles.delay()設定點進入螢幕的延遲時間。為了讓資料點一列一列依次進入,這兒的circles.delay()函式實現將j(列索引)作為了變數,越左邊的列進入的越早。有興趣的同學可以把circles.delay()的返回設成一個常數試試不一樣的進入效果。circles.ease()包含了好多可選的緩動函式,規定了資料點從一個位置到另一個位置是如何過渡的。目前使用的是cubic-in-out。模擬的是自然站隊場景中快速進入,精確佔位的理念。easefuns裡面包含了不同的函式,可以試試它們不同的效果。MikeBostock大神已然想到有人會對這些函式好奇,做了一個展示的頁面:https://bl.ocks.org/mbostock/248bac3b8e354a9103c4

3.    到達最終位置後的狀態,由circles. each("end",function(d) {…})來決定。這兒就把有效點渲染回白色小圓點了。

高亮缺失資料 Highlight Missing Data

原始碼見下面。

     nodes
          .transition()
          .duration(1000)
          .delay(500)
               .attr("fill", "red")
               .attr("r", 3)
          .transition()
          .duration(1000)
               .attr("fill", "#222")
               .attr("r", 1);


這兒主要就是通過nodes.transition()帶著動畫函式duration()和delay(),並結合各種樣式的設定。簡單來說,這兒的效果就是先將缺失點高亮成紅色大圓點,然後又再次隱藏。

渲染效率

最後再扯兩句渲染效率。D3.js對動畫的渲染有其侷限性,就是當節點增多時,效率會逐步降低。原始碼中包含若干個樣例資料集。比如data-20-10.csv表示20列,10行的資料集。當渲染data-20-10.csv、data-200-10.csv時,我的瀏覽器基本上還能勝任(MacbookPro, Chrome v51)。但對於data-200-100.csv以及更多資料的資料集,能明顯感覺到渲染的遲滯。

這個也許不是D3.js的錯,而是瀏覽器的問題。不知道大家有沒有這方面的資料,推薦一下。