当前位置:   article > 正文

数据可视化 D3 力导向图鼠标右击菜单的制作及缩放(zoom)和拖拽(drag)的应用_3d force-directed graph

3d force-directed graph

先展示一波最终效果

最终效果

在这里插入图片描述

这段时间再学习D3,力导向图

力导向图(Force-Directed Graph),是绘图的一种算法。在二维或三维空间里配置节点,节点之间用线连接,称为连线。各连线的长度几乎相等,且尽可能不相交。节点和连线都被施加了力的作用,力是根据节点和连线的相对位置计算的。根据力的作用,来计算节点和连线的运动轨迹,并不断降低它们的能量,最终达到一种能量很低的安定状态。

力导向图能表示节点之间的多对多的关系。

定义一个力导向图的布局关键方法 force()
d3在v3和v4版本上对于 force() 这个方法变动很大

话不多说,代码如下:(注意:这次使用的D3.js版本是V3的)

           代码不是最终代码,但是功能基本已经实现,后续会更新这一版的最终代码

页面代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>The END</title>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
  <script src="./js/smartMenu.js"></script>
  <script src="./fun.js"></script>
  <link rel="stylesheet" href="./css/smartMenu.css">
</head>
<body>
  <div id="tpContainer">

  </div>   
 
</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

力导向图代码 fun.js

//获取数据
d3.json('./preson.json', function(error,data){
  if (error) {
      return console.log(error);
    }
  // console.log(data);
  drawToPofun(data.nodes,data.lines);
});

function drawToPofun(nodes,edges){
  
  //定义初始变量
  var width  = 1503;
  var height = 654;
  var img_w = 40;
  var img_h = 40;
  var text_dx = -20;
  var text_dy = 20;
  var i = 0;

  //初始化拖拽方法
  var drag = d3.behavior.drag()
    .origin(function(d) {
      return d;
    })
    .on("dragstart", dragstarted)
    .on("drag", dragged)
    .on("dragend", dragended);

  //初始化缩放方法
  var zoom=d3.behavior.zoom()
    .scaleExtent([-10,10]).on("zoom",zoomed);

  //定义画布
  var svg = d3.select("#tpContainer")
  .append("svg")
  .attr("width",width)
  .attr("height",height)
  .call(zoom)
  .on("dblclick.zoom", () => {});//禁止双击放大
  
  //拆分子节点
  var view = svg.append("g")
  .attr("class","graphCon");
  
  var node = view.selectAll("g.node").data(nodes,function (d) {
    return d.id || (d.id = ++i);
  });

  var nodeEnter = node
  .enter()
  .append("g")
  .attr("class","node")
  .attr("id",d=>d.id)//添加节点,以自定义图标代替
  var nodes_img = nodeEnter.append("image")
  .data(nodes)
  .attr("width",function(d){
      return img_w;
    })
  .attr("height",function(d){
      return img_h;
    })
  .attr("xlink:href",function(d){
    return d.image;
    })
  .call(drag)//开始拖拽方法
  
  //添加连线
  var edges_line = nodeEnter.append("line")
  .data(edges)
  .attr("class","line")
  .style("stroke","#ccc")
  .style("stroke-width",1);

  //添加节点说明(文本)
  var nodes_text = nodeEnter.append("text")
  .data(nodes)
  .attr("class","nodetext")
  .attr("dx",text_dx)
  .attr("dy",text_dy)
  .text(function(d){
    return d.name;
  });

  //定义力学图布局
   var force  = d3.layout.force().nodes(nodes)
    .links(edges)
    .size([width,height])
    .linkDistance(200)
    .charge(-800)
    .start()
    .on("tick", function(){
      
      //限制结点的边界
      nodes.forEach(function(d,i){
        d.x = d.x - img_w/2 < 0? img_w/2 : d.x ;
        d.x = d.x + img_w/2 > width ? width - img_w/2 : 
        d.x ; 
        d.y = d.y - img_h/2 < 0? img_h/2 : d.y ;
        d.y = d.y + img_h/2 + text_dy > height ? 
        height - img_h/2 - text_dy : d.y ;
      });

      //更新连接线的位置
      edges_line.attr("x1",function(d){ return d.source.x; });
      edges_line.attr("y1",function(d){ return d.source.y; });
      edges_line.attr("x2",function(d){ return d.target.x; });
      edges_line.attr("y2",function(d){ return d.target.y; });
      //更新结点图片和文字
      nodes_img.attr("x",function(d){
        return d.x - img_w/2; 
      });
      nodes_img.attr("y",function(d){ 
        return d.y - img_h/2; 
      });                  
      nodes_text.attr("x",function(d){ return d.x; });
      nodes_text.attr("y",function(d){ 
        return d.y + img_h/2; 
      });
    }); 

    // 定义菜单选项
    var userMenuData = [
      [{
          text: "菜单1",
          func: function () {
            var id = Number($(this).attr("id"))
            alert("菜单1"+",No."+ id)
          }
        },
        {
          text: "菜单2",
          func: function () {
            var id = Number($(this).attr("id"))
            alert("菜单2"+",No."+ id)
          }
        },
        {
          text: "菜单3",
          func: function () {
            var id = Number($(this).attr("id"))
            alert("菜单3"+",No."+ id)
          }
        } 
      ]
    ];
    // 事件监听方式添加事件绑定
    $("body").smartMenu(userMenuData, {
      name: "chatRightControl",
      container: "g.node"
    });
    
    function zoomed(){
      view.attr("transform",
      "translate("+d3.event.translate+")scale(" +
      d3.event.scale + ")");
    }
    function dragstarted(d) {
      d3.event.sourceEvent.stopPropagation();
      d3.select(this).classed("dragging", true);
      force.start();
    }
    function dragged(d) {
      d.x = d3.event.x;
      d.y = d3.event.y;
    }
    function dragended(d) {
      d3.select(this).classed("dragging", false);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172

数据格式 preson.json

{
  "nodes": [
    {"name": "preson1", "image": "./person.png"}, 
    {"name": "preson2", "image": "./person.png"}, 
    {"name": "preson3", "image": "./person.png"}, 
    {"name": "preson4", "image": "./person.png"}, 
    {"name": "preson5", "image": "./person.png"}, 
    {"name": "preson6", "image": "./person.png"},
    {"name": "preson7", "image": "./person.png"},
    {"name": "preson8", "image": "./person.png"},
    {"name": "preson9", "image": "./person.png"},
    {"name": "preson10","image": "./person.png"}
],
"lines": [
  { "source": 0 , "target": 1 },
  { "source": 0 , "target": 2 },
  { "source": 0 , "target": 3 },
  { "source": 1 , "target": 4 },
  { "source": 1 , "target": 5 },
  { "source": 1 , "target": 6 },
  { "source": 0 , "target": 7 },
  { "source": 7 , "target": 8 },
  { "source": 7 , "target": 9 }
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

菜单我用的是jQuery插件,下载下来的,js/css代码就不展示了。

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

闽ICP备14008679号