You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React+D3.js柱状图的条形上方叠加数值文本?

在React + D3.js柱状图的条形上方添加数值标签

我用React结合D3.js构建了柱状图,现在想在每个条形上方显示对应数值,但尝试添加文本标签后,虽然DOM里能看到text元素,却在图表中看不到内容。当前组件代码如下:

import { useRef, useEffect } from "react";
import { select, axisBottom, axisLeft, scaleLinear, scaleBand } from "d3";
import styles from './BarChart.module.scss';

export type IBarChartProps = {
  data: {label: string, value: number}[];
  height: number;
  width: number;
}

const MAX_STAT = 200;

function BarChart({ data, height, width } : IBarChartProps) {
  const svgRef = useRef<SVGSVGElement | null>(null);

  useEffect(() => {
    const svg = select(svgRef.current);

    const xScale = scaleBand()
      .domain(data.map((value, index) => index.toString()))
      .range([0, width])
      .padding(0.1);

    const yScale = scaleLinear()
      .domain([0, MAX_STAT])
      .range([height, 0]);

    const xAxis = axisBottom(xScale)
      .ticks(data.length)
      .tickFormat((_, index) => data[index].label);

    svg
      .select("#x-axis")
      .style("transform", `translateY(${height}px)`)
      .style("font-size", '16px')
      .call(xAxis as any);

    const yAxis = axisLeft(yScale);
    svg
      .select("#y-axis")
      .style("font-size", '16px')
      .call(yAxis as any);

    svg.selectAll('g.tick');

    svg
      .selectAll(".bar")
      .data(data)
      .join("rect")
      .attr("class", "bar")
      .style("transform", "scale(1, -1)")
      .attr("x", (_, index) => xScale(index.toString()) as any)
      .attr("y", -height)
      .attr("width", xScale.bandwidth())
      .transition()
      .attr("fill","#0072BD")
      .attr("height", (value) => height - yScale(value.value));

    svg
      .selectAll(".bar")
      .append('text')
      .text('score')
  }, [data]);

  return (
    <div className={styles.BarChart__Container} style={{ height: `${height + 50}px`, width: `${width + 50}px` }}>
      <svg className={styles.BarChart} ref={svgRef} style={{ height: `${height}px`, width: `${width}px` }}>
        <g id="x-axis" />
        <g id="y-axis" />
      </svg>
    </div>
  );
}

export default BarChart;

问题原因

  1. SVG的<rect>是图形元素,无法包含<text>子元素,所以append到.bar上的文本不会被渲染
  2. 条形设置了transform: scale(1, -1)的翻转效果,文本继承该变换后位置完全错乱

解决方案

单独创建文本元素,和条形矩形分开处理,同时计算正确的坐标位置:

修改useEffect中的文本处理逻辑,替换为以下代码:

useEffect(() => {
  const svg = select(svgRef.current);

  const xScale = scaleBand()
    .domain(data.map((value, index) => index.toString()))
    .range([0, width])
    .padding(0.1);

  const yScale = scaleLinear()
    .domain([0, MAX_STAT])
    .range([height, 0]);

  const xAxis = axisBottom(xScale)
    .ticks(data.length)
    .tickFormat((_, index) => data[index].label);

  svg
    .select("#x-axis")
    .style("transform", `translateY(${height}px)`)
    .style("font-size", '16px')
    .call(xAxis as any);

  const yAxis = axisLeft(yScale);
  svg
    .select("#y-axis")
    .style("font-size", '16px')
    .call(yAxis as any);

  // 处理条形矩形
  svg
    .selectAll(".bar")
    .data(data)
    .join("rect")
    .attr("class", "bar")
    .style("transform", "scale(1, -1)")
    .attr("x", (_, index) => xScale(index.toString()) as any)
    .attr("y", -height)
    .attr("width", xScale.bandwidth())
    .transition()
    .attr("fill","#0072BD")
    .attr("height", (value) => height - yScale(value.value));

  // 单独处理数值文本标签
  svg
    .selectAll(".bar-label")
    .data(data)
    .join("text")
    .attr("class", "bar-label")
    // 文本x坐标:条形中间位置
    .attr("x", (_, index) => (xScale(index.toString()) as number) + xScale.bandwidth() / 2)
    // 文本y坐标:条形顶部上方10px处
    .attr("y", d => yScale(d.value) - 10)
    // 文本居中对齐
    .attr("text-anchor", "middle")
    .style("font-size", "14px")
    .style("fill", "#333")
    .text(d => d.value);
}, [data]);

关键说明

  • 用.bar-label选择器独立创建文本元素,不与条形矩形绑定
  • x坐标设置为条形的中间位置,确保文本居中
  • y坐标通过yScale(d.value)获取条形顶部的基准位置,再减10px让文本和条形保持间距
  • 设置text-anchor: middle保证文本水平居中,同时配置字体样式确保可见
  • 文本无需继承条形的翻转transform,直接用yScale计算正向位置即可

内容的提问来源于stack exchange,提问作者Jon_B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 11:35:21