使用canvas和d3.js分层圆结构计算缩放和平移的方法
Posted
技术标签:
【中文标题】使用canvas和d3.js分层圆结构计算缩放和平移的方法【英文标题】:Way to calculate zoom and pan using canvas and d3.js hierarchical circle structure 【发布时间】:2019-10-28 08:43:48 【问题描述】:我目前在画布中有一个 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】:所以我想通了...我应该除以整体规模。
真的要感谢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))
【讨论】:
以上是关于使用canvas和d3.js分层圆结构计算缩放和平移的方法的主要内容,如果未能解决你的问题,请参考以下文章
Javascript / D3.js - 绘制大型数据集 - 提高 d3.js 绘制的 svg 图表中的缩放和平移速度