D3js - 强制有向图 - 高级突出显示neigbour节点和链接,是否可能?

时间:2013-10-03 08:17:12

标签: javascript html d3.js highlight force-layout

this tutorial from Mike Bostock的帮助下,我成功地突出了我的力导向图。现在我的想法和我的图形需求的进一步程序,我有点卡住,首先是因为我仍然对d3js和第二,我没有发现任何类似的东西。

要清除我的意图,您可以在以下链接中突出显示当前图表:http://jsfiddle.net/2FwSY/

它很好用,但我的想法有点先进。我想知道是否有可能以这样的方式调整突出显示:在鼠标转移到节点时,它会突出显示该节点到邻居节点,邻居节点以及从相邻节点到其邻居节点的链接的链接。

准确地说,在我的jsfiddle例子上。如果您悬停BNGBNGYHOCEO和所有links between them都会突出显示。问题是我用作较大节点之间“连接”的小蓝节点,我现在的亮点是“切断”它们,因为它们实际上是节点。在我看来,还需要一个IF语句,它可以判断悬停节点是否是那个小的或更大的节点,因为我希望那个小小的连接节点正好像现在一样工作。

我不知道从哪里开始,所以欢迎任何建议或建议......

完整脚本如下所示:

var data = {"nodes":[
                        {"name":"YHO", "full_name":"Yahoo", "type":1, "slug": "www.yahoo.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                        {"name":"GGL", "full_name":"Google", "type":2, "slug": "www.google.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                        {"name":"BNG", "full_name":"Bing", "type":2, "slug": "www.bing.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},
                        {"name":"YDX", "full_name":"Yandex", "type":2, "slug": "www.yandex.com", "entity":"company", "img_hrefD":"", "img_hrefL":""},

                        {"name":"Desc1", "type":4, "slug": "", "entity":"description"},
                        {"name":"Desc2", "type":4, "slug": "", "entity":"description"},
                        {"name":"Desc4", "type":4, "slug": "", "entity":"description"},

                        {"name":"CEO", "prefix":"Mr.", "fst_name":"Jim", "snd_name":"Bean", "type":3, "slug": "", "entity":"employee"},
                        {"name":"ATT", "prefix":"Ms.", "fst_name":"Jenna", "snd_name":"Jameson", "type":3, "slug": "", "entity":"employee"},
                        {"name":"CTO", "prefix":"Mr.", "fst_name":"Lucky", "snd_name":"Luke", "type":3, "slug": "", "entity":"employee"},
                        {"name":"CDO", "prefix":"Ms.", "fst_name":"Pamela", "snd_name":"Anderson", "type":3, "slug": "", "entity":"employee"},
                        {"name":"CEO", "prefix":"Mr.", "fst_name":"Nacho", "snd_name":"Vidal", "type":3, "slug": "", "entity":"employee"},

                        {"name":"Desc5", "type":4, "slug": "", "entity":"description"},
                    ], 
            "links":[
                        {"source":0,"target":4,"value":1,"distance":5},
                        {"source":0,"target":5,"value":1,"distance":5},
                        {"source":0,"target":6,"value":1,"distance":5},

                        {"source":1,"target":4,"value":1,"distance":5},
                        {"source":2,"target":5,"value":1,"distance":5},
                        {"source":3,"target":6,"value":1,"distance":5},

                        {"source":7,"target":3,"value":10,"distance":6},
                        {"source":8,"target":3,"value":10,"distance":6},
                        {"source":9,"target":1,"value":10,"distance":6},
                        {"source":10,"target":1,"value":10,"distance":6},

                        {"source":11,"target":12,"value":10,"distance":6},
                        {"source":12,"target":2,"value":10,"distance":6},
                        ]
               }    



    var w = 560,
        h = 500,
        radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);

    var vis = d3.select("body").append("svg:svg")
        .attr("width", w)
        .attr("height", h);

        //vis.append("defs").append("marker")
        //.attr("id", "arrowhead")
        //.attr("refX", 22 + 3) /*must be smarter way to calculate shift*/
        //.attr("refY", 2)
        //.attr("markerWidth", 6)
        //.attr("markerHeight", 4)
        //.attr("orient", "auto")
        //.append("path")
            //.attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead

    //d3.json(data, function(json) {
        var force = self.force = d3.layout.force()
            .nodes(data.nodes)
            .links(data.links)
            .linkDistance(function(d) { return (d.distance*10); })
            //.friction(0.5)
            .charge(-250)
            .size([w, h])
            .start();



        var link = vis.selectAll("line.link")
            .data(data.links)
            .enter().append("svg:line")
            .attr("class", function (d) { return "link" + d.value +""; })
            .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; })
            .attr("marker-end", function(d) {
                                                if (d.value == 1) {return "url(#arrowhead)"}
                                                else    { return " " }
                                            ;});




        function openLink() {
            return function(d) {
                var url = "";
                if(d.slug != "") {
                    url = d.slug
                } //else if(d.type == 2) {
                    //url = "clients/" + d.slug
                //} else if(d.type == 3) {
                    //url = "agencies/" + d.slug
                //}
                window.open("//"+url)
            }
        }




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


        node.append("circle")
            .attr("class", function(d){ return "node type"+d.type})
            .attr("r",function(d){if(d.entity == "description"){ return 6 } else { return 18 }})
            //.on("mouseover", expandNode);
            //.style("fill", function(d) { return fill(d.type); })



        node.append("text")
            .attr("class", function(d){ return "nodetext title_"+d.name })
            .attr("dx", 0)
            .attr("dy", ".35em")
            .style("font-size","10px")
            .attr("text-anchor", "middle")
            .style("fill", "white")
            .text(function(d) { if (d.entity != "description"){return d.name} });



        node.on("mouseover", function (d) {
            if (d.entity == "company"){   
                d3.select(this).select('text')
                    .transition()
                    .duration(300)
                    .text(function(d){
                            return d.full_name;
                        })
                    .style("font-size","15px")

            }
            else if(d.entity == "employee"){
                var asdf = d3.select(this);
                asdf.select('text').remove();

                asdf.append("text")
                            .text(function(d){return d.prefix + ' ' + d.fst_name })
                            .attr("class","nodetext")
                            .attr("dx", 0)
                            .attr("dy", ".35em")
                            .style("font-size","5px")
                            .attr("text-anchor", "middle")
                            .style("fill", "white")
                            .transition()
                            .duration(300)
                            .style("font-size","12px");

                asdf.append("text").text(function(d){return d.snd_name })
                            .attr("class","nodetext")
                            .attr("transform","translate(0, 12)")
                            .attr("dx", 0)
                            .attr("dy", ".35em")
                            .style("font-size","5px")
                            .attr("text-anchor", "middle")
                            .style("fill", "white")
                            .transition()
                            .duration(300)
                            .style("font-size","12px");                                         
            }
            else {
                d3.select(this).select('text')
                    .transition()
                    .duration(300)
                    .style("font-size","15px")
            }

            if (d.entity == "company") {
                d3.select(this).select('image')
                    .attr("width", "90px")
                    .attr("x", "-46px")
                    .attr("y", "-36.5px")
                    .attr("xlink:href", function (d) {
                        return d.img_hrefL
                        });               
            }

            if (d.entity == "company") {

                d3.select(this).select('circle')
                                .transition()
                                .duration(300)
                                .attr("r",28)

            }
            else if (d.entity == "employee"){
                d3.select(this).select('circle')
                                .transition()
                                .duration(300)
                                .attr("r",32)
            }
        })


         node.on("mouseout", function (d) {
            if (d.entity == "company") {
                d3.select(this).select('text')
                    .transition()
                    .duration(300)
                    .text(function(d){return d.name;})
                    .style("font-size","10px")
                }
            else if(d.entity == "employee"){
                ///////////////////////////
                // CHANGE
                ///////////////////////////

                d3.select(this).selectAll('text').remove();

                //d3.select(this).select('text')
                d3.select(this).append('text')
                    .text(function(d){return d.name;})
                    .style("font-size","14px")  
                    .attr("dx", 0)
                    .attr("dy", ".35em")
                    .attr("text-anchor", "middle")
                    .style("fill", "white")
                    .attr("class","nodetext")
                    .transition()
                    .duration(300)
                    .style("font-size","10px")

            }
            else {
                d3.select(this).select('text')
                    .transition()
                    .duration(300)
                    .style("font-size","10px")
            }


             if (d.entity == "company") {
                d3.select(this).select('image')
                    .attr("width", "70px")
                    .attr("x", "-36px")
                    .attr("y", "-36px")
                    .attr("xlink:href", function (d) {
                    return d.img_hrefD
                });
            }

            if (d.entity == "company" || d.entity == "employee") {

                d3.select(this).select('circle')
                                .transition()
                                .duration(300)
                                .attr("r",18)
            }

        });

        node.on("mouseover", fade(.4,"red"))
            .on("mouseout", fade(1));

