堆疊長條圖 stacked bar chart

臺南市勞動人口-依年齡別區分
          
            // html 
            <div class="stackedBarChart"></div>

            // js 
            const drawBarChart = async () => {
              // 建立SVG
              const width = parseInt(d3.select('.stackedBarChart').style('width')),
                    height = 500,
                    margin = 20,
                    marginBottom = 100
      
              const svg = d3.select('.stackedBarChart')
                            .append('svg')
                            .attr('width', width)
                            .attr('height', height);
      
              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, width - margin])
                              .padding(0.6)
      
              const xAxis = d3.axisBottom(xScale)
      
              // 呼叫繪製x軸、調整x軸位置
              const xAxisGroup = svg
                  .append("g")
                  .call(xAxis)
                  .attr("transform", `translate(0,${height - marginBottom})`)
      
              // 設定要給 Y 軸用的 scale 跟 axis
              const yScale = d3.scaleLinear()
                              .domain([0, 1200])
                              .range([height - 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)`)
              
              // 設定分組,用 d3.stack() 把資料堆疊起來
              const subgroups =  Object.keys(data[0]).slice(1)
              const stackedData = d3.stack()
                                    .keys(subgroups)(data);
      
              // 設定不同 subgorup bar的顏色
              const color = d3.scaleOrdinal()
                .domain(subgroups)
                .range(['#97a9bf','#d6dbbb','#d4e6e8', '#dcd2d0'])  
              
              // 開始建立長條圖
              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())
                            .style('cursor', 'pointer')
                            .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');
              }
      
              // 加上tag標籤
              const tagsWrap =  svg.append('g')
                  .selectAll('g')
                  .attr('class', 'tags')
                  .data(subgroups)
                  .join('g')
      
              tagsWrap.append('rect')
                .attr('x', (d,i)=> {
                  const unit = (width-margin*2)/5;
                  return (i+1)*unit;
                })
                .attr('y', height-marginBottom/2)
                .attr('width', 20)
                .attr('height', 20)
                .attr('fill', d => color(d))
    
              tagsWrap.append('text')
                      .attr('x', (d,i)=> {
                        const unit = (width-margin)/5;
                        return (i+1)*unit;
                      })
                      .attr('y', height-10)
                      .style('fill', '#000')
                      .style('font-size', '12px')
                      .style('font-weight', 'bold')
                      .style("text-anchor", 'middle')
                      .text(d=>d)
            };
      
            drawBarChart();