D3强制布局缩放和平移:单击节点也平移

时间:2014-01-24 17:29:31

标签: d3.js zoom force-layout pan

我在下面的d3.js强制布局代码主要适用。它使用锚标签,以便标签在拥挤的图形中更加明显。我还找到了一个平移和缩放示例并实现了它。平移和缩放大多数都有效,但是当点击一个节点时它会平移并移动节点,这意味着它不会真正移动节点。我只是想让它移动节点而不是在点击节点时平移。我创建了一个jsfiddle here

代码:

var links = [ 
    { source: "a", target: "b", indicator_type: "x"},
    { source: "a", target: "c", indicator_type: "x"},
    { source: "e", target: "d", indicator_type: "x"},
    { source: "e", target: "d", indicator_type: "x"},
    { source: "e", target: "b", indicator_type: "y"},
    { source: "e", target: "c", indicator_type: "y"},
];

var w = 850,
    h = 500;

var fill = d3.scale.category20();

var labelDistance = 0;

var vis = d3.select("#relationship_graph")
    .append("svg:svg")
    .attr("width", w)
    .attr("height", h)
    .attr("pointer-events", "all")
    .append('svg:g')
    .call(d3.behavior.zoom().on("zoom", redraw))
    .append('svg:g');

vis.append("rect")
    .attr("width", w)
    .attr("height", h)
    .attr("opacity", 0);

function redraw() {
    vis.attr("transform",
        "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}

var nodes = {};
var labelAnchors = [];
var labelAnchorLinks = [];

// Compute the distinct nodes from the links.
links.forEach(function (link) {
    link.source = nodes[link.source] || (nodes[link.source] = {
        label: link.source,
        type: "notice",
        indicator_type: "notice"
    });
    link.target = nodes[link.target] || (nodes[link.target] = {
        label: link.target,
        type: "indicator",
        indicator_type: link.indicator_type
    });
    link.weight = 1;
});

for (var node in nodes) {

    var a = labelAnchors.push({
        node: nodes[node]
    });
    var b = labelAnchors.push({
        node: nodes[node]
    });
    labelAnchorLinks.push({
        source: (a - 1),
        target: (b - 1),
        weight: 1
    });
};

var force = d3.layout.force()
    .size([w,h])
    .nodes(d3.values(nodes))
    .links(links)
    .gravity(1)
    .linkDistance(60)
    .charge(-3000)
    .linkStrength(function (x) {
            return x.weight * 5
    });

force.start();

var force2 = d3.layout.force()
    .nodes(labelAnchors)
    .links(labelAnchorLinks)
    .gravity(0)
    .linkDistance(0)
    .linkStrength(8)
    .charge(-300)
    .size([w, h]);

force2.start();

var link = vis.selectAll("line.link")
    .data(links)
    .enter()
    .append("svg:line")
    .attr("class", "link")
    .style("stroke", "#CCC");

var node = vis.selectAll("g.node")
    .data(force.nodes())
    .enter()
    .append("svg:g")
    .attr("class", "node");

node.append("svg:circle")
    .attr("r", function (d) {
        return d.type == "notice" ? 10 : 7
    })
    .style({
        fill: function (d) {
            return fill(d.indicator_type)
        },
        stroke: "#CCC",
    });

node.call(force.drag);

var anchorLink = vis.selectAll("line.anchorLink")
    .data(labelAnchorLinks)
    //.enter().append("svg:line").attr("class", "anchorLink").style("stroke", "#999");

var anchorNode = vis.selectAll("g.anchorNode")
    .data(force2.nodes())
    .enter()
    .append("svg:g")
    .attr("class", "anchorNode");

anchorNode.append("svg:circle").attr("r", 0).style("fill", "#FFF");
anchorNode.append("svg:text").text(function (d, i) {
    return i % 2 == 0 ? "" : d.node.label;
})
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 10);

var updateLink = function () {
    this.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;
    });
}

var updateNode = function () {
    this.attr({
        "transform": function (d) {
            return "translate(" + d.x + "," + d.y + ")";
        }
    });
}

force.on("tick", function () {

    force2.start();

    node.call(updateNode);

    anchorNode.each(function (d, i) {
        if (i % 2 == 0) {
            d.x = d.node.x;
            d.y = d.node.y;
        } else {
            var b = this.childNodes[1].getBBox();

            var diffX = d.x - d.node.x;
            var diffY = d.y - d.node.y;

            var dist = Math.sqrt(diffX * diffX + diffY * diffY);

            var shiftX = b.width * (diffX - dist) / (dist * 2);
            shiftX = Math.max(-b.width, Math.min(0, shiftX));
            var shiftY = 5;
            this.childNodes[1]
                .setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")");
        }
    });

    anchorNode.call(updateNode);

    link.call(updateLink);
    anchorLink.call(updateLink);

});

0 个答案:

没有答案