繁体   English   中英

淡化未直接连接到d3图形中悬停的节点的链接和节点

[英]Fade links and nodes that are not immediately connected to the node hovered on in a d3 graph

我现在正在进行d3和web开发。

我正在使用d3库创建一个图形。 我试图确保每当用户在节点上盘旋时,其直接父节点和子节点的不透明度应该保持不变,但其余节点的不透明度应该降低。

我部分实现了我的目标,让下面写的文字在我悬停的所有文本之外消失。

这是我的javascript代码:

// setting up the canvas size :)
var width = 960,
    height = 500;

// initialization
var svg = d3.select("div").append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("id", "blueLine"); // the graph invisible thing :)

var force = d3.layout.force()
    .gravity(0) // atom's cohesiveness / elasticity of imgs :)
    .distance(150) // how far the lines ---> arrows :)
    .charge(-50) // meta state transition excitement
    .linkDistance(140)
    //.friction(0.55) // similar to charge for quick reset :)
    .size([width, height]); // degree of freedom to the canvas

// exception handling
d3.json("graph.json", function(error, json) {
    if (error) throw error;

    // Restart the force layout
    force
        .nodes(json.nodes)
        .links(json.links)
        .start();

    // Build the link
    var link = svg.selectAll(".links")
        .data(json.links)
        .enter().append("line")
        .attr("class", "lol")
        .style("stroke-width", "2")
        .attr("stroke", function(d){
            return linkColor(d.colorCode);})
        .each(function(d) {
            var color = linkColor(d.colorCode);
            d3.select(this).attr("marker-end", marker(color));
        });

    function marker(color) {
        svg.append("svg:marker")
            .attr("id", color.replace("#", ""))
            .attr("viewBox", "0 -5 10 10")
            .attr("refX", 10)
            .attr("refY", 0)
            .attr("markerWidth", 15)
            .attr("markerHeight", 15)
            .attr("orient", "auto")
            .attr("markerUnits", "userSpaceOnUse")
            .append("svg:path")
            .attr("d", "M0,-5L10,0L0,5")
            .style("fill", color);

        return "url(" + color + ")";
    };

    // this link : https://stackoverflow.com/questions/32964457/match-arrowhead-color-to-line-color-in-d3

    // create a node
    var node = svg.selectAll(".nodes")
        .data(json.nodes)
        .enter().append("g")
        .attr("class", "node")
        .call(force.drag)
        .on("mouseover", fade(.2))
        .on("mouseout", fade(1));;

    // Define the div for the tooltip
    var div = d3.select("body").append("pre")
        .attr("class", "tooltip")
        .style("opacity", 0);

    // Append custom images
    node.append("svg:image")
        .attr("xlink:href",  function(d) { return d.img;}) // update the node with the image
        .attr("x", function(d) { return -5;}) // how far is the image from the link??
        .attr("y", function(d) { return -25;}) // --- same ---
        .attr("height", 55) // size
        .attr("width", 55);

    node.append("text")
        .attr("class", "labelText")
        .attr("x", function(d) { return -5;})
        .attr("y", function(d) { return 48;})
        .text(function(d) { return d.name });

    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 + ")"; });

        force.stop();
    });

    function linkColor(linkCode) {
        switch (linkCode)
        {
            case 'ctoc':
                return '#0000FF';//blue
                break;
            case 'ctof':
                return '#00afaa';//green
                break;
            case 'ftoc':
                return '#fab800';//yellow
                break;
            case 'ftof':
                return '#7F007F';//purple
                break;
            default:
                return '#0950D0';//generic blue
                break;
        }
    }

    // build a dictionary of nodes that are linked
    var linkedByIndex = {};
    links.forEach(function(d) {
        linkedByIndex[d.source.id + "," + d.target.id] = 1;
    });

    // check the dictionary to see if nodes are linked
    function isConnected(a, b) {
        return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }

    // fade nodes on hover
    function fade(opacity) {
        return function(d) {
            // check all other nodes to see if they're connected
            // to this one. if so, keep the opacity at 1, otherwise
            // fade
            node.style("stroke-opacity", function(o) {
                thisOpacity = isConnected(d, o) ? 1 : opacity;
                return thisOpacity;
            });
            node.style("fill-opacity", function(o) {
                thisOpacity = isConnected(d, o) ? 1 : opacity;
                return thisOpacity;
            });
            // also style link accordingly
            link.style("stroke-opacity", function(o) {
                return o[0] === d || o[2] === d ? 1 : opacity;
            });
        };
    }
});

css:

