功能点描述:点击侧边栏对象树中的对象,在拓扑图中定位到所选对象,反之亦可。
思路:
1.分成两部分,首先是单向定位,点击侧边栏,定位到拓扑图。这部分思路就是获取到当前点击的对象的坐标,根据坐标对拓扑图中的画布<g> 进行偏移。难在偏移应该如何做。
2.然后是反向定位,点击拓扑图中的节点,然后将该节点的id传到侧边栏,侧边栏做出标注显示。
解决方案:
最终完美方案:
/**
* @Description :处理关联设备的显示和偏移。
* @author : yjz
* @param :
* @return
* @exception :
* @date : 2019/1/14
*/
handleDeviceShowAndTranslate() {
this.selectOneDevice();
this.clickDeviceTranslate();
},
/**
* @Description :标识拓扑图中的单一设备
* @author : yjz
* @param : 拓扑中需要标记的设备的坐标
* @return :
* @exception :
* @date : 2019/04/04
*/
selectOneDevice :function () {
let selectedDevice = curThis.topologyNodeList[curThis.clickDeviceId]
let startXAxis = selectedDevice.xAxis;
let startYAxis = selectedDevice.yAxis;
d3.select("#checkPath")
.attr("d","M" + (startXAxis - 2) + ' ' + (startYAxis - 2) +' ' + 'h36 v36 h-36 Z')
.attr("stroke","#BEBEBE")
.attr("stroke-width",2)
.attr("fill","none")
.style("display", "block");
},
/**
* @Description :定位偏移函数。
* @author : yjz
* @param :
* @return
* @exception :
* @date : 2019/1/14
*/
clickDeviceTranslate: function () {
//被选中设备的x/y坐标
let x = this.topologyNodeList[this.clickDeviceId].xAxis + 16;
let y = this.topologyNodeList[this.clickDeviceId].yAxis + 16;
//被选中设备相对于svg左上角的的x/y坐标
let xForSvg = x + curThis.groupLeft;
let yForSvg = y + curThis.groupTop;
//当前可视化窗口的大小以及中心点
let visualWidth = 1451;
let visualHeight = 468;
let visualCenterX = visualWidth/2;
let visualCenterY = visualHeight/2;
//判断,当被点击设备的相对坐标不在可视窗口内时,进行偏移
if ( xForSvg < 0 || xForSvg > 1400 || yForSvg < 0|| yForSvg > 400) {
curThis.groupLeft = (visualCenterX - x )
curThis.groupTop = ( visualCenterY - y )
svg.call(svgZoom.transform, d3.zoomIdentity.translate(curThis.groupLeft,curThis.groupTop).scale(1));
}
},
需要注意的有两点:
1.何时进行偏移:
当点击侧边栏设备的时候,判断当前设备是否在可视窗口内,若在,不偏移,若不在,偏移。
2.进行偏移的偏移量如何计算:
假设点击非可视窗户设备,让其偏移到可视窗口中间。需要分析svg的结构,看下进行transform的那个<g>群组,节点的坐标以及svg之间有什么关系,需要选定一个固定的标准进行偏移。
经过观察,我们发现,我的svg结构下,
(1)当并未对<g>群组进行偏移时:此时<g>的transform值是(0,0).节点的坐标是相对于svg的左上角的,也就是可视窗口的左上角。就是说节点的坐标就是以svg左上角为坐标原点的坐标系下的。
(2)当对<g>群组进行偏移时:此时<g>的transform值是(left,top),节点的坐标没变,因为没有直接拖拽节点。但是此时依然选定svg左上角为坐标原点的坐标系下,节点的坐标变了,应该是原坐标 + transform值,此时就是和上种情况同一坐标系下的新坐标。
那我们需要把节点移动到可视窗口中间,也是在同意坐标系下,直接计算此时选中节点的新坐标,与窗口中点坐标值的差,偏移量=窗口中点坐标值 - 选中节点的新坐标。
3.如何进行偏移:
不可以直接对g.attr('transform',..),因为这样会出现一个bug。
应该在此调用最开始使用的zoom函数。
svg.call(svgZoom.transform, d3.zoomIdentity.translate(curThis.groupLeft,curThis.groupTop).scale(1));
其中svgZoom是这个:
/**
* @Description :添加缩放事件
* @author : yjz
* @param :
* @return :
* @exception :
* @date : 2019/04/14
*/
addZoomToSvg() {
//创建zoom函数,实现缩放和平移
svgZoom = d3.zoom()
.scaleExtent([0.8, 5])//设置缩放的范围
.on("zoom",function () {//和drag类似,设置缩放监听事件,"start" "zoom" "end" 三个状态。
curThis.$options.methods.zoom();
});
// svg调用zoom函数,并取消双击事件
svg.call(svgZoom).on("dblclick.zoom", () => {});
},
/**
* @Description :添加缩放的缩放状态的响应函数
* @author : yjz
* @param :
* @return :
* @exception :
* @date : 2019/04/14
*/
zoom() {
//左偏移量,就是<g>的左边界距离显示区域的距离
curThis.groupLeft = d3.event.transform.x;
//上偏移量,就是<g>的上边界距离显示区域的距离
curThis.groupTop = d3.event.transform.y;
//记录scale的倍数
curThis.scaleK = d3.event.transform.k;
return g.attr('transform', "translate(" + curThis.groupLeft + "," + curThis.groupTop + ") scale(" + curThis.scaleK + ")");
},
基本解决了焦点定位的问题。
以下是第一次构思的思路以及出现的问题,无用,上面是最终的解决方案
1.当对画布<g>进行偏移的时候,需要搞清的是几个变量之间的关系。画布<g>的位置,节点的x/y坐标,可显示区域的宽度和高度。根据这三者的关系,做所有情况的演绎,然后每种情况进行偏移。具体代码如下:
zoom: function () {
//记录被sidebar 选中的设备在topologyNodeList中的位置。
var position = -1;
//记录在topologyNodeList是否有被选中的设备,有记为1。
var selected = 0;
//左偏移量,就是<g>的左边界距离显示区域的距离
var left = d3.event.transform.x;
//上偏移量,就是<g>的上边界距离显示区域的距离
var top = d3.event.transform.y;
// console.log(left);
// console.log(top);
//遍历topologyNodeList ,找出被选中的设备。
for (var i=0;i<this.topologyNodeList.length;i++) {
if(this.topologyNodeList[i].topologyState === 1) {
position = i;
selected = 1;
}
}
//如果有被选中的设备,计算偏移量。
if (selected === 1) {
var x = this.topologyNodeList[position].xAxis;
var y = this.topologyNodeList[position].yAxis;
var visualWidth = 1000;
var visualHeight = 350;
/**
* 1.判断当前<g>画布所在的位置,即左上角处于哪个象限。
*/
if (left >= 0 && top >= 0) {
/**
* 1.1 若<g>处于第一象限,判断当前选中的设备的坐标,是否在显示区域
*/
if ((left + x) >= visualWidth) {
//此时将所选设备向左进行偏移
left = -(((left + x) - visualWidth) + (visualWidth * 0.5))
}
if ((top + y) >= visualHeight) {
//此时将所选设备向上进行偏移
top = -(((top + y) - visualHeight) + (visualHeight * 0.5))
}
} else if (left <= 0 && top >= 0) {
/**
* 1.2 若<g>处于第二象限,判断当前选中的设备的坐标,是否在显示区域
*/
if (x <= Math.abs(left)) {
//此时将所选设备向右进行偏移
left = (Math.abs(left) - x) + (visualWidth * 0.5);
}
if (x >= (Math.abs(left) + visualWidth)) {
//此时将所选设备向左进行偏移
left = -((x - (Math.abs(left) + visualWidth)) + (visualWidth * 0.5))
}
if ((top + y) >= visualHeight) {
//此时将所选设备向上进行偏移
top = -(((top + y) - visualHeight) + (visualHeight * 0.5))
}
} else if (left <= 0 && top <= 0) {
/**
* 1.3 若<g>处于第三象限,判断当前选中的设备的坐标,是否在显示区域
*/
if (x <= Math.abs(left)) {
//此时将所选设备向右进行偏移
left = (Math.abs(left) - x) + (visualWidth * 0.5);
}
if (x >= (Math.abs(left) + visualWidth)) {
//此时将所选设备向左进行偏移
left = -((x - (Math.abs(left) + visualWidth)) + (visualWidth * 0.5))
}
if (y <= Math.abs(top)){
//此时将所选设备向下进行偏移
top = (Math.abs(top) - y) + (visualHeight * 0.5);
}
if (y >= (Math.abs(top) + visualHeight)) {
//此时将所选设备向上进行偏移
top = -((y - (Math.abs(top) + visualHeight)) + (visualHeight * 0.5))
}
} else if (left >= 0 && top <= 0) {
/**
* 1.4 若<g>处于第四象限,判断当前选中的设备的坐标,是否在显示区域
*/
if ((left + x) >= visualWidth) {
//此时将所选设备向左进行偏移
left = -(((left + x) - visualWidth) + (visualWidth * 0.5))
}
if (y <= Math.abs(top)){
//此时将所选设备向下进行偏移
top = (Math.abs(top) - y) + (visualHeight * 0.5);
}
if (y >= (Math.abs(top) + visualHeight)) {
//此时将所选设备向上进行偏移
top = -((y - (Math.abs(top) + visualHeight)) + (visualHeight * 0.5))
}
}
}
g.attr('transform',"translate(" + left + "," + top + ") scale(" + d3.event.transform.k + ")")
var startXAxis = x - 2 ;
var startYAxis = y - 2;
d3.select("#checkPath")
.attr("d","M" + startXAxis + ' ' + startYAxis +' ' + 'h36 v36 h-36 Z')
.attr("stroke","#BEBEBE")
.attr("stroke-width",2)
.attr("fill","none")
.style("display", "block");
},
最下面这几行代码,是为选中的节点添加一个标注显示,在之前的文章中说过,就不在赘述了。
现在还没完全调通,发现问题以后及时更新。
目前发现了一个问题,偏移的方向不对,应该考虑将定位和 平移时 的zoom()分开。
现在搞不清楚这个问题了,分开以后又出现了以前的问题,定位偏移之后,在对svg进行平移,焦点丢失,不是在本页面结构继续平移,而是接着上次平移的最后位置继续平移。
得想办法把定位的偏移与 zoom的平移放在一起,让zoom记录下上次平移的位置为定位偏移的位置。目前新建了一个全局变量。