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

使用D3.js与React构建Voronoi图时SVG宽度超出设定的问题

问题原因与修复方案

你的Voronoi图超出SVG宽度,核心问题有两个:一是Voronoi生成器没有设置边界限制,默认会生成超出SVG范围的单元格;二是数据坐标没有统一映射到SVG的坐标系,原始数据的数值可能超出你设定的width/height范围,且仅对x轴做了缩放,Voronoi生成时直接用了原始数据。

具体修复步骤:

  1. 给Voronoi生成器设置边界
    创建Voronoi实例时,通过voronoi([[0, 0], [width, height]])指定边界范围,这样生成的单元格会被裁剪在SVG的宽高内。

  2. 添加y轴比例尺并统一应用
    你只处理了x轴的缩放,y轴直接使用原始数据值,这会导致点和单元格超出SVG高度。需要创建y轴比例尺,并同时应用到数据点和Voronoi的输入数据上。

  3. 确保React渲染时的清理逻辑
    在useEffect中每次绘制前,先清除SVG内的旧元素,避免重复渲染导致的混乱。

修改后的完整代码

import React, { useEffect, useRef } from "react";
import * as d3 from "d3";
import "../App.css";

const VoronoiChart = ({ data }) => {
  const mySVGRef = useRef(null);
  const width = 600;
  const height = 500;

  useEffect(() => {
    if (!data || mySVGRef.current === null) return;

    // 清除旧元素,避免重复渲染
    const svg = d3.select(mySVGRef.current);
    svg.selectAll("*").remove();

    // 设置SVG尺寸
    svg.attr("width", width).attr("height", height);

    // 创建x、y比例尺,将原始数据映射到SVG坐标系
    const xScale = d3
      .scaleLinear()
      .domain([d3.min(data, d => d[0]), d3.max(data, d => d[0])])
      .range([0, width]);

    const yScale = d3
      .scaleLinear()
      .domain([d3.min(data, d => d[1]), d3.max(data, d => d[1])])
      .range([height, 0]); // y轴向上为正,所以range反转

    // 将原始数据转换为SVG坐标系下的点
    const scaledData = data.map(d => [xScale(d[0]), yScale(d[1])]);

    // 创建带边界的Voronoi生成器
    const voronoi = d3.Delaunay.from(scaledData).voronoi([[0, 0], [width, height]]);

    // 绘制Voronoi单元格
    svg
      .selectAll("path")
      .data(scaledData)
      .enter()
      .append("path")
      .attr("d", (d, i) => voronoi.renderCell(i))
      .attr("fill", "none")
      .attr("stroke", "black");

    // 绘制数据点
    svg
      .selectAll("circle")
      .data(scaledData)
      .enter()
      .append("circle")
      .attr("cx", d => d[0])
      .attr("cy", d => d[1])
      .attr("r", 3)
      .attr("fill", "red");

    // 绘制x轴
    const xAxis = d3.axisBottom(xScale);
    svg
      .append("g")
      .attr("transform", `translate(0, ${height})`)
      .call(xAxis);

    // 绘制y轴
    const yAxis = d3.axisLeft(yScale);
    svg
      .append("g")
      .attr("transform", "translate(0, 0)")
      .call(yAxis);
  }, [data]);

  return <svg ref={mySVGRef}></svg>;
};

export default VoronoiChart;

关键改动说明:

  • 边界限制:voronoi([[0, 0], [width, height]]) 强制Voronoi单元格在SVG范围内生成,不会超出宽高。
  • 统一缩放:将原始数据通过x/y比例尺转换为SVG坐标后,再传给Voronoi生成器,确保单元格和数据点都在SVG内。
  • 清理旧元素:svg.selectAll("*").remove() 避免组件更新时重复绘制元素。
  • y轴修正:y轴的range设置为[height, 0],符合SVG的y轴向下为正的特性,数据点会正确显示在SVG内。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 08:10:22