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

Vue项目中D3可缩放圆形打包图无法显示的问题求助

Vue中D3可缩放圆形打包图无法显示的排查方案

项目依赖版本

{
  "vue": "^2.6.14",
  "chart.js": "^2.9.4",
  "vue-chartjs": "^3.5.1",
  "vue-router": "^3.0.1",
  "vuex": "^3.6.2",
  "d3": "6.7.0"
}

核心问题与修复步骤

1. SVG元素未挂载到Vue容器

你通过d3.create("svg")生成了SVG元素,但没有将其添加到页面的.container容器中,这是图表完全不显示的直接原因。

2. 变量声明顺序错误

focus和view变量在调用zoomTo函数之后才声明,会触发ReferenceError,导致代码执行中断。

修正后的完整代码

<template>
  <div class="container"></div>
</template>

<script>
import * as d3 from "d3";

export default {
  name: "FrictionAnalysis",
  data() {
    return {};
  },
  mounted() {
    const width = 928;
    const height = width;

    let data = {
      "name": "flare",
      "children": [
        {
          "name": "analytics",
          "children": [
            {
              "name": "cluster",
              "children": [
                {"name": "AgglomerativeCluster", "value": 3938},
                {"name": "CommunityStructure", "value": 3812},
                {"name": "HierarchicalCluster", "value": 6714},
                {"name": "MergeEdge", "value": 743}
              ]
            },
            {
              "name": "graph",
              "children": [
                {"name": "BetweennessCentrality", "value": 3534},
                {"name": "LinkDistance", "value": 5731},
                {"name": "MaxFlowMinCut", "value": 7840},
                {"name": "ShortestPaths", "value": 5914},
                {"name": "SpanningTree", "value": 3416}
              ]
            },
            {
              "name": "optimization",
              "children": [
                {"name": "AspectRatioBanker", "value": 7074}
              ]
            }
          ]
        },
        {
          "name": "animate",
          "children": [
            {"name": "Easing", "value": 17010},
            {"name": "FunctionSequence", "value": 5842},
            {
              "name": "interpolate",
              "children": [
                {"name": "ArrayInterpolator", "value": 1983},
                {"name": "ColorInterpolator", "value": 2047},
                {"name": "DateInterpolator", "value": 1375},
                {"name": "Interpolator", "value": 8746},
                {"name": "MatrixInterpolator", "value": 2202},
                {"name": "NumberInterpolator", "value": 1382},
                {"name": "ObjectInterpolator", "value": 1629},
                {"name": "PointInterpolator", "value": 1675},
                {"name": "RectangleInterpolator", "value": 2042}
              ]
            },
            {"name": "ISchedulable", "value": 1041},
            {"name": "Parallel", "value": 5176},
            {"name": "Pause", "value": 449},
            {"name": "Scheduler", "value": 5593},
            {"name": "Sequence", "value": 5534},
            {"name": "Transition", "value": 9201},
            {"name": "Transitioner", "value": 19975},
            {"name": "TransitionEvent", "value": 1116},
            {"name": "Tween", "value": 6006}
          ]
        }
      ]
    };

    // 创建颜色比例尺
    const color = d3.scaleLinear()
      .domain([0, 5])
      .range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
      .interpolate(d3.interpolateHcl);

    // 计算打包布局
    const pack = data => d3.pack()
      .size([width, height])
      .padding(3)
      (d3.hierarchy(data)
        .sum(d => d.value)
        .sort((a, b) => b.value - a.value));
    const root = pack(data);

    // 创建SVG容器
    const svg = d3.create("svg")
      .attr("viewBox", `-${width / 2} -${height / 2} ${width} ${height}`)
      .attr("width", width)
      .attr("height", height)
      .attr("style", `max-width: 100%; height: auto; display: block; margin: 0 -14px; background: ${color(0)}; cursor: pointer;`);

    // 添加圆形节点
    const node = svg.append("g")
      .selectAll("circle")
      .data(root.descendants().slice(1))
      .join("circle")
      .attr("fill", d => d.children ? color(d.depth) : "white")
      .attr("pointer-events", d => !d.children ? "none" : null)
      .on("mouseover", function() { d3.select(this).attr("stroke", "#000"); })
      .on("mouseout", function() { d3.select(this).attr("stroke", null); })
      .on("click", (event, d) => focus !== d && (zoom(event, d), event.stopPropagation()));

    // 添加文本标签
    const label = svg.append("g")
      .style("font", "10px sans-serif")
      .attr("pointer-events", "none")
      .attr("text-anchor", "middle")
      .selectAll("text")
      .data(root.descendants())
      .join("text")
      .style("fill-opacity", d => d.parent === root ? 1 : 0)
      .style("display", d => d.parent === root ? "inline" : "none")
      .text(d => d.data.name);

    // 提前声明focus和view变量,避免未定义错误
    let focus = root;
    let view;

    // 绑定容器点击事件
    svg.on("click", (event) => zoom(event, root));

    // 初始缩放至根节点
    zoomTo([focus.x, focus.y, focus.r * 2]);

    // 将SVG挂载到Vue容器中
    d3.select(".container").append(() => svg.node());

    // 缩放定位函数
    function zoomTo(v) {
      const k = width / v[2];
      view = v;
      label.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
      node.attr("transform", d => `translate(${(d.x - v[0]) * k},${(d.y - v[1]) * k})`);
      node.attr("r", d => d.r * k);
    }

    // 点击缩放函数
    function zoom(event, d) {
      const focus0 = focus;
      focus = d;
      const transition = svg.transition()
        .duration(event.altKey ? 7500 : 750)
        .tween("zoom", d => {
          const i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2]);
          return t => zoomTo(i(t));
        });

      label
        .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
        .transition(transition)
        .style("fill-opacity", d => d.parent === focus ? 1 : 0)
        .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
        .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
    }
  }
};
</script>

关键修改说明

  • 挂载SVG:通过d3.select(".container").append(() => svg.node())将生成的SVG元素插入到Vue的容器DOM中。
  • 变量顺序调整:将focus和view变量提前声明到zoomTo调用之前,修复未定义错误。
  • 移除无意义的return svg.node();:mounted钩子不需要返回值,直接操作DOM完成挂载即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:19:54