一個基於D3.js的橫向柱狀圖的動態數據可視化工具
前言
首先感謝原作者Jannchie https://github.com/Jannchie/
以及原項目Historical-ranking-data-visualization-based-on-d3.js
寫這個東西是因為學校某項目的一個練手題目選定了這個
那我為啥要寫前端呢?因為前端那位,我好像聯系不到:)
沒辦法時間挺緊的,這小項目就我一人寫了
幾乎所有的代碼都是來自原項目的,考慮到整個項目可能不斷需要魔改動態可視化,於是就另開了一個新repo ColumnsAnimation
期待這個repo以后的變化吧
順便寫了這個隨筆,本意是給自己前端看的,但今天有位同學問優化問題,所以就寫的詳細點了
模塊組織
簡單的html+js+css,其中html和css沒什么要說的,簡單提一下
- HTML部分
主要引用js和css文件
有一個地方需要注意一下,就是窗口寬度問題
<meta name="viewport" content="width=device-width">
-
CSS部分
設置顏色類:這個點子非常好,可以方便的修改顏色數據
設置各種元素寬度,文字描邊和坐標樣式:這里有個問題,如果SVG變寬高,這里的各種元素寬度就會略有問題 -
JavaScript部分
動態可視化核心代碼,主要就是標簽的組織和更新
首先講清楚SVG內部標簽構造,和動態更新的原理
運行代碼可以簡化分析SVG內部標簽的設置
SVG內部標簽
g group標簽包含其他所有svg標簽
g x軸(由d3生成)
text x軸標簽(d3生成)
path x軸(d3生成)
g*n n個時間豎線(英文是Tick不知道怎么翻譯)
line 豎線
text x軸數字
g 這個組不知道干嘛的,可能是y軸,以后考慮去掉?
text 這個是timer,改源代碼時忘記留下來了,最后自己加上的
g*n n個bar,這是動態的主題
g 這是標記顏色的,好像沒用?
rect 這是柱子,只有顏色類
text y軸上的柱后文字,label類顏色類
text 柱前數字,value類顏色類
text 柱上文字,barInfo類
動態更新原理
就是不斷的綁定新數據:
在Enter里初始化新數據
在upate里更新新元素
在exit下刪除舊元素
關鍵在於d3提供了按索引綁定數據的方法,這樣的操作才得以簡單進行,詳細說明在下面
細節問題
-
利用setInterval()來控制整個時間變化
首先輸入數據data,按時間push到date數組里
再定義一個iter,同於控制總時間 -
獲得顏色類
作者使用了一個簡單的hash函數,做了一個name到hash值的映射,然取模color.length()取得顏色類
-
獲取當前時間下的數據
這里原作者是枚舉data數組,返回對應時間下的數據(修改的代碼中順便維護了Timer)
有一個同學也是寫了這個項目,但總是有卡頓(win10),那么我們順便講一下優化
其實這里可以二分達到O(logn),但首先需要預處理數據排序O(nlogn),其次我們設置了MaxNumber用來控制顯示柱子的數量,就是說這里還能優化
最好的方法是在后端處理好數據(排序+切片)后傳給前端,這種一勞永逸的方式何樂而不為 -
刷新圖表
這里是核心部分,我們拆開來看
注意作者將interval間隔時間分成了三份( interval/=3 )
-
首先維護yScale, xScale, xAxisG, yAxisG和Timer
原作者在初始化刻度尺時並沒有給出domain, range這是很讓人舒服的地方,非常優雅
設置坐標系的更新動畫,注意還要添加.call(xAxis),順便刪除原有tick
維護Timer,這里有兩個要點。其一、在第一次顯示Timer時停頓。其二、數字的過度動畫d3中沒有直接的實現,只能是用tween調d3.interpolate自己寫 -
綁定bar的數據
注意.data(data, [key])中的key是每個元素的鍵值,對應原有元素的鍵值進行更新。如果沒有設置的話就從頭到尾覆蓋 -
Enter元素的過渡動畫
在綁定了數據后,按道理所有元素的數據都是當前時間下最新數據。
Enter部分指新加入數據,這里應該是帶有新鍵值的數據。
這里的更新就像是初始化。
注意設置以下屬性:
transform, x, y, width, height, fill-opacity, class(帶有顏色類), text, stroke-width -
Update元素的過渡動畫
Update元素就是數據被更新的元素,這里的過渡動畫的屬性就很少,因為一些屬性不需要變化(比如顏色類) -
Exit元素的過渡動畫
Exit元素就是上一次時間的數據,在當前時間的數據中沒有出現的元素。
這里的過渡動畫就更簡單了,過渡里直接remove(),設置fill-opacity為0即可 -
更新y坐標
最后我們更新每個bar元素的y坐標即可,這里使用了一開始設置的yScale