d3 svg path添加文本_d3的焦点定位实践

d3.js是一个可以绘制svg,附带方便的选择器和提供各类dom操作api的库。近期碰到这么一个需求,需要在一张svg绘制的几千个点(circle)的图内,实现点击左侧菜单,返回符合筛选条件的焦点移动到屏幕中间,例图如下:

e4b6b79d761332fde62c8f150b918c2a.png

这个需求有点类似于百度地图,搜索出对应结果,点击后屏幕自动移到中间,让使用者清楚的知道该点在整张图的位置,增强用户体验。接到这个需求的时候,我分析了一下页面的dom结构,可以看出整个地图的缩放和平移控制是由g元素的transform来控制的,见dom结构树:

8f45312113efca6322dae76d9656693e.png

每一次地图的用户拖拽平移缩放操作,都将影响g的translate的值(x,y)scale(k)。于是我想到每一次定位时,直接操作g.attr('transform', translate)即可。但是问题出现了,由于地图g的zoom不是直接创建,通过指定初始化svg.call(zoom.transorm, translate)来建立的zoom,直接操作g的transform,地图会无法识别直接消失。。。于是我把地图zoom存放在一个变量中,传参数的形式下去,代码如下:

let svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height");
 svg.call(zoom).call(zoom.transform, zoomInit(_this.svgWidth, _this.svgHeight));

let g = svg.selectAll("g");
      g.remove();
      g = svg.append("g").style("z-index", 2);

      let zoom = d3
        .zoom()
        .scaleExtent([0.1, 5])
        .on("zoom", zoomed);
      this.zoomMap = zoom;

      let _this = this;
      function zoomed () {
        let transform = d3.event.transform;
        g.attr("transform", transform);
       // 这里做个时延提高性能
        clearTimeout(_this.timer);
        _this.timer = setTimeout(() => {
          if (_this.imagesData.length === 0) {
            _this.appendCircle(k); // 添加circle 和path的方法
            _this.setZoomCenter(_this.Data, transform.k); // 设置mark并定位的方法
          }
        }, 300);
      }

     function zoomInit(width, height) {
        return d3.zoomIdentity
          .translate(width / 2, height / 2)
          .scale(k);
     }

于是在vue的内存变量中可以得到zoom的方法,是个d3地图的回调function,通过操作zoom.transform即可操作图层,即

g.call(this.zoomMap.transform, d3.zoomIdentity.translate(width, height).scale(k));

但是这里又遇到了一个坑,图层是搜索完平移了,但是再进行别的操作的时候,图层又回归到初始状态,我上d3的官网上查看,发现是zoomed方法里d3.event.tranform在作祟,而且无法直接修改这个值!

379c047b312526bf7c01968beb2fba4b.png

由于直接修改这个值行不通,我到stackoverflow上看到有人说svg会缓存d3初始化时的所有状态,于是我想到一个点子,不仅把g的transform改了,也把svg的zoom信息也给初始化!核心代码如下:

setZoomCenter(val, k) {
  // svgwidth 和height是当前屏幕宽高,k是缩放系数。这样可以适配任何端
  let svg = d3.select('#xxxx');
        let g = svg.selectAll("g");
        let cx = val[0].x;
        let cy = val[0].y;
        // cx,cy是焦点的坐标,这里得出一个方程用来算出zoom的平移距离,因为初始焦点是当前屏幕的横纵坐标的一半,所以根据 m = svgWidth - k * cx, n = svgHeight - k * cy两个方程可以计算出平移距离
        let width = this.svgWidth / 2 - k * cx;
        let height = this.svgHeight / 2 - k * cy;
        g.call(map.transform, d3.zoomIdentity.translate(width, height).scale(k));
        svg.call(map.transform, d3.zoomIdentity.translate(width, height).scale(k));
}

这里不仅重置了g,还重置了父元素的transform, 结合到业务中,元素可以在不同的地图缩放等级中进行焦点定位,也可以顺利进行别的操作,至此大功告成。

c20d6e6f549ba1032ae5da2e2645d6f2.png

34d9797b92499d8cd58d4afc24806dfe.png

参考链接:

https://mefelixwang.gitbooks.io/d3-v4-api/api/Zooming.html#zoomtransform-tostring

https://stackoverflow.com/questions/45570145/d3-v4-reset-zoom-state

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值