d3入门与实战
本文代码均已上传至Github,文章首发于d3入门与实战,转载请注明出处
1. 什么是d3
d3是一种用于数据可视化的工具。
2. 使用d3绘制简单的图形
d3.js可以将html中特定类型的元素转为图形:
<svg height="300" width="300">
<rect width="10" height="23" x="0" y="0"></rect>
<rect width="20" height="23" x="0" y="25"></rect>
<rect width="30" height="23" x="0" y="50"></rect>
</svg>
引入d3.js,打开该html,得到图片:
d3.js可以直接操作DOM,以下代码在body中添加一个svg,得到效果与上图一致:
html:
<body>
</body>
js:
var dataArr = [10, 20, 30];
var dataHeight = 25;
d3.select('body')
.append('svg')
.attr('height', 300)
.attr('width', 300)
.selectAll('rect')
.data(dataArr)
.enter()
.append('rect')
.attr('width', function(d, i){
return d
})
.attr('height', dataHeight - 2)
.attr('x', 0)
.attr('y', function(d, i){
return i * dataHeight
})
js执行完成后的html:
<body>
<svg height="300" width="300">
<rect width="10" height="23" x="0" y="0"></rect>
<rect width="20" height="23" x="0" y="25"></rect>
<rect width="30" height="23" x="0" y="50"></rect>
</svg>
</body>
需要掌握的函数:
- select/selectAll(condition): 选择器,选择方法与jQuery一致, select选择满足选择条件 condition 的第一个元素,selectAll选择满足选择条件 condition 的所有元素。本例通过select选择html的body元素
- append(xxx): 在选中元素内部的末尾处添加元素xxx。本例选中body后在其内部的末尾添加svg元素
- attr: 设置样式
- data: 为所选元素集添加数据集。本例为svg中的所有rect添加对应元素集dataArr
- enter: 为所选元素添加数据集时,若元素集数量少于数据集,自动添加相应数量的元素。本例中selectAll(‘rect’)选中的rect数量实际为0,执行完enter后添加了3个rect。
- attr中的回调函数function(d, i):d为数据集中的数据data,i为数据集中该数据的位置index
3. 比例尺(Scale)
之前我们直接使用了数组dataset中的值设置了柱状图的尺寸,但大多情况下不适合将其直作为图形的参数,需要通过比例尺进行转换。
比例尺对数据的值(domain)进行映射,把一组输入的值映射至一定的输出范围(range)。
常用的比例尺有线性比例尺和序数比例尺两种。
3.1. 线性比例尺
var dataSet = [1, 2, 100, 30, 6];
var min = d3.min(dataSet);
var max = d3.max(dataSet);
var linear = d3.scale.linear()
.domain([min, max])
.range([0, 300]);
console.log(linear(1)); //0
console.log(linear(2)); //3.03...
console.log(linear(100)); //300
需要掌握的函数:
- d3.scale.linear():建立并返回一个线性比例尺
- domain([x, y]):设置比例尺的输入范围
- range([x, y]):设置比例尺的输出范围
通过上面的代码可以看到,通过比例尺完成了映射:([1, 100]) -> ([0, 300])
3.2. 序数比例尺
用于离散的映射,通过d3.scale.ordinal()可以建立一个离散的比例尺:
var xArr = [0, 1, 2, 3];
var yArr = ['a', 'b', 'c', 'd'];
var ordinal = d3.scale.ordinal()
.domain(xArr)
.range(yArr);
console.log(ordinal(0)); //a
console.log(ordinal(1)); //b
4. 坐标轴
坐标轴与比例尺一同使用,通过调用d3.svg.axis()建立:
var axis = d3.svg.axis()
.scale(linear)
.orient("bottom")
.ticks(5);
- scale(): 调用参数为比例尺
- orient: 指定坐标轴方向
- ticks: 指定坐标轴的刻度数量
定义好坐标轴后,通过如下方式将坐标轴axis引入画板(为svg添加一个分组元素g, 通过axis(svg)将坐标轴引入svg):
var dataset = [1, 2, 3];
var linear = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, 250]);
var axis = d3.svg.axis()
.scale(linear)
.orient("bottom")
.ticks(5);
var svg = d3.select('body')
.append('svg')
.attr('height', 300)
.attr('width', 300)
.append("g");
axis(svg);
效果图:
通常采用d3中的call(function)将以上代码写为如下形式:
var svg = d3.select('body')
.append('svg')
.attr('height', 300)
.attr('width', 300)
.append("g")
.call(axis);
d3中call()的参数是一个函数。调用之后,将当前的选择集作为参数传递给该函数。
默认坐标轴的样式不美观,可通过attr(xx, xx)修改坐标轴的样式。
5. 实战
5.1. 条线图
根据给出的数据源绘制出条线图。
要求:
- 根据数据绘制条线图
- 当鼠标hover在表上时,显示其详细信息
- 建立html:
<body> <div class="card"> <div class="title">Gross Domestic Product, USA</div> <svg class="chart"></svg> <p>Units: Billions of Dollars Seasonal Adjustment: Seasonally Adjusted Annual Rate Notes: A Guide to the National Income and Product Accounts of the United States (NIPA) - (http://www.bea.gov/national/pdf/nipaguid.pdf)</p> </div> </body>
- 设置基本参数
let chartMargin = { top: 15, right: 10, bottom: 30, left: 75 }; let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; let width = $(".chart").width() - chartMargin.left - chartMargin.right; let height = $(".chart").height() - chartMargin.bottom - chartMargin.top;
- 获取x、y轴数据数据
$.getJSON("https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/GDP-data.json", function(result) { let dataGDP = []; let dataTime = []; $.each(result.data, function(i, content) { dataTime.push(content[0]); dataGDP.push(content[1]); })
- 设置x、y轴
```javascript
let xScale = d3.time.scale() //d3.time.scale()是针对日期和时间值的一个比例尺方法,可以对日期刻度作特殊处理
.domain([new Date(dataTime[0]), new Date(dataTime[dataTime.length - 1])])
.range([0, width]);
let xAxis = d3.svg.axis()
.scale(xScale)
.orient(“bottom”)
.ticks(d3.time.years, 5); //以年为单位,每5年作为一个刻度
let yScale = d3.scale.linear()
.domain([0, d3.max(dataGDP)])
.range([height, 0]);
let yAxis = d3.svg.axis()
.scale(yScale)
.orient(“left”)
.ticks(10);
5. 将坐标轴添加至图中
```javascript
svg.append("g")
.attr("class", "axis")
.attr("transform","translate(" + chartMargin.left + "," + (chartMargin.top + height) + ")")
.call(xAxis)
svg.append("g")
.attr("class", "axis")
.attr("transform","translate(" + chartMargin.left + "," + chartMargin.top + ")")
.call(yAxis)
- 绘制reminder card(用于显示详细信息)
let reminderCard = d3.select(".card") .append("div") .attr("class", "tooltip") .style("opacity", 0);
- 绘制chart
let rects = svg.selectAll(".MyRect") .data(result.data) .enter() .append("rect").attr("class","MyRect") .attr("transform","translate(" + chartMargin.left + "," + chartMargin.top + ")") .attr("x", function(d, i){ return xScale(new Date(d[0])); }) .attr("y",function(d){ return yScale(d[1]); }) .attr("width", Math.ceil(width / result.data.length)) .attr("height", function(d){ return height - yScale(d[1]); }) .attr("fill","steelblue")
- 添加鼠标hover显示详细信息功能-动画效果与监听器
通过transition()方式为图画增加动画效果
通过.on(xx, function(){…})增加监听器
增加css:rects.on("mouseover", function(d) { let rect = d3.select(this); rect.attr("fill", "lightsteelblue"); let currentDateTime = new Date(d[0]); let year = currentDateTime.getFullYear(); let month = months[currentDateTime.getMonth()]; let dollars = d[1]; reminderCard.transition() .duration(200) .style("opacity", 0.9); reminderCard.html("<span class='amount'>" + dollars + " Billion </span><br><span class='year'>" + year + ' - ' + month + "</span>") .style("left", (d3.event.pageX + 5) + "px") .style("top", (d3.event.pageY - 50) + "px"); }); rects.on("mouseout", function() { let rect = d3.select(this); rect.attr("fill", "steelblue"); reminderCard.transition() .duration(500) .style("opacity", 0); });
```css
.card{
text-align: center;
}
.chart{
width: 80%;
height: 80%;
min-height: 500px;
}
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.tooltip {
position: absolute;
text-align: left;
width: auto;
height: auto;
padding: 3px 5px 3px 5px;
font: 1em sans-serif;
border: 0px;
border-radius: 5px;
pointer-events: none;
box-shadow: 3px 3px 6px rgba(0, 0, 0, 0.2);
background-color: lightsteelblue;
}
## 5.2. 散点图
根据给出的[数据源](https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json)绘制出散点图。
要求:
- 根据数据绘制散点图
- 当鼠标hover在散点上时,显示其详细信息
数据源:[数据源](https://raw.githubusercontent.com/FreeCodeCamp/ProjectReferenceData/master/cyclist-data.json)
源码:[Github源码](https://github.com/alivebao/My_FCC/tree/master/Data%20Viz/Scatterplot%20Graph)
预览:[在线预览](http://codepen.io/19920612/full/RKYBPX/)
![效果图](http://7xv88e.com1.z0.glb.clouddn.com/d3_exe_2_scatterplot_graph.PNG)
思路:
大致过程类似于上一题,区别在于将append('rect')变成了append('circle'):
```javascript
rects.append("circle") //添加的元素标签为circle
.attr("cx", function(d, i){ //设置横坐标位置
...
})
.attr("cy",function(d, i){ //设置纵坐标位置
...
})
.attr("r", 5) //设置圆的半径
...