如何重置/删除存储在d3 v4中元素中的缩放变换?

时间:2017-10-06 15:55:44

标签: d3.js

我正在尝试在d3(v4)图表中实现缩放和刷牙。

我让它们都分开工作,但是当我尝试在同一个图表上实现这两个功能时,问题出现了。

我的方案如下: 1.用户使用画笔显示图表的特定区域。 2.然后它们进行缩放/平移,但这会导致视图跳回到旧位置,因为存储的缩放变换不知道刷牙所做的更改。

我的理解是当前缩放变换(缩放+转换)存储在内部__zoom属性中的DOM元素内。每当您与元素交互时,缩放插件都会自动调整此项(例如,通过滚动鼠标滚轮)。

我发现您可以使用d3.zoomTransform 获取元素的当前缩放变换。

如何重置/删除存储的缩放变换(例如,在平移之后,以便从刷牙停止的位置继续进行任何后续缩放)?

注意:我不想更改缩放,而只是更新存储的缩放变换以将新缩放视为“标识”。这很重要,因为我希望能够在刷牙等​​时顺利地从一个刻度转换到另一个刻度。

1 个答案:

答案 0 :(得分:3)

我最终解决这个问题的方法是:

  1. 在缩放处理程序中,使用transform.rescaleX()获取新的转换后的比例
  2. 根据转换后的比例
  3. 更新主比例的域名
  4. 根据比例
  5. 更新x轴
  6. 将元素上的变换重置为d3.zoomIdentity。
  7. 这里的关键是在更新比例之后,DOM元素上存储的变换总是返回到标识(即scale = 1,translate = 0,0)。

    这意味着我们不需要担心刷牙/缩放或者对不同元素上的比例的任何编程变化都不会发生冲突或彼此具有不同的值。我们实际上只是继续将非常小的比例因子应用于元素。

    就代码示例而言,以下是我工作图表中的相关部分:

    // class contains:
    // this.xScale - stored scale for x-axis
    // this.xAxis - a d3 Axis
    // this.xAxisElement - a d3 selection for the element on which the x-axis is drawn
    // this.zoomX - a d3 ZoomBehavior
    // this.chartElement - a d3 selection for the element on which the zooming is added
    
    protected setupZooming(): void {
      this.zoomX = d3.zoom().on('zoom', () => { this.onZoomX(); });
    
      this.zoomXElement = this.xAxisElement
        .append('rect')
          .attr('fill', 'none')
          .style('pointer-events', 'all')
          .attr('width', this.width)
          .attr('height', this.margin.bottom)
          .call(this.zoomX);
    }
    
    onZoomX(): void {
      const transform: d3.ZoomTransform = d3.event.transform;
    
      if (transform.k === 1 && transform.x === 0 && transform.y === 0) {
        return;
      }
    
      const transformedXScale = transform.rescaleX<any>(this.xScale);
    
      const from = transformedXScale.domain()[0];
      const to = transformedXScale.domain()[1];
    
      this.zoomXTo(from, to, false);
    
      this.chartElement.call(this.zoomX.transform, d3.zoomIdentity);
    }
    
    zoomXTo(x0: Date, x1: Date, animate: boolean): void {
      const transitionSpeed = animate ? 750 : 0;
    
      this.xScale.domain([x0, x1]);
    
      this.xAxisElement.transition().duration(transitionSpeed).call(this.xAxis);
      this.updateData(transitionSpeed);
    }
    
    updateData(transitionSpeed: number): void {
      // ...
    }
    

    如果在我的其他代码的上下文之外不容易理解这个提取,那么道歉,但希望它仍然有用。