D3JS 中强制定向图像和标签布局中的不同图像大小

Different Image Sizes in Force-Directed Images and Labels Layout in d3js

本文关键字:图像 布局 D3JS 标签      更新时间:2023-09-26

我有一个简单的问题,我无法解决。所以我在 d3js 中实现了力导向布局,如下所示。但是,现在我想包含在图表中的图像具有不同的大小。所以我写了一个函数 imagesize 来确定这些图像的尺寸。但是,问题是在布局的第一个开始时,图像不会显示。如果我重新加载它,那么它可以完美运行。我想问题是加载功能,但我不知道我还能如何获得尺寸。

你可以在这里找到jsfiddle。所以我写的你可以在第 62 - 67 行找到。

.attr("width", function(d) { 
    return imagesize("https://github.com/favicon.ico")[0];
})
.attr("height", function(d) { 
    return imagesize("https://github.com/favicon.ico")[1];
});

和图像大小

function imagesize(link) {
    var img = new Image();
    img.src = link;
    return [img.width, img.width];
}

谢谢。

编辑

宽度和高度存储在每个节点的基准中,然后在绘制直线时使用它。 请参阅下面的代码更新:


您没有处理映像的异步加载。 此外,您可以使用.each来简化这一点:

appended.append("image")
  .attr("xlink:href", "https://github.com/favicon.ico")
  .attr("x", "-8px")
  .attr("y", "-8px")
  .each(imagesize); //<-- call this for each image
function imagesize(d) {
  var self = d3.select(this); //<-- this is the svg image
  function loaded() { //<-- when your img is finished loading set width/height
    d.width = img.width; //<-- stash width/height in each node
    d.height = img.height;
    self.attr('width', d.width);
    self.attr('height', d.height);
  }
  var img = new Image();
  img.src = self.attr('href'); //<-- you can get this from the svg image
  if (img.complete) {
    loaded(); //<-- is to already done
  } else {
    img.addEventListener('load', loaded); //<-- callback after loading
    img.addEventListener('error', function() {
        alert('error');
    })
  }
}

绘制线条时:

force.on("tick", function() {
  link.attr("x1", function(d) { 
    // draw line with width of node
    // since width is set async, protect against null values
    return d.source.x + (d.source.width ? d.source.width/2 : 0); 
  })

完整代码:

var width = 300, height = 300;
  var radius = 5;
	var svg = d3.select('#main')
  	.append("svg")
    .attr("height", height)
    .attr("width", width);
  
  var nodes = [], links = [];
  
  var force = d3.layout.force()
  	.charge(-350)
    .linkDistance(50)
    .nodes(nodes)
    .links(links)
  	.size([height, width]);
    
	var node = svg.selectAll(".node"),
      link = svg.selectAll('.link');
      
  var drag = force.drag().on("dragstart", dragstart);
  
  nodes.push({id: 0, name:"zero"});
  nodes.push({id: 1, name:"one"});
  
  links.push({source: 0, target: 1});
  update();
  
  function update() {
  	// add reference to index of nodes
    var edges = [];
    links.forEach(function(e){
    	var sourcenode = nodes.filter(function(n) { 
    		return n.id === e.source; 
    	})[0];
    
    	var targetnode = nodes.filter(function(n) { 
    		return n.id === e.target; 
    	})[0];
    	edges.push({source: sourcenode, target: targetnode});
   	});
    
    links = edges;
    
    var link = svg.selectAll("line.link")
                    .data(links, function(d) { 
                    	return d.source.id + '-' + d.target.id; 
                    });
    link.enter().insert("line").attr("class", "link").style("stroke","black");
    link.exit().remove();
    node = node.data(force.nodes(), function(d) {return d.id;});
    node.exit().remove();
   	var appended = node.enter().append("g").call(drag);
    appended.append("image")
      .attr("xlink:href", "https://github.com/favicon.ico")
      .attr("x", "-8px")
      .attr("y", "-8px")
      .each(imagesize);
      
    appended
    	//.on("dblclick", dblclick)
      .on("contextmenu", dragend);
    appended.append("text")
			.attr("dx", "12")
      .attr("dy", ".35em")
      .text(function(d) { return d.name; });
      
		force.on("tick", function() {
    	link.attr("x1", function(d) { 
      	return d.source.x + (d.source.width ? d.source.width/2 : 0); 
      })
      .attr("y1", function(d) { 
      	return d.source.y + (d.source.height ? d.source.height/2 : 0); 
      })
      .attr("x2", function(d) { 
      	return d.target.x + (d.target.width ? d.target.width/2 : 0);
      })
      .attr("y2", function(d) { 
      	return d.target.y + (d.target.height ? d.target.height/2 : 0); 
      });
      node.attr("transform", function(d) {
      	var x = Math.max(radius, 
        	Math.min($('svg').attr('width')  - radius, d.x));
        var y = Math.max(radius, 
        	Math.min($('svg').attr('height') - radius, d.y));
        return "translate(" + d.x + "," + d.y + ")";
      });
     });
    force.start();
  }
  
  function dragstart(d) {
  	d3.select(this).classed("fixed", d.fixed = true);
  }
  function dragend(d) {
  	d3.select(this).classed("fixed", d.fixed = false);
  }
  
function imagesize(d) {
	var self = d3.select(this);
  function loaded() {
    d.width = img.width;
    d.height = img.height;
    self.attr('width', d.width);
    self.attr('height', d.height);
  }
  var img = new Image();
  img.src = self.attr('href');
  if (img.complete) {
    loaded();
  } else {
    img.addEventListener('load', loaded)
    img.addEventListener('error', function() {
        alert('error');
    })
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
  <div id="main"></div>
</body>