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
相关产品推荐
相关产品推荐

