使用D3.js与React构建Voronoi图时SVG宽度超出设定的问题
问题原因与修复方案
你的Voronoi图超出SVG宽度,核心问题有两个:一是Voronoi生成器没有设置边界限制,默认会生成超出SVG范围的单元格;二是数据坐标没有统一映射到SVG的坐标系,原始数据的数值可能超出你设定的width/height范围,且仅对x轴做了缩放,Voronoi生成时直接用了原始数据。
具体修复步骤:
给Voronoi生成器设置边界
创建Voronoi实例时,通过voronoi([[0, 0], [width, height]])指定边界范围,这样生成的单元格会被裁剪在SVG的宽高内。添加y轴比例尺并统一应用
你只处理了x轴的缩放,y轴直接使用原始数据值,这会导致点和单元格超出SVG高度。需要创建y轴比例尺,并同时应用到数据点和Voronoi的输入数据上。确保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
相关产品推荐
相关产品推荐

