在网上有很多教程讲如何用drag拖拽操作图形元素的,但是他们都没说如何操作数据。

因为我要写的东西是一个通过在图形界面通过拖拽调整几何形状的程序,所以特别注意了这一点。

首先在zoom里已经有了拖拽功能了,可以让整个场景平移,但是原始数据不变。

而我需要在特定单元上添加拖拽功能,具体来说就是拖拽线条上的标记点,改变线条的数据。

所以必须要让特定的拖拽覆盖zoom的拖拽,也就是说在程序执行时必须先执行zoom,后执行特定的drag

drag的定义。要特别说明,在以前的版本中,需要用d3.behavior.drag(),但是现在的新版把d3打碎成很多的小模块,引用时可以按需引用,可以减少生成的文件的体积,减少网络传输的负担,现在v5可以直接用d3.drag()

this.dragHandler = d3.drag().on('start', (d, i, a) => this.dragStart(d, i, a)).on('drag', (d, i, a) => this.dragDrag(d, i, a)).on('end', (d, i, a) => this.dragEnd(d, i, a))
this.dragHandler(this.symbols.node.selectAll('path')) // this.symbols.node.selectAll('path').call(this.dragHandler)

先定义一个d3.drag(),然后给start/drag/end分别赋予不同的响应函数

比较常见的start是改变拖拽元素的颜色,然后end再改回来,这样用户可以清楚地看到拖拽了哪个元素。如果在拖拽完成后有存储或者其他需求,可以添加。很少见到start需要做特殊处理的情况。

dragStart (d, i, a) {d3.select(a[i]).attr('stroke', '#00FFFF')
},
dragEnd (d, i, a) {d3.select(a[i]).attr('stroke', '#FF0000')
}

在这个函数中需要找到拖拽的元素,本来d3提供了下面的方法来找

d3.select(this)

vue对this有特殊定义,所以在vue组件中这种方法不能用了,只能另辟蹊径

这个匿名函数有3个形参,其中

d=q[i] // 每个元素对应的数据
i // 数据的索引
q // 数据的父节点

直接用匿名函数的第2个和第3个元素来选取

let target = d3.select(a[i])

就可以直接找到拖拽的元素。

拖动的函数是最复杂的

dragDrag (d, i, a) {let target = d3.select(a[i])let [linenum, pointnum] = [target.attr('linenum'), target.attr('pointnum')]this.$store.state.lineControlPoints[linenum][pointnum][0] = this.axises.xScale.invert(d3.event.x)this.$store.state.lineControlPoints[linenum][pointnum][1] = this.axises.yScale.invert(d3.event.y)this.$store.commit('lineUpdate')this.polylines.node.selectAll('path').data(this.$store.state.lineControlPoints).attr('d', (d) => this.polylines.lineGenerator(d))this.splines.node.selectAll('path').data(this.$store.state.linePoints).attr('d', (d) => this.splines.lineGenerator(d))target.attr('transform', `translate(${d3.event.x},${d3.event.y})`)},

想在drag同时对图形进行实时更新来进行数据操作就要在dragDrag函数里添加数据操作 。

因为我为了操作方便,所以在当初生成这个元素时添加了linenum和pointnum属性,这里直接用attr取出来就可以了。

let [linenum, pointnum] = [target.attr('linenum'), target.attr('pointnum')]

这里的lineControlpoints和linePoints可以不是一致的,比如lineControlPoints是控制点,linePoints是样条线或者Bezier曲线,需要一些运算,而且这些数据还和程序中其他部分关联,所以使用vuex。因为是拖拽过程中实时更新,所以采用了同步的commit,而不是异步的dispatch

this.$store.state.lineControlPoints[linenum][pointnum][0] = this.axises.xScale.invert(d3.event.x)
this.$store.state.lineControlPoints[linenum][pointnum][1] = this.axises.yScale.invert(d3.event.y)
this.$store.commit('lineUpdate')

数据处理完成以后,从lineControlPoints得到了linePoints

要实时呈现出来,就要设定线条的data,还要处理d属性,因为svg中的元素归根结底是通过d属性来呈现的,仅仅处理到data是不会刷新图形的

这个案例用的是用d3.line()处理的,此处的处理方法和设定线条数据的方法一样

this.polylines.node.selectAll('path').data(this.$store.state.impeller.stage.lineControlPoints).attr('d', (d) => this.polylines.lineGenerator(d))
this.splines.node.selectAll('path').data(this.$store.state.impeller.stage.linePoints).attr('d', (d) => this.splines.lineGenerator(d))

如果vuex中存储的数据和这里要呈现的数据之间还有较多运算,也可以引入其他函数

因为目标点是一个symbol,而且symbol的数据也是直接关联的vuex数据,所以不做过多处理,而是直接移动symbol的位置即可

target.attr('transform', `translate(${d3.event.x},${d3.event.y})`)

这样,在拖动的同时,标记点随鼠标移动,vuex中的数据随时更新,svg中呈现的线条图形也跟着更新

这里要特别提示,这里移动的是symbol,而symbol默认的初始位置是原点,也就是svg的左上角。所以这里translate直接到鼠标位置。如果是其他元素,需要根据元素自身的实际情况来设定。

对于大多数元素,可以设定drag的origin,然后

dragHandler.origin(target.x, target.y)
target.attr('transform', `translate(${d3.event.dx},${d3.event.dy})`)

一定要设定origin,不仅仅是因为这样有助于计算鼠标的实际移动距离,而且在鼠标刚刚点中目标元素时,目标元素不会突然跳动使得目标元素的定位点和鼠标位置重合。比如点击饼图的单元时,饼图单元的中心位置在半径均值、角相位均值的扇形中心位置,如果鼠标点击到饼图扇形的其他位置,饼图会突然跳动使得中心位置跳动到鼠标位置,这样的跳动会改变操作者的预期,使得操作不方便。

