#d3.js实现力导向图圈选框选
今天给大家带来的是如何在2D可视化图形中加入通过鼠标拖动圈选功能,以力导向图为例。
##最终效果

demo跳转

##代码解析
我们是要在节点的上方绘制一个矩形覆盖节点的视觉效果,但是为了和原来的节点拖动不冲突,就需要对事件的target做判断。
当鼠标在空白区域时才能圈选。还不明白的话,返回电脑桌面试一下拖动鼠标。

###首先还是先画一个力导向图

数据

var nodes = [{value: "66666666",type: "home",index: "0"
},
{value: "11111111111",type: "phone",index: "1"
},
{value: "22222222222",type: "phone",index: "2"
},
{value: "33333333333",type: "phone",index: "3"
},
{value: "44444444444",type: "phone",index: "4"
},
{value: "55555555555",type: "phone",index: "5"
},
{value: "aaa",type: "weixin",index: "6"
},
{value: "bbb",type: "weixin",index: "7"
},
{value: "ccc",type: "weixin",index: "8"
},
{value: "ddd",type: "weixin",index: "9"
},
{value: "eee",type: "weixin",index: "10"
},
{value: "fff",type: "weixin",index: "11"
},
];
var links = [{source: 0,target: 1
},
{source: 0,target: 2
},
{source: 0,target: 3
},
{source: 0,target: 4
},
{source: 0,target: 5
},
{source: 2,target: 6
},
{source: 2,target: 7
},
{source: 2,target: 8
},
{source: 3,target: 9
},
{source: 3,target: 10
},
{source: 3,target: 11
},
]

###绘制力导向图

var svg = d3.select("#forceMap").append("svg").attr("width", width).attr("height", height).attr("id", "forceSvg");
var mapG = svg.append("g").attr("id", "forceGroup");var force = d3.layout.force().nodes(nodes).links(links).size([width, height]).linkDistance(100).charge([ - 1250]).gravity(0.5).friction(0.5);
force.start();
var linkG = mapG.selectAll(".link").data(links).enter().append("line").attr("class", "link").attr("stroke", "#ccc");
var nodeG = mapG.selectAll(".node").data(nodes).enter().append("circle").attr("class", "node unselected")//加入新的class,给选中和未选中做判断.attr("r", 8).attr("fill", function(d) {switch (d.type) {case "home":return "red";break;case "phone":return "blue";break;case "weixin":return "green";break;}
}).call(force.drag);force.on("tick", function() {linkG.attr("x1", function(d) {return d.source.x;}).attr("y1", function(d) {return d.source.y;}).attr("x2", function(d) {return d.target.x;}).attr("y2", function(d) {return d.target.y;});nodeG.attr("cx", function(d) {return d.x}).attr("cy", function(d) {return d.y});
});

这里和以前是有区别的,就是在绘制node时加入了新的class=“unselected”,这里规定选中的node为selected,未选中为unselected。
两种样式在css里实现。

.unselected{opacity:0.3
}
.selected{opacity:1
}

###圈选功能

首先确定思路,通过获取鼠标按下时的坐标位置和最后鼠标左键抬起时的结束位置来确定圈选框的大小和绘制方式。
值得注意的是不同的拖动方向(左上,右上,左下,右下),绘制的方法有区别。
需要判断点击时是否鼠标在空白区域,防止和节点的拖动冲突。
做时间标记区别短暂点击和拖动。

