如何将特定d3节点作为力导向图中的图像?

时间:2016-11-29 02:26:03

标签: javascript node.js d3.js svg

我制作了一个类似于此的力导向图。note this is a random example from the internet

我希望中间的Nine Inch Nails节点是一个图像,但其余节点只是圆圈。在answer之后,这似乎并不太难,但我无法理解svg,defs,pattern和d3的组合。

我的代码是:

      var simulation =
      d3.forceSimulation()
      .force("charge", d3.forceManyBody().strength(-50))
      .force("collide", d3.forceCollide().radius(function (d) { return 15 - d.group}).strength(2).iterations(2))
      .force("link", d3.forceLink().id(function(d, i) { return i;}).distance(20).strength(0.9))
      .force("center", d3.forceCenter(width/2, height/2))
      .force('X', d3.forceX(width/2).strength(0.15)) 
      .force('Y', d3.forceY(height/2).strength(0.15));

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")

    var defs = svg.append('svg:defs');

    defs.append("svg:pattern")
          .attr("id", "vit-icon")
          .attr("width", 48)
          .attr("height", 48)
          .attr("patternUnits", "userSpaceOnUse")
          .append("svg:image")
          .attr("xlink:href", "http://placekitten.com/g/48/48")
          .attr("width", 48)
          .attr("height", 48)
          .attr("x", width/2)
          .attr("y", height/2)

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
      .attr("id", function(d, i) { return 'c'+i})
      .attr("r", radius)
      .attr("fill", function(d) {
            if(d.group==0) {return "url(#vit-icon)";}
            else {return color(d.group); }
          })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

正如我所说,在我看来似乎是直截了当的。基本上我认为我试图做的是在svg模式中使用图像,然后使用if语句告诉我的根节点使用图像URL而不是填充颜色。

在开发工具中,我可以看到检查图像并显示它将占用的蓝色区域以及我希望它与之关联的节点具有' url(#vit-icon)'作为它的填充属性。但它没有显示该节点的图像或任何填充。

我做错了什么?或者这是完全错误的做法? 谢谢。

1 个答案:

答案 0 :(得分:1)

defs中,只需更改:

.attr("x", width/2)
.attr("y", height/2)

要:

.attr("x", 0)
.attr("y", 0);

这是一个演示:

var nodes = [{
    "id": 1,
}, {
    "id": 2,
}, {
    "id": 3,
}, {
    "id": 4,
}, {
    "id": 5,
}, {
    "id": 6,
}, {
    "id": 7,
}, {
    "id": 8,
}];

var links = [{
    source: 1,
    target: 2
}, {
    source: 1,
    target: 3
}, {
    source: 1,
    target: 4
}, {
    source: 2,
    target: 5
}, {
    source: 2,
    target: 6
}, {
    source: 1,
    target: 7
}, {
    source: 7,
    target: 8
}];

var index = 10;
var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    node,
    link;

var defs = svg.append('svg:defs');

defs.append("svg:pattern")
    .attr("id", "vit-icon")
    .attr("width", 1)
    .attr("height", 1)
    .append("svg:image")
    .attr("xlink:href", "http://66.media.tumblr.com/avatar_1c725152c551_128.png")
    .attr("width", 48)
    .attr("height", 48)
    .attr("x", 0)
    .attr("y", 0);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) {
        return d.id;
    }).distance(100))
    .force("collide", d3.forceCollide(50))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(width / 2, height / 2));

link = svg.selectAll(".link")
    .data(links, function(d) {
        return d.target.id;
    })

link = link.enter()
    .append("line")
    .attr("class", "link");

node = svg.selectAll(".node")
    .data(nodes, function(d) {
        return d.id;
    })

node = node.enter()
    .append("g")
    .attr("class", "node")
    .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

node.append("circle")
    .attr("r", d=> d.id === 1 ? 24 : 14)
    .style("fill", function(d) {
        if (d.id === 1) {
            return "url(#vit-icon)";
        } else {
            return "teal"
        }
    })

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

simulation.force("link")
    .links(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
        .attr("transform", function(d) {
            return "translate(" + d.x + ", " + d.y + ")";
        });
}

function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart()
}

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 = undefined;
    d.fy = undefined;
}
.link {
  stroke: #aaa;
}

.node {
  pointer-events: all;
  stroke: none;
  stroke-width: 40px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500" height="300"></svg>