// html
<div class="multiBarChart"></div>
// js-建立svg
const drawBarChart = async()=>{
const rwdSvgWidth = parseInt(d3.select('.multiBarChart').style('width')),
rwdSvgHeight = 500,
margin = 20,
marginBottom = 100
const svg = d3.select('.multiBarChart')
.append('svg')
.attr('width', rwdSvgWidth)
.attr('height', rwdSvgHeight);
const data = await d3.csv('./data/tainan_labor_force_population.csv')
// 設定要給 X 軸用的 scale 跟 axis
const xData = data.map((i) => i['年度']);
const xScale = d3.scaleBand()
.domain(xData)
.range([margin*2, rwdSvgWidth - margin])
.padding(0.2)
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, 600])
.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軸資料、比例尺
// 用來設定多條長條圖的位置
const subgroups = Object.keys(data[0]).slice(1)
const xSubgroup = d3.scaleBand()
.domain(subgroups)
.range([0, xScale.bandwidth()])
.padding([0.05])
// 設定不同 subgorup bar的顏色
const color = d3
.scaleOrdinal()
.domain(subgroups)
.range(['#d4be92','#c2cccd','#b2c2e3', '#ead0d1'])
// 開始建立長條圖
const bar = svg.append('g')
.selectAll('g')
.data(data)
.join('g')
.attr('transform', d => `translate(${xScale(d['年度'])}, 0)`)
.selectAll('rect')
.data(d =>
subgroups.map(key=> {return {key:key, value:d[key]}}))
.join('rect')
.attr('x', d => xSubgroup(d.key))
.attr("y", d => yScale(d.value))
.attr("width", xSubgroup.bandwidth())
.attr("height", d =>
(rwdSvgHeight-marginBottom) - yScale(d.value))
.attr("fill", d => color(d.key))
.style('cursor', 'pointer')
.on("mouseover", handleMouseOver)
.on("mouseleave", handleMouseLeave)
function handleMouseOver(d, i){
const pt = d3.pointer(event, svg.node())
// 加上文字標籤
svg.append('text')
.attr('class', 'infoText')
.attr('y', yScale(d.target.__data__['value']))
.attr("x", margin*2)
.style('fill', '#000')
.style('font-size', '18px')
.style('font-weight', 'bold')
.style("text-anchor", 'middle')
.text(d.target.__data__['value'] + '千人')
// 加上軸線
svg.append('line')
.attr('class', 'dashed-Y')
.attr('x1', margin*2)
.attr('y1', yScale(d.target.__data__['value']))
.attr('x2', pt[0])
.attr('y2', yScale(d.target.__data__['value']))
.style('stroke', 'black')
.style('stroke-dasharray', '3' )
}
function handleMouseLeave(){
// 移除文字、軸線標籤
svg.select('.infoText').remove()
svg.select('.dashed-Y').remove()
}
// 加上辨識標籤
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)
};
drawBarChart();