d3之实现焦点定位功能

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

思路:

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记录下上次平移的位置为定位偏移的位置。目前新建了一个全局变量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • 一、Unity简介 1. Unity界面 Shift + Space : 放大界面 Scene界面按钮渲染模式2D...
    MYves阅读 8,180评论 0 22
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,445评论 1 45
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,734评论 1 92
  • 图片中一个六岁的孩子因为好心人送他的一双新鞋,他高兴的不得了。刹那间仿佛某种东西触及到心灵,不知从什么时候开始,已...
    爱上树的老蚂蚁阅读 181评论 0 1
  • 什么是文档流? 按w3c规范中这个其实叫normal-flow,即普通流,只是国内翻译者习惯将其翻译成文档流 定义...
    勿忘初心方始终阅读 393评论 0 0