d3之实现焦点定位功能

功能点描述:点击侧边栏对象树中的对象,在拓扑图中定位到所选对象,反之亦可。

思路:

1.分成两部分,首先是单向定位,点击侧边栏,定位到拓扑图。这部分思路就是获取到当前点击的对象的坐标,根据坐标对拓扑图中的画布 进行偏移。难在偏移应该如何做。

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的那个群组,节点的坐标以及svg之间有什么关系,需要选定一个固定的标准进行偏移。
经过观察,我们发现,我的svg结构下,
(1)当并未对群组进行偏移时:此时的transform值是(0,0).节点的坐标是相对于svg的左上角的,也就是可视窗口的左上角。就是说节点的坐标就是以svg左上角为坐标原点的坐标系下的。
(2)当对群组进行偏移时:此时的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() {

        //左偏移量,就是的左边界距离显示区域的距离
        curThis.groupLeft = d3.event.transform.x;
        //上偏移量,就是的上边界距离显示区域的距离
        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.当对画布进行偏移的时候,需要搞清的是几个变量之间的关系。画布的位置,节点的x/y坐标,可显示区域的宽度和高度。根据这三者的关系,做所有情况的演绎,然后每种情况进行偏移。具体代码如下:

zoom: function () {
        //记录被sidebar 选中的设备在topologyNodeList中的位置。
        var position = -1;
        //记录在topologyNodeList是否有被选中的设备,有记为1。
        var selected = 0;
        //左偏移量,就是的左边界距离显示区域的距离
        var left = d3.event.transform.x;
        //上偏移量,就是的上边界距离显示区域的距离
        var top = d3.event.transform.y;
//        console.log(left);
//        console.log(top);
        //遍历topologyNodeList ,找出被选中的设备。
        for (var i=0;i画布所在的位置,即左上角处于哪个象限。
           */
          if (left >= 0 && top >= 0) {
            /**
             * 1.1 若处于第一象限,判断当前选中的设备的坐标,是否在显示区域
             */
             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 若处于第二象限,判断当前选中的设备的坐标,是否在显示区域
             */
            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 若处于第三象限,判断当前选中的设备的坐标,是否在显示区域
             */
            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 若处于第四象限,判断当前选中的设备的坐标,是否在显示区域
             */
            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记录下上次平移的位置为定位偏移的位置。目前新建了一个全局变量。

你可能感兴趣的:(d3之实现焦点定位功能)