Vue3 TypeScript组合式SFC中D3.js无法渲染图表求助
Vue3 + D3.js 组件无DOM渲染问题排查与修复
错误原因分析
- DOM引用未就绪:组件顶层代码直接访问
lineElement.value时,组件尚未完成挂载,ref的值为null。强制类型断言as SVGGElement会让D3选择空对象,所有后续DOM操作都不会生效。 - Y轴方向错误:SVG坐标系默认向下为正,当前
yScale的range([10, 950])会将数值大的y值映射到SVG底部之外,即使生成路径也无法在可视区域内显示。
修复方案
- 将D3的DOM操作逻辑移入
onMounted钩子,确保组件挂载后再获取真实DOM元素。 - 反转Y轴的range,将
[10, 950]改为[950, 10],适配SVG坐标系让数据正确显示。 - 增加
stroke-width样式,提升线条和标记的可见性。
修复后的完整代码
<template> <div> <svg xmlns="http://www.w3.org/2000/svg" ref="svgElement" class="center-plot-graph"> <g ref="lineElement" class="lineElements"> <path class="splineElement" /> <g class="symbolElement" /> </g> </svg> </div> </template> <script setup lang="ts"> import { select, scaleLinear, symbol, symbolCircle, line, curveLinear, ValueFn, BaseType, } from "d3"; import { ref, onMounted } from "vue"; type Vector1d = Array<number>; type Vector2d = Array<Vector1d>; const svgElement = ref<SVGSVGElement | null>(null); const lineElement = ref<SVGGElement | null>(null); const lineData1: Vector2d = [ [10.5, 0, -26.5, 1], [10.515418023668452, 0, -25.98615240945993, 0.9999999999999999], [10.54673612522916, 0, -25.4242010779106, 0.9999999999999998], [10.599262000092798, 0, -24.79828284532514, 0.9999999999999998], [10.682246533999162, 0, -24.080779736377877, 0.9999999999999999], [10.814880527142483, 0, -23.214463145842437, 0.9999999999999998], [11.054647461420542, 0, -22.028119146822945, 0.9999999999999999], [11.671811400028488, 0, -19.86283988201097, 0.9999999999999998], [12.613086386434938, 0, -17.545566003251505, 0.9999999999999999], [13.813926262485333, 0, -15.363942341711933, 1], [15.263842352648265, 0, -13.35830776532418, 1.0000000000000004], [16.93791350677736, 0, -11.540160490114632, 1.0000000000000007], [18.800820600423645, 0, -9.900220288676735, 1.0000000000000004], [20.817015127495804, 0, -8.428748556487237, 1.0000000000000004], [22.9509485819025, 0, -7.116006689022895, 1.0000000000000002], [25.16710035838422, 0, -5.9522535772986, 1], [27.437849524060642, 0, -4.927039014178517, 0.9999999999999999], [29.7542920863866, 0, -4.028232704145715, 0.9999999999999999], [32.11023490194244, 0, -3.243461017781614, 0.9999999999999998], [34.499484827308535, 0, -2.56035032566763, 0.9999999999999997], [36.91584871906518, 0, -1.9665269983851892, 0.9999999999999996], [39.35328336132695, 0, -1.4504051633262336, 0.9999999999999997], [41.8071311282343, 0, -1.0076791848683684, 0.9999999999999999], [44.273483525476955, 0, -0.6379795521514762, 1], [46.499497521985354, 0, -0.3675404908536285, 0.9999999999999998], [47.70003443876498, 0, -0.24619654497075863, 1], [48.571814387462624, 0, -0.16874721429387524, 0.9999999999999999], [49.292111379398605, 0, -0.11149361650523448, 1], [49.91998747855483, 0, -0.06654412557289584, 1], [50.483859148305946, 0, -0.03010369508701564, 1.0000000000000002], [51, 0, 0, 1], ]; const lineData2: Vector2d = [ [10.5, 0, -26.5, 1], [10.573820291076342, 0, -22.314239546778368, 0.9999999999999995], [13.359019071367536, 0, -13.451591871333579, 1.0000000000000007], [24.504724058628779, 0, -5.452588866468975, 1.0000000000000003], [37.471579656269877, 0, -1.4571818281579628, 0.9999999999999992], [46.497084577993259, 0, -0.24908378543712748, 1.0000000000000003], [51, 0, 0, 1], ]; onMounted(() => { if (!lineElement.value) return; const xScale = scaleLinear() .domain([-26, 0]) .range([10, 920]); // 反转Y轴range,适配SVG坐标系 const yScale = scaleLinear() .domain([10, 52]) .range([950, 10]); // 绘制曲线 select(lineElement.value) .select(".splineElement") .datum(lineData1) .attr( "d", (d: Vector2d) => { return line<Vector1d>() .x((di) => xScale(di[2])) .y((di) => yScale(di[0])) .curve(curveLinear)(d); } ) .style("stroke", "black") .style("fill", "none") .style("stroke-width", 2); // 绘制标记点 const SymbolTransform: ValueFn<SVGPathElement | BaseType, Vector1d, string> = (d) => { const x = xScale(d[2]); const y = yScale(d[0]); return `translate(${x},${y})`; }; select(lineElement.value) .select(".symbolElement") .selectAll<SVGPathElement, Vector1d>("path") .data(lineData2) .join("path") .attr("d", symbol(symbolCircle, 20)) .style("stroke", "red") .style("fill", "none") .style("stroke-width", 2) .attr("transform", SymbolTransform); }); </script> <style scoped lang="less"> .center-plot-graph { position: absolute; width: 936px; height: 960px; border: 1px solid #eee; /* 可选:添加边框方便观察SVG范围 */ } </style>
额外说明
- 添加了空值判断
if (!lineElement.value) return,避免DOM引用获取失败时出现错误。 - 简化了D3回调函数的参数,移除未使用的
i和a参数,让代码更简洁。 - 可选添加SVG边框,方便确认SVG的可视范围。
内容的提问来源于stack exchange,提问作者Zhipeng Liu
相关产品推荐
相关产品推荐

