You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3.js独立X/Y轴缩放时轴域自动对齐问题求助

D3.js独立X/Y轴缩放时轴域自动对齐问题求助

嘿,我仔细看了你的代码,发现问题根源在于你每次都是基于初始的staticBaseX/staticBaseY来重新计算缩放域,而不是基于当前已经调整过的baseX/baseY。这就导致只要触发了“同时更新X/Y”的操作(比如单指平移、对角线 pinch、滚轮),轴域就会被拉回和初始缩放绑定的状态,看起来像是“自动对齐”了。

举个例子:假设你已经单独缩放X轴,把X域改成了[-20,20],这时候做一次单指平移,你用t.rescaleX(staticBaseX)计算新域,但staticBaseX的初始域是[-10,10],所以平移后的X域会基于初始范围重新计算,直接覆盖你之前调整好的X域,看起来就像是突然“跳回去”和Y域对齐了——这就是你看到的“snap”行为。

解决思路

  • 单独缩放X/Y轴时,继续用staticBaseX/staticBaseY来rescale,这样能保证只修改目标轴,另一轴的当前状态不受影响。
  • 对于平移、对角线 pinch、滚轮这类需要同时调整X/Y的操作,必须基于**当前的baseX/baseY**来计算新域,这样才能在已调整的范围基础上继续变换,不会出现跳回的情况。

修改后的代码

const staticBaseX = d3.scaleLinear().domain([-10, 10]).range([margin, svgWidth - margin]);
const staticBaseY = d3.scaleLinear().domain([-10, 10]).range([svgHeight - margin, margin]);
let baseX = staticBaseX.copy();
let baseY = staticBaseY.copy();

const ANGLE_TOLERANCE = 10; // degrees

const zoomBehavior = d3.zoom()
  .filter((event) => {
    const t = event.type;
    return t === 'wheel' || t === 'mousedown' || t === 'mousemove' || t === 'touchstart' || t === 'touchmove';
  })
  .scaleExtent([1e-6, 1e6])
  .on('zoom', (event) => {
    const t = event.transform;
    const se = event.sourceEvent;
    let newX = baseX.domain();
    let newY = baseY.domain();

    if (se?.touches && se.touches.length === 2) {
      // Two-finger pinch gesture — detect angle
      const [p1, p2] = se.touches;
      const dx = p2.clientX - p1.clientX;
      const dy = p2.clientY - p1.clientY;
      let angle = Math.abs(Math.atan2(dy, dx)) * 180 / Math.PI;
      if (angle > 180) angle -= 180;

      const nearH = angle <= ANGLE_TOLERANCE || angle >= 180 - ANGLE_TOLERANCE;
      const nearV = angle >= 90 - ANGLE_TOLERANCE && angle <= 90 + ANGLE_TOLERANCE;

      if (nearH) {
        // Only update X, keep Y fixed
        newX = t.rescaleX(staticBaseX).domain();
      } else if (nearV) {
        // Only update Y, keep X fixed
        newY = t.rescaleY(staticBaseY).domain();
      } else {
        // Diagonal: update both based on current domains
        newX = t.rescaleX(baseX).domain();
        newY = t.rescaleY(baseY).domain();
      }
    } else {
      // Pan, wheel, or single-finger: update both based on current domains
      newX = t.rescaleX(baseX).domain();
      newY = t.rescaleY(baseY).domain();
    }

    // Store the current domains
    currentXRange = newX;
    currentYRange = newY;
    baseX.domain(newX);
    baseY.domain(newY);

    plotGraph(currentExpr, currentXRange, currentYRange, true);
  });

额外说明

修改后你可以测试一下:单独缩放X/Y轴的逻辑保持正常,而平移、对角线缩放、滚轮操作会基于你当前调整好的轴域继续变换,不会再出现“自动对齐”的跳变问题。另外滚轮缩放的行为也会更符合预期——基于当前的轴域进行同步缩放,而不是每次都回到初始范围重新计算。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:15:31