兩次在項目中用到d3都是做tree這種樹狀的流程圖,所以就針對d3談一下我個人的心得體會,然后再做一個簡單例子展示一下圖形,最后談一談在項目中踩到的坑。
但是在講d3之前需要了解一下數據驅動的思想和svg。
1. 首先,說一下我理解的數據驅動的思想
在進行數據可視化的時候,最重要的一點就是將數據和對應的DOM元素關聯起來,然后將用戶需要的數據展示在DOM元素上,d3提供了方法,能夠方便的將數據和DOM進行綁定。
那么數據驅動又是怎么回事呢?
一開始,頁面上除了一個圖形的container元素用來包裹整個圖形,是沒有其他元素的。現在我們開始向后端請求數據,前端拿到數據之后,當然是一對雜亂無章的不知道什么東西,我們需要做的是將這堆數據在DOM元素上展示出來,但是我們并不知道需要多少DOM元素,這里d3向我們提供了一個方法 ,可以告訴d3我們需要什么元素,然后在container中選擇該元素,然后就開始向這個元素上添加一點數據,方法是selection.data()
,這里的selection暫時一個都沒有,但是不用著急, 我們可以將從后端取到的數據綁定在這個空的selection上。 這里selection為空,但是我們的數據有很多,這怎么辦呢?這里再使用selection.enter()
方法,數據實際使用了占位符,d3這里知道多出了多少數據沒有實際的DOM元素綁定(好可憐的數據們啊),最后使用append()
方法,將多出的數據添加上DOM元素,將他們綁定在一起。數據們終于找到落腳之處,我也就放心了。到目前為止,我們終于可以將綁定好的數據用在DOM上,進行可視化的渲染。可以用數據的大小來改變div的寬度,可以根據數據的不同改變<rect>
的顏色等等。當我們的數據改變的時候,圖形會相應的改變,而不用我們再去重新計算dom的樣式,這樣我們可以將更多的精力放在數據操作上,而不是一直操作DOM。
因為我們項目中使用了Vue.js,Vue的數據雙向綁定也是數據驅動的思想,當數據變動的時候,頁面上的數據自動改變,而不需要我們去手動操作DOM,如果使用過一些框架的開發者來說,理解這個概念更容易。
2. SVG
其實SVG本身很簡單,但是為什么這里要說呢,因為說了,d3操作DOM很簡單,所以,作圖的時候用SVG比使用canvas方便多咯?但并不是說不能用canvas,可以看看官方的實例,還是有一些是使用canvas做的。
這里想說一下SVG的<g>
元素,它可以將多個元素組織在一起,由g元素編組在一起的svg元素可以設置相同的顏色,可以進行坐標變換。像這樣:
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<g fill="dodgerblue">
<rect x="10" y="10" width="40" height="40" ry="10" />
<rect x="80" y="80" width="40" height="40" ry="10" />
<rect x="150" y="150" width="40" height="40" ry="10" />
</g>
</svg>
上面所有的rect元素都是由dodgerblue顏色填充
當然,要做圖還要了解坐標和viewbox的概念,坐標的概念默認開發者都知道,viewbox可能需要查閱資料,這里延伸開說就太多了。
3. 下面就實戰作一個圖
我們在做數據可視化的時候,最常用和比較簡單的就是柱狀圖,所以我們就從柱狀圖入手。做好之后的效果圖是這樣:
首先,需要一個div作為container包裹住整個圖形,div的id為chart
這里我們先模擬一個后端傳回來的數據:
var data = [4, 8, 15, 16, 23, 42];
下面這段代碼是控制div的顯示范圍,因為數字可能很大,也可能很小,為了用戶在視覺上看起來體驗更好,d3能夠將圖形展示在設置的范圍內
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
//現在進行數據綁定
d3.select(“.chart") //選擇container
.selectAll(“div") //選擇我們將要添加的DOM元素
.data(data) //將數據綁定在一個空的DOM元素上
.enter() //數據形成占位符
.append(“div”) //將多出來的data數據添加上DOM
.attr(‘class’, ‘item’) //將每個div添加一個class
//接下來我們就可以將數據進行可視化操作了
d3.selectAll(‘.item’) //選擇所有的item
.style("width", function(d) { //控制style的width,使div的width樣式和數據相同
return x(d) + "px"; //這樣就能根據數據的不同呈現不同的效果
})
.text(function(d) { return d; });// 這里講具體的數據顯示出來
以上代碼就能展示出一個最簡單的樹狀圖
4.項目中踩到的坑
我們用了dagreD3這個插件來做流程圖,遇到的一個問題就是,當兩個節點之間有多條線的時候,d3只顯示后面數據的那根線,看到的也就只有一根線了像這樣:
原本
a1
到a3
之間有兩條線,但是合并成了一條。我在github上也發現有人跟我提出了同樣的issue
后來想到了一個辦法,將所有線上的數據數據都做成節點,然后再將節點的樣式做成一根線的樣子,看起來就是一個流程線上的數據,就像這樣:
我們只需要后端修改數據結構,前端只用稍稍修改一下樣式,整個圖形就會變成我們想要的樣子。