D3.js可缩放树形图(Treemap)内部矩形尺寸异常问题求助
Looks like I found the issue with your treemap's sizing problem—the culprit is how your position function handles scale calculations, especially when navigating into child levels like "Maxillopoda". Let's break this down and fix it:
The Root Cause
Your x and y scales aren’t being updated to match the current root node’s coordinate bounds when you zoom into a subgroup. Right now, you’re likely using a global scale (mapped to the full treemap’s 0-1 domain) to calculate child node sizes. This skews proportions once you’re in a sub-level: even though "Halopitilus" has a value of 1, its relative width within its parent’s local bounds gets inflated when using the global scale, leading to the oversized rectangle.
Fix Steps & Revised Code
Here’s how to adjust your code to align visual sizes with node values:
Update scales dynamically when zooming
Every time you switch to a new root (e.g., clicking into "Maxillopoda"), redefine yourxandyscales to use the current root’sx0/x1andy0/y1as their domain. This ensures all child node calculations are relative to their parent’s bounds, not the entire treemap.Fix header and child node positioning
Your originaltranslate(0,-30)pushed the root header off the canvas, and child nodes didn’t account for the header’s height offset.
Revised Zoom/Update Function
function zoomTo(root) { // Calculate available space for treemap content (excluding the 30px header) const contentWidth = width; const contentHeight = height - 30; // Create scales tied to the current root's coordinate range const x = d3.scaleLinear() .domain([root.x0, root.x1]) .range([0, contentWidth]); const y = d3.scaleLinear() .domain([root.y0, root.y1]) .range([0, contentHeight]); // Pass updated scales to the position function position(group, root, x, y); }
Updated Position Function
function position(group, root, x, y) { group.selectAll("g") .attr("transform", d => { if (d === root) { // Position root header at the top of the canvas return `translate(0, 0)`; } else { // Position child nodes below the header using dynamic scales return `translate(${x(d.x0)}, ${y(d.y0) + 30})`; } }) .select("rect") .attr("width", d => d === root ? width : x(d.x1) - x(d.x0)) .attr("height", d => d === root ? 30 : y(d.y1) - y(d.y0)); }
Quick Additional Check
Ensure your treemap layout uses d3.treemap().sum(d => d.value)—this guarantees the sizing logic is correctly tied to your nodes’ numerical values from the start.
By tying scales to the current root’s bounds, each child node’s width/height is calculated as a proportion of its parent’s available space, which aligns visual size with actual value. The header positioning fixes the offset so all elements stay within the canvas, and the content area scales correctly.
内容的提问来源于stack exchange,提问作者Herlock

