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

如何使用D3 JS在SVG区域绘制DIV等HTML元素实现散点图

D3 实现SVG内HTML元素散点图(支持动态更新)

核心注意点:SVG本身是矢量图形命名空间,无法直接嵌套普通DIV/HTML元素,需要借助SVG原生的<foreignObject>标签作为HTML容器,它完全遵循SVG坐标系定位,内部可渲染任意标准HTML元素。


完整可运行代码

<!DOCTYPE html>
<html>
<head>
  <style>
    /* 散点DIV基础样式 */
    .scatter-dot {
      width: 60px;
      height: 30px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      font-size: 12px;
      box-shadow: 0 2px 4px rgba(0,0,0,0.2);
      transform: translate(-50%, -50%); /* 让DIV中心对准散点坐标,和circle定位逻辑对齐 */
    }
    svg {
      border: 1px solid #eee;
      margin: 20px;
    }
  </style>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
  <svg id="scatter-chart" width="800" height="500"></svg>

  <script>
    // 初始化画布参数
    const svg = d3.select('#scatter-chart')
    const width = 800, height = 500, margin = 40
    // 定义坐标比例尺,把数据值映射到画布像素位置
    const xScale = d3.scaleLinear().domain([0, 100]).range([margin, width - margin])
    const yScale = d3.scaleLinear().domain([0, 100]).range([height - margin, margin])

    // 渲染函数,接收数据完成绘制/更新
    function renderScatter(data) {
      // 绑定数据,创建foreignObject作为HTML容器
      const dots = svg.selectAll('.dot-container')
        .data(data, d => d.id) // 用id作为数据唯一标识,提升更新性能
        .join(
          // 新增元素逻辑
          enter => {
            const fo = enter.append('foreignObject')
              .attr('class', 'dot-container')
              .attr('width', 60)
              .attr('height', 30)
            // 在foreignObject内部添加DIV
            fo.append('xhtml:div')
              .attr('class', 'scatter-dot')
            return fo
          },
          // 已有元素更新逻辑
          update => update,
          // 多余元素删除逻辑
          exit => exit.remove()
        )
      
      // 更新所有点的位置和DIV内容
      dots.attr('x', d => xScale(d.x))
          .attr('y', d => yScale(d.y))
        .select('.scatter-dot')
          .style('background', d => d.color)
          .text(d => d.content)
    }

    // 生成模拟数据
    function generateData() {
      return d3.range(8).map(i => ({
        id: i,
        x: Math.random()*100,
        y: Math.random()*100,
        content: `点${i+1}`,
        color: d3.schemeCategory10[i]
      }))
    }

    // 首次渲染
    renderScatter(generateData())
    // 动态更新演示:每2秒刷新数据重新渲染
    setInterval(() => {
      const newData = generateData().map(d => ({
        ...d,
        content: `值:${Math.round(d.x)}`,
        x: Math.random()*100,
        y: Math.random()*100
      }))
      renderScatter(newData)
    }, 2000)
  </script>
</body>
</html>

关键说明

  • 定位逻辑:<foreignObject>的x/y属性和普通SVG图形(圆形、矩形)完全一致,直接传入比例尺映射后的坐标即可。CSS中给DIV加的偏移样式是为了让元素中心对准坐标点,避免出现左上角对齐坐标的偏移问题。
  • 内容扩展:内部的DIV支持所有HTML/CSS能力,可以替换成图片、按钮、表单、富文本等任意内容,不受SVG原生图形元素的能力限制。
  • 动态更新:使用D3标准的data().join()模式,只要把新数据传入渲染函数,就会自动完成元素新增、位置更新、内容更新、冗余元素删除,不需要手动清空画布重绘。

踩坑提示:不要直接把DIV等HTML元素append到<svg>标签下,SVG属于独立的XML命名空间,直接嵌套HTML元素不会被浏览器解析渲染,必须放在<foreignObject>标签内部才能正常显示。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 10:57:13