【问题标题】:d3js zooming in one direction, panning in both directionsd3js 单向放大,双向平移
【发布时间】:2020-01-19 02:59:02
【问题描述】:

我正在尝试构建一个仅在 X 轴上缩放但允许在两个轴上平移的 d3js 图表。下面的例子有我想要的效果:

https://jsfiddle.net/xpr364uo/

但是,我无法将其转换为我自己的代码。一方面,我正在渲染到画布,所以我无法在某些元素上设置“转换”属性。据我了解,我的缩放在比例的副本上使用 rescaleX/rescaleY,就像通过 d3-zoom 进行缩放的“新方法”一样:

const zoomBehavior = zoom().on('zoom', () => {
    const xDomain = event.transform.rescaleX(x2).domain();
    const yDomain = event.transform.rescaleY(y2).domain();          
    xScale.domain(xDomain);
    yScale.domain(yDomain);
    render();
});

这适用于在两个轴上缩放/平移。如何修改它以获得与小提琴相同的效果?在我的代码中,我应该如何处理 deltaPanY(来自小提琴)?

【问题讨论】:

    标签: d3.js


    【解决方案1】:

    您可以跟踪第二次缩放变换(我称之为yTransform)并使用它来重新缩放 y 轴。由于您希望 x 正常缩放,您仍然可以使用 d3.event.transform.rescaleX() 在 X 轴上重新缩放,而 yTransform 可用于在 Y 轴上重新缩放。

    平移时,yTransform 的 y 平移值应更新为当前缩放状态。相反,在缩放时,应使用yTransform 覆盖缩放状态的y 平移变化。

    也许是这样的:

    var yTransform = d3.zoomIdentity; // initial state for the y transform
    
    var zoom = d3.zoom()
      .on("zoom", function() {
        var t = d3.event.transform;  // zoom state
        x2 = t.rescaleX(x);          // rescale x as normal (t.y is irrelevant)
    
        // for a pan event, update the y translate
        if (d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;  
    
        // for a scroll, use the  current y translate
        else t.y = yTransform.y;        
    
        y2 = yTransform.rescaleY(y);   // rescale y.
    
        render();
    
      })
    

    yTranslatekx 值无关紧要:比例始终为 1,因为我们没有放大,x 平移与在 y 轴上重新缩放无关。 上面没有考虑双击事件,但我会在下面补充一下

    var dots = d3.range(100)
      .map(function() {
         return {x: Math.random(), y: Math.random()}
      })
      
    var x = d3.scaleLinear().range([0,500])
    var x2 = x.copy();
    var y = d3.scaleLinear().range([0,300])
    var y2 = y.copy();
    
    var canvas = d3.select("canvas")
    var context = canvas.node().getContext("2d");
    
    // Just for reference:
    var axis = d3.axisRight(y);
    var g = d3.select("svg").append("g");
      g.call(d3.axisRight(y2))
      
      
    
    render();
    
    var yTransform = d3.zoomIdentity;
    
    var zoom = d3.zoom()
      .on("zoom", function() {
        var t = d3.event.transform;
        x2 = t.rescaleX(x);
    
       // For dbl clicks, d3.event.sourceEvent is null.
       if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;  
       else t.y = yTransform.y;      
    
        y2 = yTransform.rescaleY(y);
       
        render();
        
      })
    
    canvas.call(zoom);
    
    
      
    function render() {
       context.clearRect(0,0,500,300);
       dots.forEach(function(d) {
          context.beginPath();
          context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
          context.stroke(); 
       })
       
       g.call(d3.axisRight(y2));
       
    }
    canvas, svg {
      position: absolute;
      top: 0;
      left: 0;
    }
    svg {
      pointer-events:none;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <canvas width="500" height="300"></canvas>
    <svg width="500" height="300"></svg>

    因为我直接修改属性 - 这不是最理想的。

    或者,我们可以跟踪 y 上的平移偏移量(带有 dbl 点击/滚轮事件和没有这些事件的 y 平移之间的差异)。 y offset 和 y translate 都可以用来创建适当的缩放变换:

    var yOffset = 0;
    var lastY = 0;
    
    var zoom = d3.zoom()
      .on("zoom", function() {
        var t = d3.event.transform;
        x2 = t.rescaleX(x);
    
       // For dbl clicks, d3.event.sourceEvent is null.
       if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
         lastY = t.y - yOffset;
         y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
       }
       else {
         yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
       }
        render();
    })
    

    var dots = d3.range(100)
      .map(function() {
         return {x: Math.random(), y: Math.random()}
      })
      
    var x = d3.scaleLinear().range([0,500])
    var x2 = x.copy();
    var y = d3.scaleLinear().range([0,300])
    var y2 = y.copy();
    
    var canvas = d3.select("canvas")
    var context = canvas.node().getContext("2d");
    
    // Just for reference:
    var axis = d3.axisRight(y);
    var g = d3.select("svg").append("g");
      g.call(d3.axisRight(y2))
      
      
    
    render();
    
    var yOffset = 0;
    var lastY = 0;
    
    var zoom = d3.zoom()
      .on("zoom", function() {
        var t = d3.event.transform;
        x2 = t.rescaleX(x);
        
       // For dbl clicks, d3.event.sourceEvent is null.
       if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
         lastY = t.y - yOffset;
         y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
       }
       else {
         yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
       }
    
        render();
        
      })
    
    canvas.call(zoom);
    
    
      
    function render() {
       context.clearRect(0,0,500,300);
       dots.forEach(function(d) {
          context.beginPath();
          context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
          context.stroke(); 
       })
       
       g.call(d3.axisRight(y2));
       
    }
    canvas, svg {
      position: absolute;
      top: 0;
      left: 0;
    }
    svg {
      pointer-events:none;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <canvas width="500" height="300"></canvas>
    <svg width="500" height="300"></svg>

    【讨论】:

    • 非常感谢。不幸的是,我仍然无法将代码翻译成我的。当我尝试缩放时,其行为与您的示例不同。见这里:codepen.io/parliament718/pen/dyPQovq。很抱歉最初没有提供这个 codepen,在 d3 中有很多不同的方法来做事情,这可能会让人很困惑。这是一支笔,我的原始代码带有双向缩放/平移功能:codepen.io/parliament718/pen/xxbQGgp Btw,我注意到在您的代码中您直接修改了 d3.zoomIdentity.y。这是允许的吗?在打字稿中,我收到一个错误,yTransform.y 是只读的
    • 另外,这是另一支笔,我使用原始帖子中的示例非常接近,但是您会注意到在缩放和拖动之间交替时会出现“跳转”codepen.io/parliament718/pen/xxbQGJz 修复跳转将解决我的问题
    • 今天或明天我将有机会重温。
    • @parliament,我添加了一种替代方法,该方法应该避免修改只读属性 - 似乎正在工作。 x 和 y 的不同比例可能会在 d3-zoom 的未来版本中得到解决——这在当前版本中给不少人带来了挑战。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 2010-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多