D3 v4中缩放平移后调整窗口时axisBottom比例尺域偏移问题
D3 v4 缩放平移后窗口resize导致X轴域偏移问题解决
问题根因
窗口resize时,你没有基于当前的缩放平移状态更新比例尺,而是直接重置了比例尺的domain,导致之前的缩放变换状态丢失,X轴域出现偏移。核心是要在resize过程中,保留并复用当前的zoom变换来更新比例尺。
解决思路
- 保留一个原始比例尺(比如
x0),存储初始的domain和动态更新的range(对应容器宽度变化)。 - 绘制用的比例尺
x始终通过当前zoom变换对x0进行rescale得到。 - 窗口resize时,先更新
x0的range为新容器宽度,再用当前的zoom变换重新rescale得到新的x,最后更新轴和图表元素。
修改后的可复现代码
// 初始化SVG和容器 const svg = d3.select("#chart") .append("svg") .attr("width", "100%") .attr("height", 400) .call(d3.zoom().scaleExtent([1, 8]).translateExtent([[0, 0], [Infinity, 0]]).on("zoom", zoomed)); const margin = {top: 20, right: 20, bottom: 30, left: 20}; let width = parseInt(svg.style("width"), 10) - margin.left - margin.right; const height = 400 - margin.top - margin.bottom; const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`); // 原始比例尺:存储初始domain,动态更新range const x0 = d3.scaleLinear() .domain([0, 100]) .range([0, width]); // 绘制用的比例尺 let x = x0.copy(); const y = d3.scaleLinear() .domain([0, 100]) .range([height, 0]); const xAxis = g.append("g") .attr("class", "x-axis") .attr("transform", `translate(0,${height})`) .call(d3.axisBottom(x)); // 示例路径 const line = d3.line() .x(d => x(d.x)) .y(d => y(d.y)); const data = d3.range(100).map(i => ({x: i, y: Math.random() * 100})); g.append("path") .datum(data) .attr("fill", "none") .attr("stroke", "steelblue") .attr("stroke-width", 1.5) .attr("d", line); // 缩放事件处理 function zoomed() { x = d3.event.transform.rescaleX(x0); xAxis.call(d3.axisBottom(x)); g.select("path").attr("d", line); } // 窗口resize处理 window.addEventListener("resize", resize); function resize() { width = parseInt(svg.style("width"), 10) - margin.left - margin.right; // 更新原始比例尺的range为新宽度 x0.range([0, width]); // 获取当前的zoom变换 const currentTransform = d3.zoomTransform(svg.node()); // 基于新的x0和当前变换重新计算绘制用的比例尺 x = currentTransform.rescaleX(x0); // 更新X轴和路径 xAxis.call(d3.axisBottom(x)); g.select("path").attr("d", line); // 更新SVG高度(可选,根据需求调整) svg.attr("height", 400); }
关键说明
x0作为原始比例尺,只在初始化时设置domain,resize时仅更新range,保证缩放的基准始终是初始数据域。currentTransform = d3.zoomTransform(svg.node())获取当前的缩放平移状态,确保resize后不会丢失之前的操作。- 通过
transform.rescaleX(x0)重新计算x的domain,保证X轴的缩放平移状态与resize前一致,不会出现偏移。
内容的提问来源于stack exchange,提问作者Wazce
相关产品推荐
相关产品推荐

