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

先展示一波最终效果

最终效果

在这里插入图片描述

这段时间再学习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>

力导向图代码 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);
    }
}

数据格式 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 }
]
}

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

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个简单的D3.js导向示例,演示如何添加新节点和关系连线: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>D3.js导向 - 新增节点和关系连线示例</title> <script src="https://d3js.org/d3.v6.min.js"></script> </head> <body> <svg width="800" height="600"></svg> <script> // 定义节点和关系数据 var nodes = [ { id: "node1", name: "节点1" }, { id: "node2", name: "节点2" }, { id: "node3", name: "节点3" }, { id: "node4", name: "节点4" } ]; var links = [ { source: "node1", target: "node2" }, { source: "node1", target: "node3" }, { source: "node2", target: "node4" } ]; // 创建导向对象 var simulation = d3.forceSimulation(nodes) .force("link", d3.forceLink(links).id(d => d.id)) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(400, 300)); // 创建节点和关系连线 var svg = d3.select("svg"); var link = svg.selectAll("line") .data(links) .enter().append("line") .attr("stroke", "#999") .attr("stroke-opacity", 0.6) .attr("stroke-width", d => Math.sqrt(d.value)); var node = svg.selectAll("circle") .data(nodes) .enter().append("circle") .attr("r", 10) .attr("fill", "#ccc") .call(drag(simulation)); var label = svg.selectAll("text") .data(nodes) .enter().append("text") .text(d => d.name) .attr("font-size", "12px") .attr("dx", 15) .attr("dy", 4); // 定义行为 function drag(simulation) { function dragstarted(event, d) { if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(event, d) { d.fx = event.x; d.fy = event.y; } function dragended(event, d) { if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } return d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended); } // 新增节点和关系连线 var newNode = { id: "node5", name: "节点5" }; var newLink = { source: "node4", target: "node5" }; nodes.push(newNode); links.push(newLink); node = node.data(nodes, d => d.id); node.exit().remove(); node = node.enter().append("circle") .attr("r", 10) .attr("fill", "#ccc") .call(drag(simulation)) .merge(node); label = label.data(nodes, d => d.id); label.exit().remove(); label = label.enter().append("text") .text(d => d.name) .attr("font-size", "12px") .attr("dx", 15) .attr("dy", 4) .merge(label); link = link.data(links); link.exit().remove(); link = link.enter().append("line") .attr("stroke", "#999") .attr("stroke-opacity", 0.6) .attr("stroke-width", d => Math.sqrt(d.value)) .merge(link); // 更新导向 simulation.nodes(nodes); simulation.force("link").links(links); simulation.alpha(1).restart(); </script> </body> </html> ``` 在上面的示例中,我们首先定义了一个简单的节点和关系数据,并创建了一个导向对象。然后,我们使用D3.js创建了节点和关系连线的SVG元素,并绑定了数据。 接下来,我们定义了一个行为,以便用户可以动节点。然后,我们添加了一个新节点和一个新的关系连线,并使用D3.js更新了节点和关系连线的SVG元素。 最后,我们更新了导向对象,并重新启动了导向的模拟,以确保新节点和关系连线被正确地添加到导向中。 请注意,这只是一个简单的示例,实际应用中需要根据具体需求进行更复杂的处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值