如何在d3中放大我的力导向图?

时间:2017-11-08 06:11:05

标签: javascript d3.js svg

当我将鼠标悬停在图形svg的任何部分上方并向上或向下滚动时,我想放大我的力导向图。我真的不确定怎么做。我能够选择svg并增加/减小svg的大小,这就像伪变焦一样,但是它改变了svg的大小和位置,这不是我正在寻找的。当我滚动svg时,如何选择节点和链接并增加它们的大小?感谢

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

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

var forceParameter = 30
    maxDistanceParameter = 100;

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().distance(forceParameter).strength(.5))
    .force("charge", d3.forceManyBody().distanceMax(maxDistanceParameter))
    .force("center", d3.forceCenter(width / 2, height / 2));

var linkMap = d3.map();

d3.json("courses.json", function(error, graph) {
    if (error) throw error;

    var nodes = graph.nodes,
        nodeById = d3.map(nodes, function(d) { return d.id; }),
        links = graph.links,
        bilinks = [];

    links.forEach(function(link) {
        var s = link.source = nodeById.get(link.source),
            t = link.target = nodeById.get(link.target),
            i = {}; // intermediate node
        nodes.push(i);
        links.push({source: s, target: i}, {source: i, target: t});
        bilinks.push([s, i, t]);
    });

    //Populate the map that keeps track of how many links a class node has
    links.forEach(function(d){
        if(linkMap.has(d.source.id))
            linkMap.set(d.source.id, linkMap.get(d.source.id) + .5);
        else
            linkMap.set(d.source.id, .5);
    });

    var link = svg.selectAll(".link")
      .data(bilinks)
      .enter().append("path")
        .attr("class", "link");

    var node = svg.selectAll(".node")
      .data(nodes.filter(function(d) { return d.id; }))
      .enter().append("circle")
        .attr("class", "node")
        //Size the node differently if the node represents a department
        .attr("r", function(d){
            return (d.group === "Department") ? 15 : (5 * linkMap.get(d.id));
        })
        //Color the node differently if the node represents a department
        .attr("fill", function(d) { 
            return (d.group === "Department") ? "#3B4656" : color(d.group);
        })
        //Color the node outline differently if the node represents a department
        .style("stroke", function(d){
            return (d.group === "Department") ? "black" : "white";
        })
        //When a node is clicked, all nodes that are more than 1 link away are faded out
        .on("click", function(d){
            d3.event.stopPropagation();
            onNodeClick(this, d, links);
        })
        .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));


    //Unfade all nodes when whitespace is clicked
    svg.on("click", function(){
        d3.selectAll(".node")
            .attr("opacity", "1");
    });

    //Force parameter buttons
    d3.select("#forcesPlus")
        .on("click", function(){
            updateParameters(1);
    });
    d3.select("#forcesMinus")
        .on("click", function(){
            updateParameters(-1);
    });

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

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

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

    function ticked() {
        link.attr("d", positionLink);
        node.attr("transform", positionNode);
    }

    /** Updates the force parameters of the graph
     * 
     * @param sign - Determines if we are increasing or decreasing the parameters
     */
    function updateParameters(sign){
        //Increase the passive jiggle of the nodes so that they visually update (expand/contract) 
        //when the parameter gets changed
        simulation.alphaTarget(.1);

        forceParameter += sign * 20;
        maxDistanceParameter += sign * 20; //Need to update the max distance so that the
                                           //links can actually get longer

        simulation.nodes(nodes);
        simulation.force("link", d3.forceLink().distance(forceParameter))
                  .force("charge", d3.forceManyBody().distanceMax(maxDistanceParameter));

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

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

        //Hack so that the buttons don't break when the nodes settle down to a stand-still.
        //Not sure why the buttons don't update once they are still, but this solves it.
        //Need the nodes to move in order to see the change in force parameter.
        setTimeout(function(){
            simulation.alphaTarget(.01);
        }, 2000);
    }

});

//Fades out any nodes that are not connected to the clicked node
function onNodeClick(click, selectedNode, linkList){
    var linkedNodes = d3.set();

    linkList.forEach(function(v){
        //If the node clicked is the link's source, add the link's target
        if (selectedNode.id === v.source.id){
            linkedNodes.add(v.target.id);
        }
        //If the node clicked is the link's target, add the link's source
        else if (selectedNode.id === v.target.id){
            linkedNodes.add(v.source.id);
        }
    });

    d3.selectAll(".node")
        .attr("opacity", function(d){
            if (linkedNodes.has(d.id))
                return "1";
            else
                return "0.1";
    });

    d3.select(click)
        .attr("opacity", "1");
}

function positionLink(d) {
    return "M" + d[0].x + "," + d[0].y
         + "S" + d[1].x + "," + d[1].y
         + " " + d[2].x + "," + d[2].y;
}

function positionNode(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(.01);
    d.fx = null, d.fy = null;
}

function linkDistance(d){
    console.log(d);
}

1 个答案:

答案 0 :(得分:1)

有关如何添加缩放的示例,请访问:https://coderwall.com/p/psogia/simplest-way-to-add-zoom-pan-on-d3-js

从技术上讲,你所做的是在svg root上添加一个<g>元素,然后通过翻译和缩放该元素告诉d3自动处理缩放/平移。因此,所有内容(节点和链接)也必须添加到该元素内(此处:zoomContainer):

var zoomContainer = svg.call(d3.zoom().on("zoom", function () {
    zoomContainer.attr("transform", d3.event.transform)
}))
.append("g");

...

var link = zoomContainer.selectAll(".link")
                .data(bilinks)
                .enter().append("path")
...

var node = zoomContainer.selectAll(".node")
                .data(nodes.filter(function(d) { return d.id; }))
                .enter().append("circle")
...

https://jsfiddle.net/fseeyphe/