强制网络与 d3 匹配边界框

Force network to fit bounding box with d3

我正在尝试将我的网络放入 div。我使用了这个例子:https://bl.ocks.org/puzzler10/2531c035e8d514f125c4d15433f79d74 但在使用 d3.json()

时无法让它工作

这是我正在使用的代码。

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>

html, body {
  height: 100%;
  margin: 0;
}

.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

text {
  font-family: sans-serif;
  font-size: 10px;
  color: black;
}

.sidebar {
  height: 100%;
  width: 0;
  margin-top:10%;
  margin-bottom:1.5%;
  margin-left:1.5%;
  position: absolute;
  z-index: 2000;
  top: 0;
  left: 0;
  bottom:0;
  background-color:rgba(240,240,240,0.6);
  overflow-x: hidden;
  transition: 0.5s;
  border-radius:5px;
  overflow: hidden;
}
    
.sidebar:hover {
  overflow-y: scroll;
}

svg{
  border: 1px solid black;
}

.container {
  display: flex;   
  justify-content: center
};


</style>

<div class="container">
<svg width="500" height="500"></svg>
</div>


<div id="mySidebar" class="sidebar">
  <p id="area"></p>
  <p id="desc"></p>
  <div>
    <img id="media" src="">
  </div>
  <p id="content"></p>
</div>


<script src="https://d3js.org/d3.v4.min.js"></script>
<script>


//Navbar navigation
function openNav() {
  document.getElementById("mySidebar").style.width = "350px";
}

function closeNav() {
  document.getElementById("mySidebar").style.width = "0";
}

//Graph options
var radius = 15;

var svg = d3.select("div#container")
  .append("svg")
  .attr("preserveAspectRatio", "xMinYMin meet")
  .attr("viewBox", "0 0 300 300")
  .classed("svg-content", true);

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
                    .force("link", d3.forceLink().id(function(d) { return d.id; }))
                    .force("charge", d3.forceManyBody().strength(-2000))
                    .force("center", d3.forceCenter(width / 2, height / 2));


//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
  if (error) throw error;
  var link = svg.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.append("g")
                .attr("class", "nodes")
                .selectAll("g")
                .data(graph.nodes)
                .enter().append("g")

  var circles = node.append("circle")
    .attr("r", 5)
    .attr("fill", function(d) { return color(d.group); });

  // Create a drag handler and append it to the node object instead
  var drag_handler = d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended);

  drag_handler(node);
  
  var labels = node.append("text")
      .text(function(d) {
        return d.id;
      })
      .attr('x', 6)
      .attr('y', 3);

  link.on("click", 
    function(d) {
      console.log(d.content);
      openNav();
      document.getElementById("content").innerHTML = d.content;
    },
  )

  node.append("title")
      .text(function(d) { return d.id; });

  simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(graph.links);

  function ticked() {
    link
        .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; });

    node //This is where the "problem" seems to be located
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        })

  }
});

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

</script>

可以将网络约束到边界框的函数如下:

function ticked() {
   link
       .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; });

   node //This is where the "problem" seems to be located
       .attr("transform", function(d) {
         return "translate(" + d.x + "," + d.y + ")";
       })

 }

我已经尝试了很多东西,但无法让它发挥作用。有什么想法吗?

您共享的块中将节点保持在边界内的部分是

node
    .attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
    .attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });

注意对 Math.maxMath.min 的调用,以确保 d.xd.y 保持在边界内。您的代码缺少此内容。

你可以做类似的事情:

node
    .attr("transform", function(d) {
      d.x = Math.max(radius, Math.min(width - radius, d.x));
      d.y = Math.max(radius, Math.min(height - radius, d.y));
      return "translate(" + d.x + "," + d.y + ")";
    });

这是一个更新的例子:

<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<style>

html, body {
  height: 100%;
  margin: 0;
}

.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

text {
  font-family: sans-serif;
  font-size: 10px;
  color: black;
}

.sidebar {
  height: 100%;
  width: 0;
  margin-top:10%;
  margin-bottom:1.5%;
  margin-left:1.5%;
  position: absolute;
  z-index: 2000;
  top: 0;
  left: 0;
  bottom:0;
  background-color:rgba(240,240,240,0.6);
  overflow-x: hidden;
  transition: 0.5s;
  border-radius:5px;
  overflow: hidden;
}
    
.sidebar:hover {
  overflow-y: scroll;
}

svg{
  border: 1px solid black;
}

.container {
  display: flex;   
  justify-content: center
};


</style>

<div class="container">
<svg width="500" height="500"></svg>
</div>


<div id="mySidebar" class="sidebar">
  <p id="area"></p>
  <p id="desc"></p>
  <div>
    <img id="media" src="">
  </div>
  <p id="content"></p>
</div>


<script src="https://d3js.org/d3.v4.min.js"></script>
<script>


//Navbar navigation
function openNav() {
  document.getElementById("mySidebar").style.width = "350px";
}

function closeNav() {
  document.getElementById("mySidebar").style.width = "0";
}

//Graph options
var radius = 15;

var svg = d3.select("div#container")
  .append("svg")
  .attr("preserveAspectRatio", "xMinYMin meet")
  .attr("viewBox", "0 0 300 300")
  .classed("svg-content", true);

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
                    .force("link", d3.forceLink().id(function(d) { return d.id; }))
                    .force("charge", d3.forceManyBody().strength(-2000))
                    .force("center", d3.forceCenter(width / 2, height / 2));


//Graph creation
d3.json("http://myjson.dit.upm.es/api/bins/10fx", function(error, graph) {
  if (error) throw error;
  var link = svg.append("g")
      .attr("class", "links")
      .selectAll("line")
      .data(graph.links)
      .enter().append("line")
      .attr("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.append("g")
                .attr("class", "nodes")
                .selectAll("g")
                .data(graph.nodes)
                .enter().append("g")

  var circles = node.append("circle")
    .attr("r", 5)
    .attr("fill", function(d) { return color(d.group); });

  // Create a drag handler and append it to the node object instead
  var drag_handler = d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended);

  drag_handler(node);
  
  var labels = node.append("text")
      .text(function(d) {
        return d.id;
      })
      .attr('x', 6)
      .attr('y', 3);

  link.on("click", 
    function(d) {
      console.log(d.content);
      openNav();
      document.getElementById("content").innerHTML = d.content;
    },
  )

  node.append("title")
      .text(function(d) { return d.id; });

  simulation
      .nodes(graph.nodes)
      .on("tick", ticked);

  simulation.force("link")
      .links(graph.links);

  function ticked() {
    node
        .attr("transform", function(d) {
          d.x = Math.max(radius, Math.min(width - radius, d.x));
          d.y = Math.max(radius, Math.min(height - radius, d.y));
          return "translate(" + d.x + "," + d.y + ")";
        });
  
    link
        .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; });
  }
});

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

</script>