使用canvas和d3.js分层圆结构计算缩放和平移的方法

时间:2019-06-13 17:35:03

标签: javascript angularjs animation d3.js html5-canvas

我目前在画布上有一个d3分层圆包图(在this awesome tutorial!的帮助下)

除了放大节点外,我还想使图形也可以用鼠标平移并用滚轮缩放。 I used this.

现在,当我第一次平移然后使用初始比例缩放到节点时(页面首次加载时),它会按预期工作。它会从图形的最后平移位置放大到该节点。

但是,当我尝试从缩放状态进行平移和缩放时,视图将在一个完全不同的位置开始,然后在预期的节点处结束。如果从缩放状态不平移,而是从一个节点跳转/缩放到另一个节点,则过渡将按预期工作。我不确定为什么会这样。谁能帮我弄清楚我在做什么错?预先感谢!

(很抱歉有任何假冒行为-这是我的第一个堆栈溢出问题...)


  @ViewChild('myCanvas') canvas: ElementRef;
  @ViewChild('myCanvas') hiddenCanvas: ElementRef;
  public context: CanvasRenderingContext2D;
  public hiddenContext: CanvasRenderingContext2D;

  public focus;
  public xleftView = 0;
  public ytopView = 0;

  public widthViewOriginal = window.innerWidth;           
  public heightViewOriginal = window.innerHeight;
  public widthView = this.widthViewOriginal;           
  public heightView = this.heightViewOriginal;
  public widthCanvas = window.innerWidth;
  public heightCanvas = window.innerHeight;

  public mouseDown = false;
  public movedCoordinatesX;
  public movedCoordinatesY;
  public lastX = 0;
  public lastY = 0;

  public zoomInfo = {
    centerX: window.innerWidth / 2,
    centerY: window.innerHeight / 2,
    scale: 1
  };

    public handleMouseDown(event) {
      // get where the mouse first clicked to pan the graph
      this.movedCoordinatesX = event.clientX
      this.movedCoordinatesY = event.clientY
      if (event.which === 1) {
        this.mouseDown = true;
      }
    }

    public handleMouseUp(event) {
      this.mouseDown = false;
      // Get the distance moved in the x & y direction from the initial click to the end mouse position
      this.movedCoordinatesX = this.movedCoordinatesX - event.clientX
      this.movedCoordinatesY =  this.movedCoordinatesY - event.clientY

      // add distance in the X & Y to the old view's X & Y (to make the transition happen from the current position
      this.vOld[0] = (this.vOld[0] + this.movedCoordinatesX) 
      this.vOld[1] = (this.vOld[1] + this.movedCoordinatesY) 
    }

    public handleMouseMove(event) {

      var X = event.clientX - this.context.canvas.offsetLeft -  this.context.canvas.clientLeft + this.context.canvas.scrollLeft;
      var Y = event.clientY -  this.context.canvas.offsetTop -  this.context.canvas.clientTop +  this.context.canvas.scrollTop;
      if (this.mouseDown) {
          var dx = (X - this.lastX) / this.widthCanvas * this.widthView;
          var dy = (Y - this.lastY)/ this.heightCanvas * this.heightView;
          this.xleftView -= dx;
          this.ytopView -= dy;
      }
      this.lastX = X;
      this.lastY = Y;
  }

  // Listen for clicks on the main canvas
  public zoomInToNode(e) {
    // We actually only need to draw the hidden canvas when there is an interaction. 
    // This sketch can draw it on each loop, but that is only for demonstration.
    this.drawCanvas(this.hiddenContext, true);

    //Figure out where the mouse click occurred.
    var mouseX = e.layerX;
    var mouseY = e.layerY;
    this.lastX = mouseX
    this.lastY = mouseY

    // Get the corresponding pixel color on the hidden canvas and look up the node in our map.
    // This will return that pixel's color
    var col = this.hiddenContext.getImageData(mouseX, mouseY, 1, 1).data;
    //Our map uses these rgb strings as keys to nodes.
    var colString = "rgb(" + col[0] + "," + col[1] + ","+ col[2] + ")";
    var node = this.colToCircle[colString];

    if(node) {
      if (this.focus !== node) {

        this.xleftView = 0
        this.ytopView = 0
        this.hiddenContext.setTransform(1, 0, 0, 1, 0, 0);
        this.zoomToCanvas(node); 
      }
      else {

        this.xleftView = 0
        this.ytopView = 0
        this.hiddenContext.setTransform(1, 0, 0, 1, 0, 0);

        this. widthView = this.widthViewOriginal; 
        this. heightView = this.heightViewOriginal;
        this. widthCanvas = window.innerWidth;
        this. heightCanvas = window.innerHeight;
        this.zoomToCanvas(this.rooting);
      }
    }//if
  };

  public zoomToCanvas(focusNode) {
    // the node that we want to zoom to
    this.focus = focusNode;

    var v = [this.focus.x, this.focus.y, this.focus.r * 2.05]; //The center and width of the new "viewport"

    this.interpolator = d3.interpolateZoom(this.vOld, v); //Create interpolation between current and new "viewport"

    this.duration = this.interpolator.duration; //Interpolation gives back a suggested duration         
    this.timeElapsed = 0; //Set the time elapsed for the interpolateZoom function to 0  
    this.vOld = v; //Save the "viewport" of the next state as the next "old" state

  }//function zoomToCanvas

  // Perform the interpolation and continuously change the zoomInfo while the "transition" occurs
  public interpolateZoom(dt) {
    if (this.interpolator) {
      this.timeElapsed += dt;
      var t = this.ease(this.timeElapsed / this.duration);

      if(isFinite(t)) { 
      this.zoomInfo.centerX = this.interpolator(t)[0];
      this.zoomInfo.centerY = this.interpolator(t)[1];
      this.zoomInfo.scale = this.diameter / this.interpolator(t)[2];

      if (this.timeElapsed >= this.duration) this.interpolator = null;
      }
    }//if
  }//function zoomToCanvas

  public drawCanvas(chosenContext, hidden) {
    chosenContext.save(); 
    chosenContext.setTransform(1,0,0,1,0,0);
    chosenContext.clearRect(0, 0, this.canvas.nativeElement.width, this.canvas.nativeElement.height);
    chosenContext.scale(this.widthCanvas/this.widthView, this.heightCanvas/this.heightView);
    chosenContext.translate(-this.xleftView,-this.ytopView);
    chosenContext.restore(); 

  }

1 个答案:

答案 0 :(得分:0)

所以我想通了...我应该除以总体规模。

我真的很感谢this answer给我的启发!

    public handleMouseUp(event) {
      this.mouseDown = false;
        this.movedCoordinatesX = this.movedCoordinatesX - event.clientX
        this.movedCoordinatesY =  this.movedCoordinatesY - event.clientY

        if(this.zoomInfo.scale == 1) {
          this.vOld[0] = (this.vOld[0] + this.movedCoordinatesX) 
          this.vOld[1] = (this.vOld[1] + this.movedCoordinatesY)
        } else {
          this.vOld[0] = (this.vOld[0] + (this.movedCoordinatesX/this.zoomInfo.scale)) 
          this.vOld[1] = (this.vOld[1] + (this.movedCoordinatesY/this.zoomInfo.scale))
        }
    }