上一节中,我们对数据可视化进行了一些基本的介绍,包括其选择器选择,数据绑定方式以及属性的设置,和图表数据的更新等一些操作,今天,我们来学习制作一些柱状图,并对其中用到的一些api 进行讲解。
svg矩形位置设定
x: 矩形左上角x轴坐标
y: 矩形右上角y轴坐标
width: 矩形的宽度
height: 矩形的高度
rx: 对于圆角矩形,指椭圆在x轴方向的半径
ry: 对于圆角矩形,指椭圆在y轴方向的半径
例如:
<svg width="100mm" height="100mm"> <rect x="20" y="20" width="50" height="50" style="stroke: #f36; fill: rgba(123,123,23,.5);"/> </svg>
比例尺
- 线性比例尺
一个最简单的线性关系: y= 2x +1
在d3 的v4版本中,运行可得下列结果:
let yScale = d3.scaleLinear()
.domain([0, 500])
.range([0, 100]);
console.log( yScale(50) ) //10
console.log( yScale(250) ) // 50
console.log( yScale(450) ) //90
- 序数比例尺
定量比例尺的定义域和值域都是连续的,在实际的使用中,很多时候定义域和值域都是是非连续的,这时候我们就要用到序数比例尺了。
let xScale = d3.scaleBand()
.domain(["a", "b", "c", "d", "e"])
.rangeRound([0, 100])
console.log(xScale("a")) //0
console.log(xScale("d")) //60
console.log(xScale("e")) //80
好了,学习上面两个知识点之后,我们开始我们的柱状图绘制!
准备的数据
const data =[
{x:"上海", y:100},
{x:"北京", y:200},
{x:"天津", y:280},
{x:"西安", y:100},
{x:"武汉", y:240},
{x:"长沙", y:210},
{x:"深圳", y:100},
{x:"郑州", y:220},
{x:"驻马店", y:410},
{x:"信阳", y:100},
{x:"漯河", y:220},
{x:"商丘", y:210},
{x:"南阳", y:100},
{x:"纽约", y:220},
{x:"南昌", y:210}
]
样式
svg {
box-sizing: content-box
}
界面初始化
在进行渲染之前,我们会先在svg 各侧预留padding,这里做的主要目的是为了坐标轴数值部分超出的可以正常显示。
var initWidth = 340
var initHeight = 500
var padding = { left:40, top:20, right:10, bottom: 20}
var height = initWidth - padding.top - padding.bottom
var width = initHeight - padding.left - padding.right
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.style("padding-left", padding.left)
.style("padding-right", padding.right)
.style("padding-top", padding.top)
.style("padding-bottom", padding.bottom)
此时效果如下:
定义坐标轴
// y比例尺
let ydata = data.map(function(e, i){ return e.y}) //拿到y轴所有数据
let yScale = d3.scaleLinear() //定义线性比例尺(数值型,等比缩放)
.domain([0, d3.max(ydata)]) // 定义域
.range([height , 0]); //值域
let _yScale = d3.scaleLinear()
.domain([0, d3.max(ydata)])
.range([0, height]);
***上述yScale与 _yScale的差别就在于range恰恰相反,
因此同样的数值在运算时拿到的正好是相反的数据,
而这在待会的坐标轴数值计算和直方位置的计算中我们会用到***
//定义y轴
let yAxis = d3.axisLeft(yScale); //这句话的意思是添加左侧坐标轴,比例尺使用yScale
//添加y轴
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + 0 + "," + 0 + ")")
.call(yAxis);
//添加x轴坐标轴
//x轴比例尺
let xData = data.map(function(e, i){ return e.x})
let xScale = d3.scaleBand() //定义序数比例尺
.domain(xData)
.rangeRound([0, width])
.padding(0.1)
//定义x轴
let xAxis = d3.axisBottom(xScale)
//添加x轴
svg.append("g")
.attr("class","axis--x")
.attr("transform","translate(" + "0 ," + height + ")")
.call(xAxis);
此时效果如下
绘制直方
var rect = svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d, i){
return xScale(d.x)
})
.attr("y", function(d){
return height - _yScale(d.y)
})
.attr("width", xScale.bandwidth())
.attr("height", function(d){
return _yScale(d.y)
})
.attr("fill", "steelBlue")
效果
接下来,我们可以在柱上添加文字
var text = svg.append("g")
.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", function(d, i){
return xScale(d.x)
})
.attr("y", function(d){
return height - _yScale(d.y)
})
.attr("dy", "1em")
.attr("dx", xScale.bandwidth() / 2)
.attr("text-anchor", "middle")
.attr("font-size", '14px')
.attr("fill", "#fff")
.text(function(d){
return d.y
})
效果如下
我们可以给坐标轴添加一些样式让他看起来活泼一些
.axis--x path {
display: none;
}
鼠标在上面移动时我们可以给他添加一些样式
var rect = svg.selectAll("rect")
.........
.attr("fill", "steelBlue")
.on("mouseover",function(){
d3.select(this).attr("fill", "yellow")
})
.on("mouseout", function(){
d3.select(this).attr("fill","steelBlue")
})
预览
接下来我们给它添加一些交互效果
点击事件
这个很简单只需这样一句
var rect = svg.selectAll("rect")
.........
.on("click",function(d){
console.log(d.x+":"+d.y)
})
(框选和点击不能同时存在)
框选
var brush = svg.append("g")
.attr("class", "brush")
.call(d3.brushX()
.on("start", function(){ })
.on("brush", function(){ } )
.on("end", function(e) {
var selection = d3.event.selection;
console.log(selection) //可以拿到框选的x轴坐标范围,我们通过这个数值拿到框选到的目标就很简单啦
})
)
最终效果
一个小案例: 使用柱状图动态展示冒泡排序
本文所有图表源码
好了,今天我们先学到这里,明天更新饼图制作 !