d3.js学习笔记vue2(7)插入单元

有一组线条,通过一个三层的数组传递数据,第一层是线条,第二层是点,第三层是x、y坐标值,根据之前的

d3.js学习笔记(6)嵌套式select

绘制这个图形是很容易的。但是新的需求是:上游数据更新,在中间插入了一条新的线,为了实现这个需求,遇到了一些弯路。

第一种错误方法:继续使用data()绑定,试图让d3自动更新,简单来说就是最初绘制这条线时使用的代码,在数据points更新后,再次用同样的代码来划线

linesObject.node.selectAll('path')
  .data(points)
  .enter()
  .append('path')
  .attr('d', (d) => linesObject.generator(d))
  .attr('linenum', (d, i) => i)
  .attr('stroke', linesObject.color)
  .attr('stroke-width', '2px')
  .attr('fill', 'none')
  .exit().remove()

这里涉及到d3的一个机制,就是enter().append()的机制,它会查询points的length,然后根据这个length补足append()的元素,而且对于那些已经有了d属性的<path>单元,d3不会更新其d属性。也就是说,即使上游的points数据已经更新,但是d3不会再次更新已经存在的<path>里面的d属性,也就是说,浏览器里的图形不会更新。最后的结果是:因为points增加了,所以最后会append()一些<path>,并且把points里面最后的一些数据写到后面追加的<path>里,呈现到浏览器上的图形就是排列在最后的几条线被叠加绘制,如果不是通过浏览器的调试工具去查看dom节点的话,在视觉上和更新之前没有差异。

第二种正确方法:既然d3对已经存在的<path>不做更新,那么干脆把这些<path>全删掉,然后重新绘制就好了

linesObject.node.selectAll('*').remove()
linesObject.node.selectAll('path')
  .data(points)
  .enter()
  .append('path')
  .attr('d', (d) => linesObject.generator(d))
  .attr('linenum', (d, i) => i)
  .attr('stroke', linesObject.color)
  .attr('stroke-width', '2px')
  .attr('fill', 'none')
  .exit().remove()

事实证明,这种方法达到了效果。

第三种正确方法:利用d3.insert(type[, before])方法

该方法会在before这个位置插入一个type,并把原来位于before这个位置的以及更靠后的节点,全部向后推

什么?你问我如果一定要在最后插入怎么办?用append()

https://d3js.org.cn/api/d3-selection/

上面的链接有insert方法的介绍,其中要注意的是

1)type仅接受两种类型的实参:一是字符串,d3会直接插入一个tag为type的标签;二是函数,这个函数和d3.js学习笔记(4)匿名函数类似,接受3个输入参数,但是区别在于返回值必须是一个dom节点。因为这里我们要插入的一个新的图形,而不是简单的文字,所以需要通过函数返回一个dom节点。

如果要绘制图形,不可避免要使用d3.select()然后绑定数据和生成器才能生成,不过d3.selection()返回值中没有dom节点的属性,所以必须要多做一步。

How to access the DOM element that correlates to a D3 SVG object? - Stack Overflow

只要在d3.select()后面加一个node()就可以返回dom节点了

d3.select()要选择一个父节点才能用append()或者insert()生成节点,而为了避免导致问题,所以最好是直接生成一个新的dom节点,这个dom节点直接就是<path>标签,然后d3.select(newDOM).attr('d', (d) => linesObject.generator(d))

在这里有一点要特别留意,如果用document.createElement(),创建的是一个html节点,在svg中不会被正确处理,所以必须要使用

document.createElementNS('http://www.w3.org/2000/svg', 'path')

把这个节点放置到svg命名空间中,svg中才能正确的绘制图形,具体请参考MDN

Document.createElementNS() - Web API 接口参考 | MDN

2)before仅接受两种类型的实参:一是字符串,该字符串必须是一个css选择器,实际上可能会有多个子节点满足该选择器,但是d3只会在第一个满足要求的子节点处插入,后面的就不会管了,如果你需要在所有满足该选择器的元素处插入节点或者做什么操作的话,需要使用each()

javascript - d3.js - how to insert new sibling elements - Stack Overflow

另外一种可接受的实参是一个函数,而且这个函数和d3.js学习笔记(4)匿名函数类似,接受3个输入参数,区别在于返回值必须是一个dom节点(不可以是d3.select()的返回值),而且必须是d3.selection().node()的子节点

对于我实际面临的问题来讲,因为这个程序需要记录线条的编号,这个编号在后面做用户交互要用,所以必须要在所有的<path>节点加上自定义的linenum属性用来记录,所以如果插入线条,则还需要对后面所有被推后一位的元素的linenum属性做处理,出于程序完整性的考虑,我就直接在before函数里做了一个遍历,把所有要做的工作处理之后,再把对应的插入位置返回。这样就不怕调用insert之后忘记处理linenum的事情

linesObject.node.insert(() => {
  return d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'path'))
    .datum(points)
    .attr('d', (d) => linesObject.generator(d))
    .attr('linenum', position)
    .attr('stroke-width', '2px')
    .attr('fill', 'none')
    .node()
  }, (d, i, a) => {
    let n
    for (let k = 0; k < a[i].childNodes.length; k++) {
      let t = Number.parseInt(a[i].childNodes[k].getAttribute('linenum'))
      if (t === position) {
        a[i].childNodes[k].setAttribute('linenum', t + 1)
        n = k
      }
      if (t > position) {
        a[i].childNodes[k].setAttribute('linenum', t + 1)
      }
    }
    return a[i].childNodes[n]
  })

选哪一种方法?

第二种正确方法是先删除节点,再重新生成,如果节点数量非常大,则计算量会大。但是如果采用insert方法,只需要处理一小部分数据就可以了。不管怎么样浏览器需要reflow,所以这里还是推荐采用insert方法

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
D3.js 是一个强大的 JavaScript 数据可视化库,而 Vue 是一个流行的 JavaScript 前端框架。它们可以很好地结合使用,以创建交互式和动态的数据可视化。 在将 D3.js 结合 Vue 使用时,有几种常见的方法可以实现: 1. 使用 Vue 的生命周期钩子函数:可以在 Vue 组件的 `mounted` 或 `updated` 钩子函数中初始化和更新 D3.js 可视化。这样,在组件渲染完成后或数据更新后,可以调用 D3.js 的相关方法来绘制、更新或销毁可视化。 2. 创建自定义 Vue 指令:可以编写一个自定义指令,用于在 DOM 元素上直接调用 D3.js 的方法。这样,可以在模板中使用指令来绑定数据和配置,将 D3.js 的功能封装到指令中,使代码更具可维护性和复用性。 3. 使用第三方插件或库:有一些第三方插件或库可以帮助更好地结合 D3.jsVue。例如,`vue-d3` 是一个专门为 D3.jsVue 设计的插件,提供了一些方便的组件和指令,简化了 D3.jsVue 的集成过程。 无论选择哪种方法,重要的是要确保良好的数据流和交互机制。使用 Vue 的响应式数据和事件系统,可以实现数据的双向绑定、动态更新和交互操作,以提供更丰富的用户体验。 需要注意的是,D3.jsVue 在处理 DOM 和数据方面有不同的思维方式和方法,因此在结合使用时,需要理解和熟悉两者的工作原理,并根据具体需求进行适当的调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值