如果拖拽的目标不仅仅是改变图形呈现的位置,还要改变图形切实的定位,那么

target.attr('x', d3.event.x).attr('y', d3.event.y)

这里使用的是x、y属性,涉及到具体的图形元素,可能有不同的属性,比如圆形就是cx、cy,可以根据实际情况调整

如果绑定是简单的数据其实也可以用

d3.select(this).data()[0]=xxx

来直接操作绑定的数据,但是这样不是很灵活。比如说我这里的点数据是3维数组,第0维是线条编号。这样我在把数据绑定到标记点的时候就要做一次转化。而且处理完节点以后,还要处理Bezier曲线,这需要较复杂的函数。还不如直接处理原始数据,然后重新给图形赋值。

该示例代码是vue单文件组件中的代码,所以this指的是上下文组件,this里的数据是在data里定义的

d3.js学习笔记(5)drag拖拽操作数据相关推荐

  1. 第一篇博客 赶鸭子上架的d3.js学习笔记(bilibili从零开始画图表学习笔记 感谢万能的b站以及up睿小狼d)

    svg: 可缩放矢量图形是基于可扩展标记语言(标准通用标记语言的子集),用于描述二维矢量图形的一种图片格式.他由万维网联盟制定,是一个开放标准.(源代码类似于html) 一个简单的代码 <svg ...

  2. d3.js学习笔记--Mike Bostock: Thinking with Joins

    为什么80%的码农都做不了架构师?>>>    我们可以使用append方法, 来创建一个单一元素: var svg = d3.select('svg'),d = {"x& ...

  3. d3.js学习笔记②搭建服务器(含报错解决)

    强烈建议自己搭建一个服务器,否则在后续往js里导入本地数据(比如csv.json文件等)的时候会报错.我用的是Apache服务器,下载.安装过程参考这篇文章:Apache安装配置 在浏览器输入http ...

  4. html矢量图 对画布的拖拽,d3.js画矢量图+可拖拽的实现思路

    箭头.png testtt.html Arrow var width = 400; var height = 400; var svg = d3.select("body").ap ...

  5. Vue.js学习笔记 2022 04 17

    Vue.js学习笔记 1 Vue.js基础 Vue.js介绍 vuejs 渐进式 JavaScript 框架 Vue.js 如何应用 1 在普通html项目中,引入VUE.JS Hb 的项目 生成的 ...

  6. JS实现漂亮的窗口拖拽效果(可改变大小、最大化、最小化、关闭)

    转自<JS实现漂亮的窗口拖拽效果(可改变大小.最大化.最小化.关闭)>:http://www.jb51.net/article/73157.htm 这篇文章主要介绍了JS实现漂亮的窗口拖拽 ...

  7. html实现鼠标拖拽按钮,JS实现鼠标按下拖拽效果

    原生js实现鼠标按下拖拽效果,供大家参考,具体内容如下 鼠标拖动 body { margin: 0; } div { width: 200px; height: 200px; position: ab ...

  8. ArcGIS JS 学习笔记4 实现地图联动

    原文:ArcGIS JS 学习笔记4 实现地图联动 1.开篇 守望屁股实在太好玩了,所以最近有点懒,这次就先写个简单的来凑一下数.这次我的模仿目标是天地图的地图联动. 天地的地图联动不仅地图有联动,而 ...

  9. backbone.js学习笔记

    backbone.js学习笔记 之前只接触过jQuery,看来Backbone是除了jQuery的第二大JS框架... backbone到底是个啥? 其实刚开始我也不知道=_=,我是这周二才听说居然还 ...

  10. node.js学习笔记

    # node.js学习笔记标签(空格分隔): node.js---## 一 内置模块学习 ### 1. http 模块 ``` //1 导入http模块 const http =require('ht ...

最新文章

  1. Nature子刊:你想成为生物信息学家?
  2. boost::hana::fold_right用法的测试程序
  3. OpenCV_04 几何变换:图像缩放+图像平移+图像旋转+仿射变换+透射变换+图像金字塔
  4. python open r w r+ w+ a的区别
  5. html颜色转换rgba,RGB颜色在线转换
  6. What is Dymola?---Dymola的特点和架构
  7. Android自带语音播报TextToSpeech功能开发记录
  8. 利用gsm模块自动收发短信
  9. 雷军:《我十年的程序员生涯》系列之一:我的程序人生路
  10. MFA 中文国际音标
  11. python摇骰子游戏小案例
  12. MATLAB 基础与通信系统仿真
  13. JS验证电话和传真号码格式
  14. 『统计学』常用的数据分析方法都在这了!Part.2
  15. 原子操作(Atomic)
  16. VS中使用Qt方法详解
  17. 互联网大鱼吃小鱼背后:十亿消费者的推崇
  18. ECshop4.1版本搭建
  19. html如何实现表情加文字发布,vue实现文字表情同时输入的方法
  20. 工程监测多通道振弦模拟信号采集仪VTN数据查看

热门文章

  1. curl 命令的使用
  2. HUAWEI 机试题:黑板上的数涂颜色
  3. 2021.08.29_Android_各系统间换行符不同引起的无法执行shell脚本,提示no such file or directory
  4. “全栈这个概念坑害了多少开发者
  5. 电脑微信语音通话听不到对方声音或无法播放消息语音
  6. 智能家居研究:深耕用户体验实现核心业务17.3%增长
  7. Java动态代理的理解
  8. 数据库空间存储已满,如何清理释放空间
  9. oracle lead() over,Oracle数据库之lead over 和 lag over
  10. 塔望·食品品牌营销|如何把私域流量做成私域“留量”