svg").remove();var width = $("#svg").width(), height = 700, duration = 500, i = 0;var tree = d3.layout.tree() _d3 v7 tree 收缩">
当前位置:   article > 正文

【d3】层级树(缩放+折叠)_d3 v7 tree 收缩

d3 v7 tree 收缩

包含代码点:

效果:

代码:

  1. //清空SVG中的内容
  2. d3.select("#svg>svg").remove();
  3. var width = $("#svg").width(),
  4. height = 700,
  5. duration = 500,
  6. i = 0;
  7. var tree = d3.layout.tree()
  8. .size([width, height])
  9. .separation(function (a, b) { //获取或设置相邻结点间的间隔(不仅限于兄弟结点)
  10. if(a.depth == 1 && b.depth == 1){
  11. return 3;
  12. }else{
  13. return (a.parent == b.parent ? 1:2);
  14. };
  15. });
  16. //定义布局方向(横向)
  17. var diagonal = d3.svg.diagonal()
  18. .projection(function(d) {
  19. return [d.y, d.x];
  20. });
  21. // 拖拽及扩大缩放
  22. var zoom = d3.behavior.zoom()
  23. .scaleExtent([.5, 3])
  24. .on('zoom', function () {
  25. svg.attr("transform", "translate(" + (d3.event.translate[0]+150) +","+ d3.event.translate[1] + ") scale(" + d3.event.scale + ")");
  26. });
  27. var svg = d3.select("#svg").append("svg")
  28. .call(zoom) //给svg绑定zoom事件
  29. .attr("width", width)
  30. .attr("height", height)
  31. .append("g")
  32. .attr("transform", "translate(150, 0)"); // 将图整体下移,以防止顶部节点被遮挡
  33. //根据数据生成nodes集合
  34. var nodes = tree.nodes(this.analyzeList);
  35. //记录现在的位置
  36. nodes.forEach(function(d){
  37. d.x0 = d.x;
  38. d.y0 = d.y;
  39. });
  40. //获取node集合的关系集合
  41. var links = tree.links(nodes);
  42. links.forEach(function (d) {
  43. d.target.y = d.target.y+100;
  44. if(d.source.depth==4 && d.source.children && d.source.children.length>0){ //携带附件
  45. d.target.y = d.target.y+200;
  46. };
  47. });
  48. //为关系集合设置贝塞尔曲线连接
  49. var link = svg.selectAll(".link")
  50. .data(links)
  51. .enter()
  52. .append("path")
  53. .attr("class", "link")
  54. .attr("d", function(d) {
  55. let x0 = d.source.x,
  56. y0 = d.source.y,
  57. x1 = d.target.x,
  58. y1 = d.target.y- 55,
  59. r = y1 - y0;
  60. return `M ${y0},${x0} C ${y0 + r / 2},${x0} ${y0 + r / 2},${x1} ${y1},${x1}`;
  61. });
  62. var node = svg.selectAll(".node")
  63. .data(nodes)
  64. .enter()
  65. .append("g")
  66. .attr("class", "node")
  67. .attr("transform", function (d) {
  68. return "translate(" + (d.y-50) + "," + d.x + ")";
  69. })
  70. .on("click", nodeClick);
  71. // 绘制矩形与文字
  72. node.append("rect")
  73. .attr('width', function(d){
  74. let width = 0;
  75. if(d.depth==0 || d.depth==1){
  76. width = 100;
  77. }else if(d.children && d.children.length>0){
  78. width = 15;
  79. angular.forEach(d.name, function (te) {
  80. if(/[^A-Za-z0-9]/.test(te)){
  81. width += 12;
  82. }else{
  83. width += 6;
  84. };
  85. });
  86. };
  87. return (width>100)?100:width;
  88. })
  89. .attr('height', function(d){
  90. return d.depth==0 || d.depth==1?'40':'14';
  91. })
  92. .attr('y', function(d){
  93. return d.depth==0 || d.depth==1?'-20':'-8';
  94. })
  95. .attr('x', function(d){
  96. return d.depth==0 || d.depth==1?'-30':'0';
  97. })
  98. .attr('ry', 5)
  99. .attr('rx', 5)
  100. .attr('fill', function(d){
  101. let fill = '';
  102. if(d.depth==0){
  103. fill = '#e04a4a';
  104. }else if(d.depth==1){
  105. fill = '#c79329';
  106. }else{
  107. fill = 'white';
  108. };
  109. return fill;
  110. })
  111. .attr('stroke', function(d){
  112. let stroke = '';
  113. if(d.depth==0){
  114. stroke = 'red';
  115. }else if(d.depth==1){
  116. stroke = '#a57107';
  117. }else{
  118. stroke = 'white';
  119. };
  120. return stroke;
  121. })
  122. .attr('strokeWidth', '2px');
  123. node.append("circle")
  124. .attr("r", 4.5)
  125. .attr('fill', function(d){
  126. let fill = '';
  127. if(d.depth==0 || d.depth==1){
  128. fill = 'none';
  129. }else if(d.click){
  130. fill = $rootScope.theme.chartColor || '#0073BA';
  131. }else{
  132. fill = '#faaa65';
  133. };
  134. return fill;
  135. })
  136. .attr('stroke', function(d){
  137. let stroke = '';
  138. if(d.depth==0 || d.depth==1){
  139. stroke = 'none';
  140. }else if(d.click){
  141. stroke = $rootScope.theme.chartColor || '#0073BA';
  142. }else{
  143. stroke = '#faaa65';
  144. };
  145. return stroke;
  146. })
  147. .attr('strokeWidth', function(d){
  148. return d.depth==0 || d.depth==1?'0':'2px';
  149. });
  150. node.append("text")
  151. .attr('y', function(d){
  152. return d.depth==0 || d.depth==1?'5':'4';
  153. })
  154. .attr("x", function (d) {
  155. return d.depth==0 || d.depth==1?'20':'10';
  156. })
  157. .attr("text-anchor", function(d){
  158. return d.depth==0 || d.depth==1?'middle':'start';
  159. })
  160. .attr('fill', function(d){
  161. return d.depth==0 || d.depth==1?'white':'';
  162. })
  163. .text(function (d) {
  164. let text = "";
  165. if(d.children && d.name.length>7){
  166. text = d.name.slice(0,7)+'...';
  167. }else{
  168. text = d.name+(d.key?":":'');
  169. };
  170. if(d.click && (d.name == d.key)){
  171. text = "";
  172. };
  173. if(d.parent && d.parent.name == "hash值"){
  174. text = d.name.slice(0,10) + "...";
  175. };
  176. if(d.name == "不同样本Dropper相同文件"){
  177. text = d.name.slice(0,7) + "...";
  178. };
  179. if(d.children){
  180. text += ' ['+d.children.length+']';
  181. };
  182. return text;
  183. })
  184. .append("a")
  185. .attr("class", function (d) {
  186. return d.click?"click":"noclick";
  187. })
  188. .attr("fill", function(d){
  189. return d.click?($rootScope.theme.chartColor || '#0073BA'):'';
  190. })
  191. .text(function (d) {
  192. return d.key;
  193. })
  194. .on("click", function (d) {
  195. d3.event.stopPropagation();
  196. if(d.type == 'ip' || d.type == 'domain'){
  197. $scope.sideBox.query(d3.event, d.key);
  198. };
  199. });
  200. node.append("title")
  201. .text(function (d) {
  202. let title = d.name;
  203. if(d.allName) title = d.allName;
  204. if(d.key && d.key != d.name) title = d.name+":"+d.key;
  205. return title;
  206. });
  207. // 节点点击,隐藏或显示子节点
  208. function nodeClick(d){
  209. if(d.children || d._children){
  210. if (d.children){
  211. d._children = d.children;
  212. d.children = null;
  213. }else{
  214. d.children = d._children;
  215. d._children = null;
  216. };
  217. update(d);
  218. };
  219. };
  220. //更新布局
  221. function update(source) {
  222. //计算新树的布局
  223. var nodes = tree.nodes(self.analyzeList).reverse(),
  224. links = tree.links(nodes);
  225. //树的深度这里树d.y。树的宽度最大720,要分四层,所以每层就乘180
  226. nodes.forEach(function(d) {
  227. d.y = d.depth * 180;// 树的x,y倒置了,所以这里Y其实是横向的
  228. });
  229. links.forEach(function (d) {
  230. d.target.y = d.target.y+100;
  231. if(d.source.depth==4 && d.source.children && d.source.children.length>0){ //携带附件
  232. d.target.y = d.target.y+200;
  233. };
  234. });
  235. //数据连接,根据id绑定数据
  236. var node = svg.selectAll("g.node")
  237. .data(nodes, function(d) {
  238. return d.id //最初新点开的节点都没有id
  239. || (d.id = ++i); //为没有id的节点添加上ID
  240. });
  241. //点击时增加新的子节点
  242. var nodeEnter = node.enter().append("g")
  243. .attr("class", "node")
  244. .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
  245. .on("click", nodeClick);
  246. // 绘制矩形与文字
  247. nodeEnter.append("rect")
  248. .attr('width', function(d){
  249. let width = 0;
  250. if(d.depth==0 || d.depth==1){
  251. width = 100;
  252. }else if(d.children && d.children.length>0){
  253. width = 15;
  254. angular.forEach(d.name, function (te) {
  255. if(/[^A-Za-z0-9]/.test(te)){
  256. width += 12;
  257. }else{
  258. width += 6;
  259. };
  260. });
  261. };
  262. return (width>100)?100:width;
  263. })
  264. .attr('height', function(d){
  265. return d.depth==0 || d.depth==1?'40':'14';
  266. })
  267. .attr('y', function(d){
  268. return d.depth==0 || d.depth==1?'-20':'-8';
  269. })
  270. .attr('x', function(d){
  271. return d.depth==0 || d.depth==1?'-30':'0';
  272. })
  273. .attr('ry', 5)
  274. .attr('rx', 5)
  275. .attr('fill', function(d){
  276. let fill = '';
  277. if(d.depth==0){
  278. fill = '#e04a4a';
  279. }else if(d.depth==1){
  280. fill = '#c79329';
  281. }else{
  282. fill = 'white';
  283. };
  284. return fill;
  285. })
  286. .attr('stroke', function(d){
  287. let stroke = '';
  288. if(d.depth==0){
  289. stroke = 'red';
  290. }else if(d.depth==1){
  291. stroke = '#a57107';
  292. }else{
  293. stroke = 'white';
  294. };
  295. return stroke;
  296. })
  297. .attr('strokeWidth', '2px');
  298. nodeEnter.append("circle")
  299. .attr("r", 4.5)
  300. .attr('fill', function(d){
  301. let fill = '';
  302. if(d.depth==0 || d.depth==1){
  303. fill = 'none';
  304. }else if(d.click){
  305. fill = $rootScope.theme.chartColor || '#0073BA';
  306. }else{
  307. fill = '#faaa65';
  308. };
  309. return fill;
  310. })
  311. .attr('stroke', function(d){
  312. let stroke = '';
  313. if(d.depth==0 || d.depth==1){
  314. stroke = 'none';
  315. }else if(d.click){
  316. stroke = $rootScope.theme.chartColor || '#0073BA';
  317. }else{
  318. stroke = '#faaa65';
  319. };
  320. return stroke;
  321. })
  322. .attr('strokeWidth', function(d){
  323. return d.depth==0 || d.depth==1?'0':'2px';
  324. });
  325. nodeEnter.append("text")
  326. .attr('y', function(d){
  327. return d.depth==0 || d.depth==1?'5':'4';
  328. })
  329. .attr("x", function (d) {
  330. return d.depth==0 || d.depth==1?'20':'10';
  331. })
  332. .attr("text-anchor", function(d){
  333. return d.depth==0 || d.depth==1?'middle':'start';
  334. })
  335. .attr('fill', function(d){
  336. return d.depth==0 || d.depth==1?'white':'';
  337. })
  338. .text(function (d) {
  339. let text = "";
  340. if(d.children && d.name.length>7){
  341. text = d.name.slice(0,7)+'...';
  342. }else{
  343. text = d.name+(d.key?":":'');
  344. };
  345. if(d.click && (d.name == d.key)){
  346. text = "";
  347. };
  348. if(d.parent && d.parent.name == "hash值"){
  349. text = d.name.slice(0,10) + "...";
  350. };
  351. if(d.name == "不同样本Dropper相同文件"){
  352. text = d.name.slice(0,7) + "...";
  353. };
  354. if(d.children){
  355. text += ' ['+d.children.length+']';
  356. };
  357. return text;
  358. })
  359. .append("a")
  360. .attr("class", function (d) {
  361. return d.click?"click":"noclick";
  362. })
  363. .attr("fill", function(d){
  364. return d.click?($rootScope.theme.chartColor || '#0073BA'):'';
  365. })
  366. .text(function (d) {
  367. return d.key;
  368. })
  369. .on("click", function (d) {
  370. d3.event.stopPropagation();
  371. if(d.type == 'ip' || d.type == 'domain'){
  372. $scope.sideBox.query(d3.event, d.key);
  373. };
  374. });
  375. nodeEnter.append("title")
  376. .text(function (d) {
  377. let title = d.name;
  378. if(d.allName) title = d.allName;
  379. if(d.key && d.key != d.name) title = d.name+":"+d.key;
  380. return title;
  381. });
  382. //原有节点更新到新位置
  383. var nodeUpdate = node.transition()
  384. .duration(duration)
  385. .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
  386. nodeUpdate.select("text")
  387. .style("fill-opacity", 1);
  388. //折叠节点的子节点收缩回来
  389. var nodeExit = node.exit().transition()
  390. .duration(duration)
  391. .attr("transform", function(d) {
  392. return "translate(" + source.y + "," + source.x + ")";
  393. })
  394. .remove();
  395. nodeExit.select("circle")
  396. .attr("r", 1e-6);
  397. nodeExit.select("text")
  398. .style("fill-opacity", 1e-6);
  399. //数据连接,根据目标节点的id绑定数据
  400. var link = svg.selectAll("path.link")
  401. .data(links, function(d) { return d.target.id; });
  402. //增加新连接
  403. link.enter().insert("path", "g")
  404. .attr("class", "link")
  405. .attr("d", function(d) {
  406. var o = {x: source.x0, y: source.y0};
  407. return diagonal({source: o, target: o});
  408. });
  409. //原有连接更新位置
  410. link.transition()
  411. .duration(duration)
  412. .attr("d", diagonal);
  413. //折叠的链接,收缩到源节点处
  414. link.exit().transition()
  415. .duration(duration)
  416. .attr("d", function(d) {
  417. var o = {x: source.x, y: source.y};
  418. return diagonal({source: o, target: o});
  419. })
  420. .remove();
  421. // 把旧位置存下来,用以过渡
  422. nodes.forEach(function(d) {
  423. d.x0 = d.x;
  424. d.y0 = d.y;
  425. });
  426. };

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/132759
推荐阅读
相关标签
  

闽ICP备14008679号