本篇大纲:d3.stack( ) 的用法、本次范例的画面与互动效果、绘制堆积长条图
今天的一天一图表,来到长条图三部曲的终章 — 堆积长条图!
堆积长条图的绘制相对困难一点,我们需要使用到 d3.scaleBand( )
跟 d3.stack( )
这两个API。
这个方法主要是用来绘制堆积图表,我们会使用 d3.stack( ) 的方法来换算每个数据的占比,接着再把这些数据呈现在长条图上。如果不清楚它要怎麽使用、会生成哪些方法、提供什麽样的数据的话,我在 Day9 的 Layouts 章节有详细的解说。
这次我们要做的范例画面与互动效果有:
我们这次沿用昨天的资料(台南市劳动人口)来制作~首先,一样先建立 svg 并把资料取回来
// css
.chart{
width: 100%;
min-width: 300px;
margin: auto;
}
// html
<div class="chart"></div>
// js
let data = []
async function getData() {
// 取资料
dataGet = await d3.csv('./data/tainan_labor_force_population.csv')
data = dataGet
console.log(data)
drawBarChart()
};
getData()
// RWD
function drawBarChart(){
// 删除原本的svg.charts,重新渲染改变宽度的svg
d3.select('.chart svg').remove();
// RWD 的svg 宽高
const rwdSvgWidth = parseInt(d3.select('.chart').style('width')),
rwdSvgHeight = rwdSvgWidth,
margin = 20,
marginBottom = 100
const svg = d3.select('.chart')
.append('svg')
.attr('width', rwdSvgWidth)
.attr('height', rwdSvgHeight);
// 接下来的程序码放这边...
// 接下来的程序码放这边...
// 接下来的程序码放这边...
}
d3.select(window).on('resize', drawBarChart);
接下来,我们把 X轴跟 Y轴要用到的资料分别整理出来,并依此来建立 X轴跟 Y轴。
// map 资料集
const xData = data.map((i) => i['年度']);
// 设定要给 X 轴用的 scale 跟 axis
const xScale = d3.scaleBand()
.domain(xData)
.range([margin*2, rwdSvgWidth - margin]) // 宽度
.padding(0.6)
const xAxis = d3.axisBottom(xScale)
// 呼叫绘制x轴、调整x轴位置
const xAxisGroup = svg.append("g")
.call(xAxis)
.attr("transform", `translate(0,${rwdSvgHeight - marginBottom})`)
// 设定要给 Y 轴用的 scale 跟 axis
const yScale = d3.scaleLinear()
.domain([0, 1200])
.range([rwdSvgHeight - marginBottom, margin])
.nice() // 补上终点值
const yAxis = d3.axisLeft(yScale)
.ticks(5)
.tickSize(3)
// 呼叫绘制y轴、调整y轴位置
const yAxisGroup = svg.append("g")
.call(yAxis)
.attr("transform", `translate(${margin*2},0)`)
重点来了!除了本来的X轴分组之外,我们还需要整理一个 subgroups
资料集。这个资料集是把我们想要分组的资料拉出来,之後用来建立堆积图的。以这边的资料来说,我们希望能分成四组:
因此,我们用 Object.keys(data[0]).slice(1)
把这四个组别拉出来。分组的资料拉出来後,接着就是用 d3.stack( )
的方法,把这些资料变成堆积图可以使用的数据。
// 拉出要分组的资料
const subgroups = Object.keys(data[0]).slice(1)
// 用 d3.stack() 把资料堆叠起来
const stackedData = d3.stack()
.keys(subgroups)(data)
接着,我们用 scaleOrdinal 的方法来设定 subgorup 资料的颜色
// 设定不同 subgorup bar的颜色
const color = d3.scaleOrdinal()
.domain(subgroups)
.range(['#e41a1c','#377eb8','#4daf4a', '#ffda6b'])
再来就是建立堆积图表啦!我们把用 d3.stack 建立好的资料带进去,并使用它提供的资料去建立 < rect >
const bar = svg.append('g')
.selectAll('g')
.data(stackedData)
.join('g')
.attr('fill', d => color(d.key))
.selectAll('rect')
.data(d=>d)
.join('rect')
.attr("x", d => xScale(d.data['年度']))
.attr("y", d => yScale(d[1]))
.attr("height", d => yScale(d[0]) - yScale(d[1]))
.attr("width",xScale.bandwidth())
这样基本的长条堆叠图表就完成了
别走,还没结束!我们还有动画跟下方的标签说明要做呢,先来加动画吧
bar.on("mouseover", handleMouseOver)
.on("mouseleave", handleMouseLeave)
// 设定文字标签
const textTag = svg.append('text')
.attr('class', 'infoText')
.style('fill', '#000')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style("text-anchor", 'middle')
.style('opacity', '0')
function handleMouseOver(d, i){
const pt = d3.pointer(event, svg.node())
d3.select(this)
.style('opacity', '0.5')
// 加上文字标签
textTag
.style('opacity', '1')
.attr("x", pt[0])
.attr('y', pt[1]-20)
.text((d.target.__data__[1] - d.target.__data__[0]) + '千人')
}
function handleMouseLeave(){
d3.select(this)
.style('opacity', '1')
textTag.style('opacity', '0')
}
最後再加上最下方的标签就可以了~
// 加上辨识标签
const tagsWrap = svg.append('g')
.selectAll('g')
.attr('class', 'tags')
.data(subgroups)
.enter()
.append('g')
if(window.innerWidth < 780){
tagsWrap.attr('transform', "translate(-70,0)")
}
tagsWrap.append('rect')
.attr('x', (d,i)=> (i+1)*marginBottom*1.3)
.attr('y', rwdSvgHeight-marginBottom/2)
.attr('width', 20)
.attr('height', 20)
.attr('fill', d => color(d))
tagsWrap.append('text')
.attr('x', (d,i)=> (i+1)*marginBottom*1.3)
.attr('y', rwdSvgHeight-10)
.style('fill', '#000')
.style('font-size', '12px')
.style('font-weight', 'bold')
.style("text-anchor", 'middle')
.text(d=>d)
完成!一天又平安的结束惹 (下台一鞠躬)~
最後附上本章的程序码:想看完整程序码的请上 Github,想直接操作图表的则去 Github Page 吧!请自行取用~
<<: {DAY 24} Pandas 学习笔记part.10
昨天提到,当Azure Hyper-V需要被其它Azure VM内网存取的时候, 该如何调整呢?内网...
强化学习什麽是? 简称RL,在没有以往资料的前提下,将模型放到使用环境中,透过一些操作观察环境状态,...
庞大的需求催生就业机会 在这人手一机,每个人都吃到饱的时代,不管什麽产业的公司行号组织单位,都需要有...
Google 虽然已经是在网路圈是稳定度相当高的一间公司与服务,但事实上挂的机会还是很高,主要也是 ...
前言 在HTML中每一个元素都会被CSS当作一个矩形盒子,CSS可以更改其宽度、高度、跟其他元素的距...