两次在项目中用到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. 下面就实战作一个图
我们在做数据可视化的时候,最常用和比较简单的就是柱状图,所以我们就从柱状图入手。做好之后的效果图是这样:
![](https://img.haomeiwen.com/i4238751/b4d217ecbb2bbb06.png)
首先,需要一个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只显示后面数据的那根线,看到的也就只有一根线了像这样:
![](https://img.haomeiwen.com/i4238751/6a943f8df6e080e1.png)
原本
a1
到a3
之间有两条线,但是合并成了一条。我在github上也发现有人跟我提出了同样的issue
后来想到了一个办法,将所有线上的数据数据都做成节点,然后再将节点的样式做成一根线的样子,看起来就是一个流程线上的数据,就像这样:
![](https://img.haomeiwen.com/i4238751/c5aa3c33416b5d58.png)
我们只需要后端修改数据结构,前端只用稍稍修改一下样式,整个图形就会变成我们想要的样子。
网友评论