d3.js

d3.js基于svg,是非常强大的数据分析和图形绘制的前端框架。当前目录下d3目录里有demo。

1 引入

最新版本是v5,之前的就不要看了。通过以下方式引入页面

<script src="https://d3js.org/d3.v5.min.js"></script>

2 数据操作(数组和集合容器)

数组的基础统计计算,以及生成数组

var arr = [1,2,2,3,4,5]
// 这里的func是调用Array.map函数的参数
d3.min(arr,[func]) // 最小值,类似的有如下几个函数
// d3.min - compute the minimum value in an array.
// d3.max - compute the maximum value in an array.
// d3.extent - compute the minimum and maximum value in an array.
// d3.sum - compute the sum of an array of numbers.
// d3.mean - compute the arithmetic mean of an array of numbers.
// d3.median - compute the median of an array of numbers (the 0.5-quantile).
// d3.quantile - compute a quantile for a sorted array of numbers.
// d3.variance - compute the variance of an array of numbers.
// d3.deviation - compute the standard deviation of an array of numbers.

d3.range(0,100,2) // 从0到100,间距为2,含0不含100,生成50个数

对象操作,Map容器,新版js已经有了这些功能

d3.keys(a)// 等价于Object.keys(a) 取对象的所有key 放到数组
d3.values(a) // 等价于Object.values(a) 取对象所有value 放到数组
d3.entries(a) // 等价于Object.entries(a)

d3.map() // 创建d3 map对象 类似于 new Map()
m.get/set/remove/clear/keys/values/each/size // js中基本都有同名方法了

d3.set() // 创建d3 set对象 类似于 new Set()
s.add/remove/clear/values/each/size/has // js中基本都有同名方法了

3 dom操作

select和selectAll可以选取dom元素,但是得到的是个d3的对象,比如我们叫他d3-dom。这个对象还可以继续调用select方法,在子元素中继续找。

d3.select('#svg')  // 选一个
d3.selectAll('svg') // 选所有
d3.select('#svg').select('rect')

给选出的元素修改属性或者style,style其实也可以用attr设置的,但是style内容太多了,所以专门搞了个style函数。

d3.select('#id1').attr('name','haha').style('color','red')

给选出的元素内部追加dom,并修改追加的dom的属性。因为返回值一直是d3-dom对象,所以可以一直追加。

d3.select('#id1')
    .append('svg')
    .attr('viewBox','0 0 400 400')
    .append('rect')
    .attr('width',10)
    .attr('height',10)

d3-dom除了可以追加append,还可以删除remove。例如删除所有的svg下的rect。

d3.select('svg')
    .selectAll('rect')
    .remove()

4 柱状图与折线图

柱状图:思路是先定义整个svg大小,然后需要定义上下左右的间隔用来放坐标轴和文字显示,以及title等。坐标轴通过d3提供的方法来生成,而内部图块通过rect实现。

// 定义大小和间隔
var width = 500
var height = 500
var margin = 40

// 画出svg
var svg = d3.select('div').append('svg')
    .attr('width','400px')
    .attr('viewBox',`0 0 ${width} ${height}`)
    .style('background',"yellow")
// 画个g标签 距离左上角间隔的距离,后面在g中画图
var g = svg.append('g')
    .attr('transform',`translate(${margin},${margin})`)

// 数据
var data =[{month:'Jan',value:23},{month:'Feb',value:11},{month:'Mar',value:73},{month:'Apr',value:64}];

// 定义x轴和y轴,先定义比例尺
// y轴比例尺函数,将domain范围等比映射到range范围。这里将domain是从高到低,因为我们要定义的坐标轴是y轴,从下往上增的,和浏览器坐标反着。
var yScale = d3.scaleLinear()
        .domain([d3.max(data,it=>it.value), 0])
        .range([0, height-2*margin]);
// 将y轴画好
g.append("g").call(d3.axisLeft(yScale))


// x轴因为是固定的文字,所以使用scaleBand这个比例尺,padding指定间距
var xScale = d3.scaleBand()
        .domain(data.map(it=>it.month))
        .range([0, width-2*margin]).padding(0.6);
// 将x轴画好,注意高度
g.append("g").call(d3.axisBottom(xScale)).attr('transform',`translate(0,${height-2*margin})`)
        
// 最后画矩形,下面是对每个数据画rect的方法。
// -- attr这行,第二个参数,可以用函数入参为data每个元素,和下标,这里让每个rect的有一定偏移。注意偏移点是矩形左上角的位置。
g.selectAll('rect')
    .data(data)
    .enter()
    .append('rect')
    .attr('transform',(it,i)=>`translate( ${ xScale(it.month)},${yScale(it.value)})`)
    .attr('width',xScale.bandwidth())
    .attr('height',it=>height-2*margin-yScale(it.value))

效果图:

i

折线图,刚才的思路基本就很清晰了,折线图就是把柱状图上面的点描出来并连接。

// 上面基础上先画点,注意x偏移量稍有改动,不然是矩形左上角
g.selectAll('circle')
    .data(data)
    .enter()
    .append('circle')
    .attr('transform',(it,i)=>`translate( ${ 0.5* xScale.bandwidth()+xScale(it.month)},${yScale(it.value)})`)
    .attr('r',10)
    .attr('fill','red')

i

然后需要连线,用到了d3中的line函数。来生成path的路径d,这里需要设定x和y,参数是个函数,分别是输入数据产生xy坐标。line函数生成一个生成器,用来接数据。如下:

var line = d3.line()
            .x(it=>xScale(it.month)+ 0.5* xScale.bandwidth())
            .y(it=>yScale(it.value));
g.append('path')
    .attr('d',line(data))
    .attr('stroke','black')
    .attr('fill','none')

image