var linkedByIndex = {};
    data.links.forEach(function(d) {
        linkedByIndex[d.source.index + "," + d.target.index] = 1;
    });

    function isConnected(a, b) {
        return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }

        force.on("tick", function() {
          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 fade(opacity,color) {
            return function(d) {

         node.style("stroke-opacity", function(o) {
            thisOpacity = isConnected(d, o) ? 1 : opacity;
            this.setAttribute('fill-opacity', thisOpacity);
            return thisOpacity;
        });

                link.style("stroke-opacity", function(o) {
                    return o.source === d || o.target === d ? 1 : opacity;
                })

                .style("stroke", function(o) {
                    return o.source === d || o.target === d ? color : "#000" ;
                });
            };

            }
    //});

1 个答案:

答案 0 :(得分:1)

要识别“特殊”节点,您可以为标识它们的数据添加另一个属性。然后,您可以在突出显示功能中检查此项,并在必要时获得二级邻居。代码看起来像这样。

function fade(opacity,color) {
    return function(d) {
        var connected = [d];
        if(d.isAuxiliary) {
            node.each(function(o) { if(isConnected(d, o)) { connected.push(o); } });
        }
        node.style("stroke-opacity", function(o) {
            thisOpacity = opacity;
            connected.forEach(function(e) { 
                if(isConnected(e, o)) { thisOpacity = 1; }
            });
            this.setAttribute('fill-opacity', thisOpacity);
            return thisOpacity;
        });
        // similar for links
    }
}

您可以调整此代码以执行任意级别的邻居。