拖更了很久,最近毕业的手续办的差很少了,应该能够回来了...css
系列传送门:html
知识点:git
数据可视化的第一步仍是数据读取,在d3中可使用d3.csv
很是方便的读取数据,它会返回一个Promise
对象。github
csv文件是以逗号,
分隔的数据内容,本地使用的csv数据以下,文件名为data.csv
:chrome
country,population
China,1415046
India,1354052
United States,326767
Indonesia,266795
Brazil,210868
Pakistan,200814
Nigeria,195875
Bangladesh,166368
Russia,143965
Mexico,130759
复制代码
首先将数据读入,代码以下:数组
const data = d3.csv('data.csv').then(data => {
console.log(data))
})
复制代码
能够看到,控制台输出了一个数组,数组中的每条数据均是一个对象,类型为{country:xx, population:xx}
安全
固然,人数天然应该是Number
类型的,同时,为了将人数转换为单位个
,将全部数据都扩大一千倍,即:服务器
const data = d3.csv('data.csv').then(data => {
data.forEach(element => {
element.population = +element.population * 1000
}); //处理完数据,就能够开始画图了
render(data)
})
复制代码
HTML文件与上一节相同,都是仅包含了一个<svg></svg>
标签,首先选择svg:app
const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
const render = data => {
} //根据已有数据,画图渲染的函数
复制代码
将数据绑定到DOM上,是D3最大的特点。d3.select
与d3.selectAll
返回选择集,但其自己是没有数据的,经过data()函数,能够将数据与之绑定。相关函数有两个:dom
selection.datum([value])
选择集上的每一个元素都绑定相同的元素valueselection.data(values[,key])
选择集上每个元素分别绑定数组values的每一项,key是一个键函数,用于指定绑定数组时的规则。datum
用比较少,这里主要用到的是data()
,将已处理好的数据绑定在dom上。
在进行数据绑定的时候,不必定数据和元素个数就是相同的,这个时候就须要一个动态的处理,这就须要用到updata
、enter
、和exit
了。
本项目主要用到了enter
,由于页面中只有svg
标签,咱们须要作的是根据数据内容,在svg
中画<rect>
来表示柱状图。 理解 Update、Enter、Exit
有了以上的概念,就能够开始画图了
const render = data => {
svg.selectAll('rect').data(data) //选择`rect`并绑定数据data,但这个时候没有元素,所以使用enter
.enter().append('rect')
.attr('width',width)
.attr('height','30px')
}
复制代码
这样更新视图,就能够看到已经有图像出来了。可是只能看到一个黑色的长方形。由于目前图形并不能反映任何数据,只是单纯的固定'width'的长方形。为此,须要用上数据,可是由于数据可能很大或者很小,为了让其可以正好显示的视图中,须要使用到比例尺。
D3中有不少比例尺,本例中主要使用到了线性比例尺(scaleLinear)和序数比例尺(scaleBand)
线性比例尺能够将domain
的内容线性映射到range
的一个范围内,这样,就能够保证不管初始数值多大或多小,都可以很好的适应画当前视图。 映射关系:
序数比例尺不是一个连续的比例尺,domain()
中使用一个数组,range()
是一个连续域。 映射关系:
所以,加上两个方向的比例尺,让柱状图的雏形开始慢慢出现吧:
const render = data => {
const xScale = d3.scaleLinear()
.domain([0,d3.max(data, d => d.population)])
.range([0,width]) //最大值将视图空间充满
const yScale = d3.scaleBand()
.domain(data.map(d => d.country))
.range([0,height])
svg.selectAll('rect').data(data) //选择`rect`并绑定数据data,但这个时候没有元素,所以使用enter
.enter().append('rect')
.attr('y',d => yScale(d.country))
.attr('width',d => xScale(d.population)) //宽度根据数据
.attr('height',yScale.bandwidth()) //高度由比例尺自动生成
}
复制代码
这样子,就有一个雏形了,效果以下:
首先,从新审视下代码,发现其中d => d.population
以及d => d.country
在比例尺设置以及使用时出现了屡次,若是须要修改,又是代码中多处的重复修改。为此,对其进行一个处理,以下:
const render = data => {
const xValue = d => d.population; //优化
const yValue = d => d.country; //优化
const xScale = d3.scaleLinear()
.domain([0,d3.max(data,xValue)]) //优化
.range([0,width])
const yScale = d3.scaleBand()
.domain(data.map(yValue)) //优化
.range([0,height])
svg.selectAll('rect').data(data)
.enter().append('rect')
.attr('y',d => yScale(yValue(d))) //优化
.attr('width',d => xScale(xValue(d))) //优化
.attr('height',yScale.bandwidth())
复制代码
下图所示是margin
的布局示意图,由于直接按照svg
的height
和width
撑满画布将致使没有多余的位置放置坐标轴等,因此这里使用一个margin
来对布局从新规划。
代码以下:
const margin = {left:50,top:10,right:20,bottom:30};
const innerHeight = height - margin.top - margin.bottom;
const innerWidth = width - margin.left - margin.right;
复制代码
其中innerHeight和innerWidth是柱状图的实际占有高度,所以,柱状图的代码能够修改成:
const xScale = d3.scaleLinear()
.domain([0, d3.max(data,xValue)])
.range([0, innerWidth]) //将 width 改成 innerWidth
const yScale = d3.scaleBand()
.domain(data.map(yValue))
.range([0, innerHeight]) //将height 改成 innerHeight
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`) //加入新元素g,总体移动maring.left和margin.top
g.selectAll('rect').data(data)
.enter().append('rect')
.attr('y',d => yScale(yValue(d)))
.attr('width', d => xScale(xValue(d)))
.attr('height',yScale.bandwidth())
复制代码
坐标轴的绘制,是d3经过<svg>
中的<path>
<text>
<line>
实现的,用到的函数如axisLeft
axisBottom
等,绘制通常分为一下几个步骤:
var axisX = d3.axisLeft(xScale)
根据比例尺建立坐标轴<g>
组 var gAxis = svg.append('g')
axisX(gAxis)
或者 在上一步直接svg.append('g').call(axisX)
所以,本例中坐标轴添加能够这样:
g.append('g').call(d3.axisLeft(yScale)); //左边显示country
g.append('g').call(d3.axisBottom(xScale))
.attr('transform',`translate(0,${innerHeight})`) //虽然是bottom,可是默认位置并不在下,须要移动至下方
复制代码
如今柱状图仍是很丑的状态,应该增长一点间隙,让它看起来更加美观,这就很是简单了,在yScale
上使用padding
属性。
const yScale = d3.scaleBand()
.domain(data.map(yValue))
.range([0, innerHeight])
.padding(0.15) //增长了这个属性
复制代码
为了让柱状图看起来更美观,增长一些css样式,样式以下:
body html{
margin:0;
overflow: hidden;
}
rect {
fill:steelblue;
}
text {
font-size: 1.1em;
}
复制代码
因为chrome的安全缘由限制,在本地使用d3.csv
读取本地文件时是会遇到问题的,并不支持file//:
读取内容。 所以,代码在github中是能够正常运转,可是本地可能没法正常运做, 能够开一个本地服务器,将代码放置上。
完整代码详见:d3系列教程源码