.node text {
    font-size: 1rem;
    text-decoration: underline;
    fill: #aeb4bf;
    font-weight: 700;
    text-anchor: end;
    alignment-baseline: central;
    pointer-events: none;
}
.node:not(:hover) .nodetext {
    display: none;
}

pre.tooltip {
    position: absolute;
    text-align: left;
    width: auto;
    height: auto;
    padding: 5px;
    font: 14px "Helvetica","Arial",sans-serif bold;
    background: #273142;
    border: 0;
    border-radius: 8px;
    cursor: pointer!important;
    pointer-events: none;
    color: #aeb4bf;
}

和我的json文件:

{
  "nodes": [
    {"x": 100, "y": 100, "name": "A", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 0},
    {"x": 250, "y": 100, "name": "B", "img":"https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 1},
    {"x": 400, "y": 100, "name": "C", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id": 2},
    {"x": 550, "y": 200, "name": "D", "img":"https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 3},
    {"x": 700, "y": 200, "name": "E", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 4},
    {"x": 100, "y": 300, "name": "F", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 5},
    {"x": 250, "y": 300, "name": "G", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id" : 6},
    {"x": 400, "y": 300, "name": "H", "img": "https://cdn0.iconfinder.com/data/icons/flat-round-system/512/android-128.png", "id": 7}
  ],
  "links": [
    {"source":  0, "target":  1, "colorCode" : "ctof"},
    {"source":  1, "target":  2, "colorCode" : "ftoc"},
    {"source":  2, "target":  3, "colorCode" : "ctof"},
    {"source":  3, "target":  4, "colorCode" : "ftoc"},
    {"source":  5, "target":  6, "colorCode" : "ctof"},
    {"source":  6, "target":  7, "colorCode" : "ftoc"},
    {"source":  7, "target":  3, "colorCode" : "ctof"}
  ]
}

我不知道我哪里错了。 我需要实现两件事:1。当我将鼠标悬停在X和2上时,X的直接父母和孩子应该保持不变。与X没有直接关系的其他节点应该像其他链接一样消失。 目前,所有节点都没有消失。

我研究了我的代码并意识到它说所有节点都相互连接,所以我的isConnected()是罪魁祸首。 我仍然不知道这些链接。

请帮我。

要解决的两个问题

  1. 对于您的节点,因为它们是图像文件,您需要设置它们的“不透明度”,而不是笔画/填充不透明度。

     node.style("opacity", function(o) { thisOpacity = isConnected(d, o) ? 1 : opacity; return thisOpacity; }); 
  2. 对于您的链接,假设名称属性是唯一的,您应该将链接的源和目标与所选节点的名称相匹配。

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

除了@TomShanley的回答

如果你是d3的新手,为什么还要使用d3v3? 目前我们处于d3v5并且API得到了很大改进。

该程序没有开箱即用,因为在确定linkedByIndex它抱怨links不存在。 它应该是json.links

linkColor return后无需break

您搜索类svg.selectAll(".nodes")元素,但您使用.attr("class", "node")创建元素。 如果要正确使用enter-exit-update,这将不起作用。 与链接相同:搜索类links但添加类lol元素。

您的标记不是唯一的,无需使用each marker-end添加marker-end 也许最好根据颜色创建一组标记,然后引用它们。 在原始代码中,您有多个具有相同id标记。 HTML中的id应该是唯一的。

// Build the link
var link = svg.selectAll(".lol")
    .data(json.links)
    .enter().append("line")
    .attr("class", "lol")
    .style("stroke-width", "2")
    .attr("stroke", function(d){
        return linkColor(d.colorCode);})
    .attr("marker-end", function(d, i){
        return marker(i, linkColor(d.colorCode));} );

function marker(i, color) {
    var markId = "#marker"+i;
    svg.append("svg:marker")
        .attr("id", markId.replace("#", ""))
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 10)
        .attr("refY", 0)
        .attr("markerWidth", 15)
        .attr("markerHeight", 15)
        .attr("orient", "auto")
        .attr("markerUnits", "userSpaceOnUse")
        .append("svg:path")
        .attr("d", "M0,-5L10,0L0,5")
        .style("fill", color);

    return "url(" + markId + ")";
};

编辑唯一标记,链接是从边到边的路径

我已经修改了代码:

  • 每种颜色的唯一标记放在svg的defs标签中。 如果尚未对此颜色使用对象进行跟踪,则创建一个新标记。
  • 链接现在是应用Gerardo描述的标记技巧的路径
  • 图像现在以节点位置为中心,这仅适用于圆形图像。

这是完整的代码

var width = 960,
    height = 500;

// initialization
var svg = d3.select("div").append("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("id", "blueLine"); // the graph invisible thing :)

var svgDefs = svg.append("defs");

var force = d3.layout.force()
    .gravity(0) // atom's cohesiveness / elasticity of imgs :)
    .distance(150) // how far the lines ---> arrows :)
    .charge(-50) // meta state transition excitement
    .linkDistance(140)
    //.friction(0.55) // similar to charge for quick reset :)
    .size([width, height]); // degree of freedom to the canvas

// exception handling
d3.json("/fade-links.json", function(error, json) {
    if (error) throw error;

    var imageSize = { width:55, height:55 };

    // Restart the force layout
    force
        .nodes(json.nodes)
        .links(json.links)
        .start();

    var markersDone = {};

    // Build the link
    var link = svg.selectAll(".lol")
        .data(json.links)
        .enter().append("path")
        .attr("class", "lol")
        .style("stroke-width", "2")
        .attr("stroke", function(d){
            return linkColor(d.colorCode);})
        .attr("marker-end", function(d){
            return marker(linkColor(d.colorCode));} );

    function marker(color) {
        var markerId = markersDone[color];
        if (!markerId) {
            markerId = color;
            markersDone[color] = markerId;
            svgDefs.append("svg:marker")
                .attr("id", color.replace("#", ""))
                .attr("viewBox", "0 -5 10 10")
                .attr("refX", 10)
                .attr("refY", 0)
                .attr("markerWidth", 15)
                .attr("markerHeight", 15)
                .attr("orient", "auto")
                .attr("markerUnits", "userSpaceOnUse")
                .append("svg:path")
                .attr("d", "M0,-5L10,0L0,5")
                .style("fill", color);
        }
        return "url(" + markerId + ")";
    };

    // this link : https://stackoverflow.com/questions/32964457/match-arrowhead-color-to-line-color-in-d3

    // create a node
    var node = svg.selectAll(".node")
        .data(json.nodes)
        .enter().append("g")
        .attr("class", "node")
        .call(force.drag)
        .on("mouseover", fade(.2))
        .on("mouseout", fade(1));

    // Define the div for the tooltip
    var div = d3.select("body").append("pre")
        .attr("class", "tooltip")
        .style("opacity", 0);

    // Append custom images
    node.append("svg:image")
        .attr("xlink:href",  function(d) { return d.img;}) // update the node with the image
        .attr("x", function(d) { return -imageSize.width*0.5;}) // how far is the image from the link??
        .attr("y", function(d) { return -imageSize.height*0.5;}) // --- same ---
        .attr("height", imageSize.width)
        .attr("width", imageSize.height);

    node.append("text")
        .attr("class", "labelText")
        .attr("x", function(d) { return 0;})
        .attr("y", function(d) { return imageSize.height*0.75;})
        .text(function(d) { return d.name });

    force.on("tick", function() {
        // use trick described by Gerardo to only draw link from image border to border:  https://stackoverflow.com/q/51399062/9938317
        link.attr("d", function(d) {
            var dx = d.target.x - d.source.x,
                dy = d.target.y - d.source.y;
            var angle = Math.atan2(dy, dx);
            var radius = imageSize.width*0.5;
            var offsetX = radius * Math.cos(angle);
            var offsetY = radius * Math.sin(angle);
            return ( `M${d.source.x + offsetX},${d.source.y + offsetY}L${d.target.x - offsetX},${d.target.y - offsetY}`);
          });

        node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

        force.stop();
    });

    function linkColor(linkCode) {
        switch (linkCode)
        {
            case 'ctoc': return '#0000FF';//blue
            case 'ctof': return '#00afaa';//green
            case 'ftoc': return '#fab800';//yellow
            case 'ftof': return '#7F007F';//purple
        }
        return '#0950D0';//generic blue
    }

    // build a dictionary of nodes that are linked
    var linkedByIndex = {};
    json.links.forEach(function(d) {
        linkedByIndex[d.source.id + "," + d.target.id] = 1;
    });

    // check the dictionary to see if nodes are linked
    function isConnected(a, b) {
        return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
    }

    // fade nodes on hover
    function fade(opacity) {
        return function(d) {
            // check all other nodes to see if they're connected
            // to this one. if so, keep the opacity at 1, otherwise
            // fade
            node.style("opacity", function(o) {
                return isConnected(d, o) ? 1 : opacity;
            });
            // also style link accordingly
            link.style("opacity", function(o) {
                return o.source.name === d.name || o.target.name === d.name ? 1 : opacity;
            });
        };
    }
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM