【问题标题】:D3 force-directed graph( v4) is not accepting img as nodesD3 力导向图(v4)不接受 img 作为节点
【发布时间】:2018-05-20 16:09:05
【问题描述】:

我正在尝试生成一个力有向图。如果我使用 'circle/rect' 来绘制节点,我能够实现它。但我想改用图像。我做错了什么?

这是我创建和转换节点的方式(我使用的是 d3 v4):

  var node = svg.append("g")
  .attr("class", "nodes f32")
  .selectAll("img")
  .data(json.nodes)
  .enter().append("img")
  .attr("class","flag ar")
  .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));


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
    .style("left", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
    .style("top", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
}

这是我目前所拥有的演示:

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

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
    .force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
    .force("center", d3.forceCenter(width / 2, height / 2));

d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
  json.nodes.forEach(function(d,i){
    d.id = i;
  })
  
  var link = svg.append("g")
      .attr("class", "links")
    .selectAll("line")
    .data(json.links)
    .enter().append("line")
  .attr("stroke-width","1");
  
    var node = svg.append("g")
      .attr("class", "nodes f32")
    .selectAll("img")
    .data(json.nodes)
    .enter().append("img")
      .attr("class","flag ar")
      .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));


  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
    
        .style("left", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); })
        .style("top", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); });
  }

  simulation
      .nodes(json.nodes)
      .on("tick", ticked);
  
  simulation.force("link")
      .links(json.links);
});

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;
}
.links line {
    stroke: #999;
    stroke-opacity: 0.6;
}
h1{
  font-family: arial;
}
body{
  display:flex;
  justify-content:center;
  align-items:center;
      flex-direction: column;
    background:#d64d4d;
}
.fdd{
 width:1000px;
  height:500px;
  background: white;
  
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>Force Directed Graph of State Contiguity</h1>
<div class="fdd">
  <svg width="1000" height="500"></svg>
</div>

【问题讨论】:

    标签: d3.js svg


    【解决方案1】:

    要包含图像元素,您应该使用.append("image") 而不是.append("img")

    另外,图片本身需要用xlink:href属性来指定。例如,您可以提供指向图像的链接。

    这 2 点结合起来得到以下 sn-p:

    .append("image")
      .attr("class","flag ar")
      .attr("xlink:href", "https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico")
    

    另外,节点的位置好像是off;你可以这样设置它们的位置(通过翻译它们):

    node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
    

    这是一个演示:

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        radius=5;
    
    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
        .force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
        .force("center", d3.forceCenter(width / 2, height / 2));
    
    d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
      json.nodes.forEach(function(d,i){
        d.id = i;
      })
      
      var link = svg.append("g")
          .attr("class", "links")
        .selectAll("line")
        .data(json.links)
        .enter().append("line")
      .attr("stroke-width","1");
      
        var node = svg.append("g")
          .attr("class", "nodes f32")
        .selectAll("image")
        .data(json.nodes)
        .enter().append("image")
          .attr("class","flag ar")
          .attr("xlink:href", "https://cdn.sstatic.net/Sites/stackoverflow/img/favicon.ico")
          .attr("height", "32") // width/height (are necessary in Firefox to make the image appear)
          .attr("width", "32")
          .call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended));
    
    
      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; });
    
        // -16 is half the width/height of the image I used:
        node
          .attr("transform", function(d) { return "translate(" + (d.x - 16) + "," + (d.y - 16) + ")"; })
      }
    
      simulation
          .nodes(json.nodes)
          .on("tick", ticked);
      
      simulation.force("link")
          .links(json.links);
    });
    
    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;
    }
    .links line {
        stroke: #999;
        stroke-opacity: 0.6;
    }
    h1{
      font-family: arial;
    }
    body{
      display:flex;
      justify-content:center;
      align-items:center;
          flex-direction: column;
        background:#d64d4d;
    }
    .fdd{
     width:1000px;
      height:500px;
      background: white;
      
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    
    <h1>Force Directed Graph of State Contiguity</h1>
    <div class="fdd">
      <svg width="1000" height="500"></svg>
    </div>

    【讨论】:

    • 太好了,非常感谢。我通过css背景设置源。我怎样才能让它工作?
    • 哼,有可能。我宁愿坚持使用 d3 进行图像的整个设置,如果图像因节点而异,则更是如此。
    【解决方案2】:

    我对 img 和 svg:image 感到困惑。

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        radius=5;
    var graph = d3.select(".fdd")
        .append("div")
        .style("width", width + "px")
        .style("height", height + "px")
        .attr('class',"countries f16")
    
    
    var node = graph
    .selectAll("img")
    .data(json.nodes)
    .enter().append("img")
      .attr("class",function(d){return "flag " +d.code} )
      .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended))
    
      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
            .style("top", function(d) { return d.y + "px"; } )
            .style("left", function(d) { return d.x + "px"; });
      }
    

    在 svg 中包含 img 不起作用。

    完整的解决方案可以在这里找到:

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        radius=5;
      var graph = d3.select(".fdd")
        .append("div")
        .style("width", width + "px")
        .style("height", height + "px")
        .attr('class',"countries f16")
    
    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function(d,index) { return d.id; }).distance(10))
        .force("charge", d3.forceManyBody().distanceMin(10).distanceMax(120))
        .force("center", d3.forceCenter(width / 2, height / 2));
    
    d3.json("https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json",function(json ) {
      json.nodes.forEach(function(d,i){
        d.id = i;
      })
      
      var link = svg.append("g")
          .attr("class", "links")
        .selectAll("line")
        .data(json.links)
        .enter().append("line")
      .attr("stroke-width","1");
      
        var node = graph
        .selectAll("img")
        .data(json.nodes)
        .enter().append("img")
          .attr("class",function(d){return "flag " +d.code} )
          .call(d3.drag()
              .on("start", dragstarted)
              .on("drag", dragged)
              .on("end", dragended))
              .on("mouseover", function(data,a){
                  return tooltip
                  .style("visibility", "visible")
                  .attr('class', 'd3-tip')
                    .html("<div class='data'>"+data.country+"</div>")
                .style("top", (event.pageY-10)+"px").style("left",(event.pageX+10)+"px");})
              .on("mouseout", function(){ 
                        return tooltip.style("visibility", "hidden");});
    
        var tooltip = d3.select("body")
          .append("div")
          .style("position", "absolute")
          .style("z-index", "100000")
          .style("visibility", "hidden")
          .text("a simple tooltip");     
    
      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
            .style("top", function(d) { return d.y + "px"; } )
            .style("left", function(d) { return d.x + "px"; });
    
      }
    
      simulation
          .nodes(json.nodes)
          .on("tick", ticked);
      
      simulation.force("link")
          .links(json.links);
    });
    
    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;
    }
    .links line {
        stroke: #999;
        stroke-opacity: 0.6;
    }
    svg{
      position:absolute;
    }
    .countries{
          width: 100%;
        height:100%;
        position: relative;
        margin: 0 auto;
    }
    h1{
      font-family: arial;
    }
    body{
      display:flex;
      justify-content:center;
      align-items:center;
          flex-direction: column;
        background:#d64d4d;
    }
    .fdd{
     width:1000px;
      height:500px;
      background: white;
      
    }
    .flag{
     position:absolute; 
     border-radius: 50%;
     border: 0;
     transform: translate(-8px,-8px);
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <link href="https://github.com/downloads/lafeber/world-flags-sprite/flags16.css" rel="stylesheet"/>
    <h1>Force Directed Graph of State Contiguity</h1>
    <div class="fdd">
      <svg width="1000" height="500"></svg>
    </div>

    【讨论】:

      猜你喜欢
      • 2017-11-12
      • 1970-01-01
      • 2015-07-30
      • 2019-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-25
      • 1970-01-01
      相关资源
      最近更新 更多