var clickTime = "";
var startLoc = [];
var endLoc = [];
var flag = "";
function drawSquare() {var rect = svg.append("rect").attr("width", 0).attr("height", 0).attr("fill", "rgba(33,20,50,0.3)").attr("stroke", "#ccc").attr("stroke-width", "2px").attr("transform", "translate(0,0)").attr("id", "squareSelect");svg.on("mousedown", function() {clickTime = (new Date()).getTime();//mark start timeflag = true;//以flag作为可执行圈选的标记rect.attr("transform", "translate(" + d3.event.layerX + "," + d3.event.layerY + ")");startLoc = [d3.event.layerX, d3.event.layerY];});svg.on("mousemove", function() {//判断事件targetif (d3.event.target.localName == "svg" && flag == true || d3.event.target.localName == "rect" && flag == true) {var width = d3.event.layerX - startLoc[0];var height = d3.event.layerY - startLoc[1];if (width < 0) {rect.attr("transform", "translate(" + d3.event.layerX + "," + startLoc[1] + ")");}if (height < 0) {rect.attr("transform", "translate(" + startLoc[0] + "," + d3.event.layerY + ")");}if (height < 0 && width < 0) {rect.attr("transform", "translate(" + d3.event.layerX + "," + d3.event.layerY + ")");}rect.attr("width", Math.abs(width)).attr("height", Math.abs(height))}})svg.on("mouseup", function(){if(flag == true){flag = false;endLoc = [d3.event.layerX, d3.event.layerY];var leftTop = [];var rightBottom = []if(endLoc[0]>=startLoc[0]){leftTop[0] = startLoc[0];rightBottom[0] = endLoc[0];}else{leftTop[0] = endLoc[0];rightBottom[0] = startLoc[0];}if(endLoc[1]>=startLoc[1]){leftTop[1] = startLoc[1];rightBottom[1] = endLoc[1];}else{leftTop[1] = endLoc[1];rightBottom[1] = startLoc[1];}//最后通过和node的坐标比较,确定哪些点在圈选范围var nodes = d3.selectAll(".node").attr("temp", function(d){if(d.x<rightBottom[0] && d.x>leftTop[0] && d.y>leftTop[1] && d.y<rightBottom[1]){d3.select(this).attr("class","node selected");}})rect.attr("width",0).attr("height",0);}var times = (new Date()).getTime()-clickTime;if (times<100 && d3.event.target.id !== "squareSelect") {var nodes = d3.selectAll(".node").attr("class", "node unselected")}})}
drawSquare();

到这里就完成了一个简单的圈选功能。
##总结
完成了圈选之后,在项目中,我们通常需要拿到圈选到的元素的属性显示出来或者保存,这些就很简单了,只需要遍历有selected的元素即可。
这里的方法不仅适用于力导向图,也同样适用于相似的树状图等。
如果你的图带缩放(Zoom),那就会复杂一点,因为圈选方法里面的对范围内节点的计算和scale有关系,需要把scale和translate加入计算。

但在有坐标系的柱状图和折线图这类图中实现圈选,这里建议使用v4中的brush方法,今后的文章中会详细介绍。

d3.js实现力导向图圈选框选相关推荐

  1. 使用d3.js开发力导向图

    最近项目需要写一个d3的力导向图,之前没接触过d3.js 所以吧这次开发的经历写一下 文章目录 分配点与线 创建dom 线 点 绘制线 友情提示:不要让设计设计的华丽呼哨,点多了很卡,而且svg 有些 ...

  2. D3.js实现力导向图(Dray和Zoom)

    今天遇到个问题就是把json(里面nodes和edges属性)文件通过D3.js展示出来,下午终于弄出来了,写篇博客记录一下. 先展示一下效果图: 放大后是这个效果: 什么是力导向图 D3.js官网: ...

  3. 关于用d3.js画力导向图(Force-directed Graph)—如何让图收放自如

    使用d3画出很好看的力图,可以展示那种Neo4j知识图谱的效果.但是当一满屏都是小圆圈时又有点丧失了即看即懂的效果.因此需要实现对节点的收放. 主要是对生成力图的数据点集进行操作. 其中在收回某个节点 ...

  4. D3.js的v5版本入门教程(第十四章)—— 力导向图

    D3.js的v5版本入门教程(第十四章) 这一章我们来绘制一个力导向图,什么叫力导向图,通俗一点将就是有节点和线组成,当鼠标拖拽一个节点时,其他节点都会受到影响(力导向图有多种类型,本章绘制的效果就是 ...

  5. D3.js 力导向图来处理拓扑图

    记录一点碰到的问题和解决方案.感觉国内关于D3.js 4.0版本的相关资料还是少. 力导向图布局 D3一种布局的方式,可以将你nodes links的节点数据转换成可以绘制的坐标点数据,然后通过svg ...

  6. d3力导向图增加节点_d3.js力导向图节点如何都显示在边框内

    最近用到d3.js中的force力导向图,想实现效果如下,所有城市节点都在可视范围内,如果超出有滚动条也可以. 遇到的问题是,当节点一多,有的节点就会跑到外面去,这边是通过加大charge相互作用力, ...

  7. D3.js 力导向图的显示优化

    D3.js 作为一个前端,说到可视化除了听过 D3.js 的大名,常见的可视化库还有 ECharts.Chart.js,这两个库功能也很强大,但是有一个共同特点是封装层次高,留给开发者可设计和控制的部 ...

  8. 利用D3.js快速绘制力导向图

    碎碎念: 最近课题需要基于图论和力导向图可视化每个脑电通道之间的连接性,MATLAB画的效果差强人意,于是上网搜寻到JavaScript 的一个函数库--D3.js,想快速得到数据的可视化结果 简介D ...

  9. d3 svg path添加文本_D3.js 力导向图的显示优化

    D3.js 作为一个前端,说到可视化除了听过 D3.js 的大名,常见的可视化库还有 ECharts.Chart.js,这两个库功能也很强大,但是有一个共同特点是封装层次高,留给开发者可设计和控制的部 ...

最新文章

  1. 与人相处时不越界,能让关系更和谐
  2. 采购定价过程字段解析
  3. 【Hibernate】Hibernate实体关系映射——双边的一对一关系
  4. vijos p1433——火炬手之梦
  5. MYSQL数据库安装记
  6. verilog 学习记(如何编写assert)
  7. Javascript设计模式之单例模式
  8. A*B Problem II
  9. 如何导入以前的qq聊天记录
  10. 中国最好大学网爬取大学排名信息
  11. 刻度如果数据比较大的情况下会溢出
  12. java说的tps pv是什么_你知道服务器PV、TPS、QPS是怎么计算出来的吗?
  13. 多模块项目-项目复制出现Module xx must not contain source root xx The root already belongs to module xx
  14. 解析少儿编程中的运用的科技语言
  15. Problem4: Tweet Tweet
  16. VR全景的拍摄与制作
  17. Proteus中MSP430与SHT11的IIC通信问题
  18. (声纳+惯性+视觉)Sonar Visual Inertial SLAM of Underwater Structures
  19. vue组件相关 传值 调用
  20. Docker 制作 MySQL 镜像并使用 `/docker-entrypoint-initdb.d/` 机制初始化数据

热门文章

  1. (ubuntu)YOLOv5报错:RuntimeError: CUDA error: no kernel image is available for execution on the ...
  2. Windows 11 个人使用体验小结
  3. 判断是否有统计意义/差异具有显著性/具有显著差异/零假设(希望证明为错误的假设)/卡方检验
  4. c语言错误1004,Excel VBA运行时错误1004仅以名称以'c'开头
  5. 浅谈模式 - 装饰者模式
  6. DirectX 11 Tutorial 4 中文翻译版教程: 缓存区、着色器和HLSL
  7. 中国举办的世界园艺博览会概览-web数据可视化(d3.js path)
  8. 平均负载(load average)
  9. 那些有意思的linux命令
  10. js获取IP地址多种方法实例教程