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

如何去除D3可视化中单元格之间的描边线?

解决D3 Voronoi单元格间描边线残留问题

你遇到的线条并非真正的描边,而是SVG渲染时路径间的细微间隙(由浮点精度、抗锯齿机制导致),即便设置了stroke: none也会显现。以下是几种彻底解决的方案:

方法1:扩展Voronoi边界,让多边形轻微重叠

生成Voronoi时,将边界范围稍微放大,让每个多边形超出视口一点,自然覆盖间隙:

// 替换原Voronoi生成代码
const voronoi = delaunay.voronoi([-1, -1, width + 1, height + 1])

生成的多边形会比视口大1单位,边缘多边形会覆盖间隙,同时通过viewBox裁剪超出部分,不影响最终显示。

方法2:添加与填充色一致的细微描边

给每个路径设置和填充色相同的极细描边,填补间隙:

g.selectAll("path")
  .data(polygons)
  .enter()
  .append("path")
  .attr("d", (d) => `M${d.join("L")}Z`)
  .attr('fill', (d, i) => palette[Math.floor(Math.random()*palette.length)])
  .attr("stroke", (d, i) => palette[Math.floor(Math.random()*palette.length)]) // 与填充色一致
  .attr("stroke-width", 0.3) // 极细宽度,刚好填补间隙

方法3:关闭SVG抗锯齿优化

给SVG添加shape-rendering: crispEdges属性,强制关闭抗锯齿消除间隙:

const svg = d3.create("svg")
  .attr("viewBox", [0, 0, width, height])
  .attr("shape-rendering", "crispEdges")

修改后的完整代码

const generateSvgNode = (width, height) => {
  const svg = d3.create("svg")
    .attr("viewBox", [0, 0, width, height])
    .attr("shape-rendering", "crispEdges")

  const g = svg.append("g");

  const points = d3.range(200).map(function() {
        return [
              Math.random() * width,
              Math.random() * height
        ];
  });

  const delaunay = d3.Delaunay.from(points)
  const voronoi = delaunay.voronoi([-1, -1, width + 1, height + 1])
  const polygons = voronoi.cellPolygons() 

  const palette = ['#8EB338', '#9DBF3E', '#9BBD3D', '#94B93B', '#9EC03F', '#97BB3B', '#94B93B', '#9EC03F', '#9EC03F', '#9BBD3D', '#A6C542', '#93B83A', '#92B73A', '#9ABE3D', '#96B93B', '#90B439', '#97BB3B', '#93B73B', '#9EC03F', '#97BA3C', '#90B439', '#A1C140', '#9BBD3D', '#90B239', '#95BA3B', '#9EC03F', '#A2C241', '#8FB339', '#93B83A', '#A2C241']


  g.selectAll("path")
    .data(polygons)
    .enter()
    .append("path")
    .attr("d", (d) => `M${d.join("L")}Z`)
    .attr('fill', (d, i) => palette[Math.floor(Math.random()*palette.length)])
    .attr("stroke", (d, i) => palette[Math.floor(Math.random()*palette.length)])
    .attr("stroke-width", 0.3)

  return svg.node()
}


const onResize = () => {
  document.querySelector('svg')?.remove();

  const width = window.innerWidth;
  const height = window.innerHeight;
  const svg = generateSvgNode(width, height)
  document.body.appendChild(svg);
}

const onLoad = () => {
  const width = window.innerWidth;
  const height = window.innerHeight;
  const svg = generateSvgNode(width, height)
  document.body.appendChild(svg);
}

window.onload = onLoad;
window.onresize = onResize;
html,
body {
  padding: 0;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.4/d3.min.js"></script>

推荐优先组合使用方法1+方法2,既能保证渲染精度,又不会影响图形平滑度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:27:35