一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情。
开始绘制
引入D3模块
<!-- 选择器模块 -->
<script src="https://cdn.jsdelivr.net/npm/d3-selection@3"></script>
<!-- 比例尺模块 和 依赖 -->
<script src="https://cdn.jsdelivr.net/npm/d3-array@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-color@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-format@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-interpolate@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-time@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-time-format@4"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-scale@4"></script>
<!-- 坐标轴 -->
<script src="https://cdn.jsdelivr.net/npm/d3-axis@3"></script>
<!-- 形状 -->
<script src="https://cdn.jsdelivr.net/npm/d3-path@3"></script>
<script src="https://cdn.jsdelivr.net/npm/d3-shape@3"></script>
数据
- 数据格式随意。后面使用时对格式不满意,可开始定义好格式或后面转换格式。
/*
* 绘制柱状图
**/
var bColor = ['#4385F4', '#34A853', '#FBBC05', '#E94335', '#01ACC2', '#AAACC2']
var dataArr = [
{
label: '1月',
value: 10.5,
value2: 20.5
},
{
label: '2月',
value: 70.5,
value2: 22.5
},
{
label: '3月',
value: 60.5,
value2: 30.5
},
{
label: '4月',
value: 10.5,
value2: 20.5
},
{
label: '5月',
value: 20.5,
value2: 40.5
},
{
label: '6月',
value: 30.5,
value2: 30.5
}
]
添加画布
var svg = d3
.select('.d3Chart')
.append('svg')
.attr('width', 500)
.attr('height', 500)
.style('background-color', '#1a3055')
创建比例尺
// 分段比例尺
var xScale = d3
.scaleBand()
.range([0, 400])
.domain(dataArr.map((s) => s.label))
.padding(0.4)
// 线性比例尺
var yScale = d3.scaleLinear().range([400, 0]).domain([0, 100])
绘制坐标轴
- 根据定义好的比例尺,使用
axis模块
自动绘制坐标轴。 - 绘制好坐标轴后,在坐标轴组中绘制文本标签。
var chart = svg.append('g').attr('transform', 'translate(50, 40)')
// X轴
chart.append('g').attr('class', 'xAxis').attr('transform', 'translate(15, 400)').call(d3.axisBottom(xScale))
// y轴
var makeYlines = () =>
d3
.axisLeft()
.scale(yScale)
.tickSize(-400)
.tickFormat((d) => {
return d + '%'
})
chart.append('g').attr('class', 'yAxis').attr('transform', 'translate(15, 0)').call(makeYlines())
// 标签
d3.select('.xAxis')
.append('text')
.attr('x', 400 / 2 - 12)
.attr('y', 0)
.attr('dy', 45)
.style('font-size', '24px')
.text('日期')
// 标签
d3.select('.yAxis')
.append('g')
.attr('transform', 'translate(-40, 0)')
.append('text')
.attr('transform', 'rotate(-90)')
.attr('class', 'axisText axisTextY')
.style('font-size', '24px')
.text('比例(%)')
d3.select('.axisTextY').attr('x', function () {
return -200 + this.getBoundingClientRect().height / 2
})
d3.selectAll('.d3Chart text').style('fill', '#fff')
d3.selectAll('.d3Chart line').style('stroke', '#fff')
d3.selectAll('.d3Chart path').style('stroke', '#fff')
绘制柱状
- 我们绘制的是堆叠柱状图,需要把同一类型的两个柱状组合在一起。
- 在D3的
d3-shape
模块中,为我们准备好了工具d3.stack()
组合堆叠。
// stack() 组合堆叠 配置
const stack = d3.stack().keys(['value', 'value2']).order(d3.stackOrderAscending)
.offset(d3.stackOffsetNone)
console.log(' stack', stack(dataArr))
- 数据堆叠后,生成三维数组。三维数据中,
0、1
代表柱状的开始和结束高度。
// 柱状
const groups = chart.selectAll().data(stack(dataArr))
// 堆叠数据创建 堆
const heaps = groups
.enter()
.append('g')
.attr('class', (d) => 'g ' + d.key)
.attr('fill', (d, i) => bColor[i])
- 在
chart组
上,绑定堆叠数据。 - 生成新的
g
元素。这里只会生成两个,因为堆叠数据我们只定义了两类。 - 使用堆叠类的key,为唯一标识。
// 堆叠数据 拆解 柱数据 绑定到对应柱上
const bars = heaps.selectAll().data((d) => {
return d.map((item) => {
item.index = d.index
item.name = d.key
return item
})
})
// 在堆叠 中绘制柱
bars
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', (d) => xScale(d.data.label) + xScale.bandwidth() / 2 - 4)
.attr('y', (d) => yScale(d[1]))
.attr('width', xScale.bandwidth())
.attr('height', (d) => yScale(d[0]) - yScale(d[1]))
- 将二维数据中的类型标识和下标放入三维数据中,绑定数据到新对应的
g
元素上。 - 最后就是正常的数组数据,开始设置柱状的样式和位置,就完成了柱状图。
d3.stack()
启用堆叠布局生成器。.keys()
指定使用对象中的那些属性进行叠堆。.order()
指定叠堆内数据的排序方式。d3.stackOrderAscending()
排序函数,从小到大排序。
添加交互
var tooltips = d3
.select('body')
.append('div')
.style('width', '100px')
.style('height', '60px')
.style('background-color', '#fff')
.style('dispaly', 'flex')
.style('justify-content', 'center')
.style('padding', '10px')
.style('border-radius', '5px')
.style('opacity', 0)
d3.selectAll('.bar')
.on('mouseenter', (e, g) => {
tooltips
.html(`月份:${g[`data`].label}<br /> 类型:${g['name']}<br /> 数据:${g[`data`][g['name']]}%`)
.style('position', 'absolute')
.style('left', `${e.clientX}px`)
.style('top', `${e.clientY}px`)
.style('opacity', 1)
})
.on('mouseleave', (e, g) => {
tooltips.style('opacity', 0).style('left', `0px`).style('top', `0px`)
})
- 创建提示框。通过标识符
.bar
获取所有的柱状。根据鼠标监听事件来设置对应的数据和位置。 - 代码地址