D3.js分级统计图:如何将文本定位到SVG路径中心?
解决分级统计图(Choropleth Map)文本居中定位问题
问题描述
我正在制作一幅分级统计图(Choropleth Map),需要将每个SVG path与title进行group分组以实现hover effect(悬停效果)。目前我遇到了文本定位难题:当前文本显示在路径顶部,我希望将每个Text放置在对应的Path中心位置。请查看以下代码片段,如何仅通过path而非坐标实现这一需求?
注意:我仅使用path,不使用坐标!
注意2:可通过开启/关闭console.log查看地图。
解决方案
核心问题是你当前代码错误地获取了文本元素自身的边界框(bbox)来计算位置,而不是对应分组里的path元素的边界框。要实现文本在path中心,需修改文本定位逻辑:
修改后的JavaScript代码
document.addEventListener("DOMContentLoaded", function () { const h = 400; const w = 400; var svg = d3 .select(".map") .append("svg") .attr("width", w) .attr("height", h) .attr("viewBox", "0 0 400 400") var groups = svg .selectAll(".groups") .data(map) .enter() .append("g") .attr("class", "region"); groups .append("path") .attr("d", (m) => m.path) .attr("stroke-width", "1") .attr("stroke", "#fff") .attr("fill","#333") groups .append("text") .text((m) => m.title) .style("font-weight", "bold") .style("font-family", "Jersey") .attr("fill","#6D6E71") .attr("text-anchor", "middle") // 水平居中对齐 .attr("dominant-baseline", "middle") // 垂直居中对齐 .style("text-transform", "uppercase") .attr("x", function() { // 获取当前分组对应path的边界框 const pathBBox = d3.select(this.parentNode).select("path").node().getBBox(); return pathBBox.x + pathBBox.width / 2; // 计算path的X轴中心 }) .attr("y", function() { const pathBBox = d3.select(this.parentNode).select("path").node().getBBox(); return pathBBox.y + pathBBox.height / 2; // 计算path的Y轴中心 }); }); const map=[{ id: 0, title: "A", path: "M 45.25 34.96 l -0.97 -3.35 l -0.97 -2.39 l 2.43 -3.35 l 1.94 -4.31 l 1.94 -5.74 l 3.89 -5.74 l 0.97 6.22 l 0.49 3.35 l 3.89 3.83 l 3.89 -0.96 l 0.97 3.35 l 3.89 0.96 l 0.49 2.87 l 3.89 1.44 l 0.49 1.91 L 76.36 34 l 2.43 -2.87 l 1.46 3.83 l 1.94 2.39 l 2.92 1.91 l 0.97 1.91 l 2.43 -0.96 l 1.46 1.44 l -1.46 3.35 l 1.46 1.44 l 1.94 1.44 v 0.96 l 3.89 -0.48 v 1.91 l 0.97 1.91 v 2.39 l 2.43 1.91 l -0.49 4.31 l 1.94 3.35 l 2.43 3.35 l -0.97 3.35 v 4.79 v 4.31 l -1.46 3.83 l 0.97 4.79 l -2.43 3.35 l -3.4 4.79 l -3.89 -0.96 l -5.35 -0.48 l -1.94 -3.83 l -2.43 -1.91 l 2.92 -5.26 l -1.94 -3.83 l -2.43 -2.87 l -3.89 -1.44 l -1.94 -1.91 l -1.46 -2.87 l -2.43 0.96 c 0 0 -0.61 -0.48 -1.94 -0.96 s -3.4 -0.96 -3.4 -0.96 l -2.43 -0.96 l -2.43 1.91 l -3.89 -0.96 l -3.4 -1.44 l -2.92 1.91 l -0.49 2.39 c 0 0 -1.58 -0.24 -3.4 -0.96 c -1.82 -0.72 -3.89 -1.91 -3.89 -1.91 l -0.97 -1.44 l 3.4 -1.91 l 1.94 -2.39 l -1.46 -2.87 v -6.7 l 5.35 -0.48 l 1.94 -1.91 l 1.94 -2.87 l 4.37 -1.44 l 0.97 -3.35 l 2.43 3.83 l 2.43 -1.44 l 1.94 -3.35 l -1.94 -2.39 l -1.94 -2.87 v -3.35 l -3.4 -2.87 l -4.37 0.96 l -2.43 -3.83 l -2.43 1.91 l -2.92 -0.48 L 45.25 34.96 Z", }, { id: 1, title: "B", path: "M 19.49 126.38 l -2.43 -4.31 l -2.92 -3.83 l 1.94 -4.31 l -4.86 -0.96 l 1.46 -7.66 l 3.4 -2.39 l -0.49 -2.87 l 1.94 -3.83 l 5.83 -3.83 l 1.94 -5.26 l -4.37 -2.39 l 2.43 -4.31 l 0.97 -4.31 l 5.35 -1.91 v -4.79 l 7.29 0.96 l 5.35 -1.44 l 7.29 4.79 l 1.46 -3.83 l 4.96 -1.03 l 4.27 1.51 l 3.89 -0.48 l 9.72 2.39 l 3.89 4.31 l 3.4 1.44 l 4.86 5.74 l -3.4 4.79 l 0.97 3.35 l -2.43 2.39 l -0.97 3.83 h -3.4 l -2.43 -3.83 l -1.46 2.87 h -2.92 l -1.46 5.27 l 1.94 1.91 l -2.43 0.48 l -2.92 4.79 l -0.97 2.87 l -1.46 2.87 l 1.94 2.39 l -2.92 0.48 h -1.94 v -2.39 l -6.32 0.48 v 3.83 h -2.43 l -0.49 4.79 l 2.92 1.44 l -1.94 1.91 v 4.31 l 2.43 1.44 l 2.92 2.39 l -0.49 3.35 l -3.4 0.48 l -2.92 -2.87 l -2.43 -1.44 v -1.92 l -3.4 -3.35 h -2.92 l -0.97 -3.35 l -5.35 -2.87 l 0.49 -2.39 l -0.49 -1.44 l -4.37 1.44 l -2.92 2.39 l -2.43 -2.87 L 19.49 126.38 Z", }, { id: 2, title: "C", path: "M 98.24 55.54 l 2.43 -2.39 l 2.43 0.48 l 2.43 1.91 l -1.94 5.26 l 2.92 0.96 l 2.43 2.87 l 2.92 -0.48 l -0.49 -3.35 l -2.43 -1.44 l 2.92 -3.35 l 5.35 0.48 l 2.43 3.35 l 1.94 2.39 l 1.46 2.39 L 124 63.2 h 4.37 l 0.97 0.96 l 2.92 -1.44 v 3.35 l 0.49 5.74 l 3.4 2.39 l 3.4 1.91 l 0.97 2.39 l -0.49 2.87 l -0.97 7.18 l 1.46 4.79 l 0.97 5.26 l 3.89 3.35 l 4.37 2.87 l 3.4 2.87 l -1.46 3.83 l -1.46 3.35 l -1.46 1.91 l -2.92 -3.35 l -4.86 4.31 l 0.49 5.27 l -3.89 -3.83 l -2.43 4.79 l -7.78 -1.91 l -5.35 1.44 l 1.94 4.79 l -2.43 2.87 l -1.46 2.39 l -4.37 -0.48 l -3.89 -3.35 h -2.43 v -5.26 l -3.89 -0.96 v -11.49 l -1.94 -2.39 l -1.94 -3.35 l -5.35 -1.44 l 0.49 -2.39 l -1.46 -0.96 v -3.83 l -0.49 -1.44 l 1.94 -2.39 l -1.46 2.39 l 2.92 -4.31 l 2.92 -1.44 v -8.14 l 1.46 -2.87 v -1.91 l -0.49 -6.7 l 1.46 -2.87 l -5.35 -5.74 L 98.24 55.54 L 98.24 55.54 L 98.24 55.54 Z", }]
关键修改说明
- 通过
d3.select(this.parentNode).select("path")获取当前文本所在分组对应的path元素 - 基于path的边界框计算中心坐标:
pathBBox.x + pathBBox.width / 2(X轴中心)、pathBBox.y + pathBBox.height / 2(Y轴中心) - 添加
dominant-baseline: middle属性,确保文本在垂直方向完全居中(也可替换为dy: "0.35em"实现类似效果)
HTML代码(无需修改)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" /> <title>Choropleth Map</title> <style> .region:hover path { cursor: pointer; fill: #ccc; } .region:hover text { fill: #fff; } </style> </head> <body> <div class="map m-auto"></div> <!-- Script Section --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://d3js.org/d3.v7.min.js"></script> </body> </html>
内容的提问来源于stack exchange,提问作者mrhrifat
相关产品推荐
相关产品推荐

