d3.js - 添加新节点后图形被破坏

时间:2017-12-29 17:17:04

标签: javascript jquery d3.js

我使用以下逻辑构建下图:

  • 鼠标悬停节点 - 它的非连接节点&他们的链接是透明的。
  • 鼠标悬停链接 - 所有节点&链接是透明的,除了触摸的链接(变为粗体)和它的两个节点。

这很好用,直到我尝试向图表添加更多节点。按添加节点按钮,添加另一个节点及其与该方的链接。问题是,节点逻辑被破坏(链接逻辑仍然有效)。 知道为什么吗?

谢谢! JSFiddle

function removeNodePopup() {
    $("#nodePopup").remove();
}

function showNodePopup(node) {
    removeNodePopup();
    if (!node['data']) {
        return;
    }

    var data = node['data'];
    var htmlStr = '';
    htmlStr += '<div id="nodePopup" >';
    htmlStr += '    <div><button id="nodePopupCloseButton" type="button" class="close" data-dismiss="alert"><span class="glyphicon glyphicon-remove" style="font-size: 13px;"> </span> </div>';
    htmlStr += '    <div class="nodePopupName">' + data['name'] + '</div>';
    if (data['desc']) {
        if (data['desc'].startsWith("http")) {
            htmlStr += '    <a class="nodePopupLink" href="' + data['desc'] + '" target="_blank">Go to post..</a>';
        }
        else {
            htmlStr += '    <div class="nodePopupDesc">' + data['desc'] + '</div>';
        }
    }
    htmlStr += '    <div class="nodePopupGroup">GROUP: ' + data['groupId'] + '</div>';
    htmlStr += '    <div class="nodePopupLeader">LEADER: ' + data['leaderId'] + '</div>';
    htmlStr += '    <div class="nodePopupImage"><img src="' + node['image'] + '" style="width: 130px;" /></div>';
    htmlStr += '</div>';

    $("body").append(htmlStr);
    $("#nodePopupCloseButton").click(removeNodePopup);
}

const LINK_DEFAULT_COLOR = "#ccc";
const NODE_DEFAULT_COLOR = "gray";
const DEFAULT_OPACITY = 1;
const BACKGROUND_OPACITY = 0.2;

function Graph(elementId) {
    var svg;
    var simulation;
    var mNodesData = [];
    var mEdgesData = [];
    var mNode = null;
    var mLink = null;
    var elementId;
    var heightDelta = 100;
    var width = window.innerWidth;
    var height = window.innerHeight - heightDelta;

    return {
        init: function () {
            svg = d3.select('#' + elementId)
                .append("svg")
                .attr("width", width)
                .attr("height", height);

            simulation = d3.forceSimulation()
                .force(".edge", d3.forceLink())
                .force("charge", d3.forceManyBody().strength(-600))
                .force("center", d3.forceCenter(width / 2, height / 2));

            mLink = svg.selectAll(".edge")
                .attr("class", "edge")
                .style("stroke", LINK_DEFAULT_COLOR)
                .style("stroke-width", function (e) {
                    return 1
                    /* e.width*/
                });

            mNode = svg.selectAll(".node")
                .attr("class", "node");
        },
        clearGraph: function () {
            $('#' + this.elementId).empty();
        },
        getNodes: function () {
            return mNodesData;
        },
        getEdges: function () {
            return mEdgesData;
        },
        addNodes: function (nodes) {
            mNodesData = mNodesData.concat(nodes);
        },
        addEdges: function (edges) {
            mEdgesData = mEdgesData.concat(edges);
        },
        onMouseOut: function () {
            // removePopup();
            mNode.select("image").style("opacity", DEFAULT_OPACITY);
            mNode.select("circle").style("stroke", NODE_DEFAULT_COLOR);
            mLink.style("opacity", DEFAULT_OPACITY).style("stroke", LINK_DEFAULT_COLOR);
        },
        draw: function () {
            mNode = svg.selectAll(".node")
                .data(mNodesData)
                .enter()
                .append("g")
                .attr("class", "node").
                merge(mNode);

            mLink = svg.selectAll(".edge")
                .data(mEdgesData)
                .enter()
                .append("line")
                .attr("class", "edge")
                .style("stroke", LINK_DEFAULT_COLOR)
                .style("stroke-width", function (e) {
                    return 2
                    /* e.width*/
                }).merge(mLink).lower();

            mNode.call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));

            mNode.on('mouseover', function (thisNode) {
                showNodePopup(thisNode);
                var thisNodeID = thisNode.id;
                var connectedNodes = mEdgesData.filter(function(d) {
                    return d.source.id === thisNodeID || d.target.id === thisNodeID
                }).map(function(d) {
                    return d.source.id === thisNodeID ? d.target.id : d.source.id
                });

                mNode.each(function (otherNode, id) {
                    var image = d3.select(this).select("image");
                    var circle = d3.select(this).select("circle");
                    if (connectedNodes.indexOf(otherNode.id) > -1 || thisNodeID == otherNode.id) {
                        image.style("opacity", DEFAULT_OPACITY);
                        circle.style("stroke", NODE_DEFAULT_COLOR);
                    }
                    else {
                        image.style("opacity", BACKGROUND_OPACITY);
                        circle.style("stroke", "#f6f6f6");
                    }
                });

                // var filteredNodes = mNode.filter(function(otherNode) {
                //     return connectedNodes.indexOf(otherNode.id) == -1
                // });
                //
                // filteredNodes.select("image").style("opacity", BACKGROUND_OPACITY);
                // filteredNodes.select("circle").style("stroke", "#f6f6f6");
                //
                // var unfilterdNode = mNode.filter(function (otherNode) {
                //     return connectedNodes.indexOf(otherNode.id) > -1 || thisNodeID == otherNode.id;
                // });
                // unfilterdNode.select("image").style("opacity", DEFAULT_OPACITY);
                // unfilterdNode.select("circle").style("stroke", NODE_DEFAULT_COLOR);

                mLink.filter(function (otherLink) {
                    return (thisNode !== otherLink.source && thisNode !== otherLink.target);
                }).style("opacity", BACKGROUND_OPACITY);

                mLink.filter(function (otherLink) {
                    return (thisNode == otherLink.source || thisNode == otherLink.target);
                }).style("opacity", DEFAULT_OPACITY);
            })
                .on('mouseout', this.onMouseOut);

            mLink.on('mouseover', function (currentLink) {
                mLink.filter(function (otherLink) {
                    return (currentLink == otherLink);
                }).style("stroke", "black");
                mLink.filter(function (otherLink) {
                    return (currentLink !== otherLink);
                }).style("opacity", BACKGROUND_OPACITY);

                mNode.filter(function (otherNode) {
                    return (currentLink.source != otherNode || currentLink.target != otherNode);
                }).select("image").style("opacity", BACKGROUND_OPACITY);
                mNode.filter(function (otherNode) {
                    return (currentLink.source != otherNode || currentLink.target != otherNode);
                }).select("circle").style("stroke", "#f6f6f6");

                mNode.filter(function (d1) {
                    return (d1 == currentLink.source || d1 == currentLink.target);
                }).select("image").style("opacity", DEFAULT_OPACITY);
                mNode.filter(function (d1) {
                    return (d1 == currentLink.source || d1 == currentLink.target);
                }).select("circle").style("stroke", NODE_DEFAULT_COLOR);

            }).on('mouseout', this.onMouseOut);

            var nodeCircle = mNode.append("circle")
                .attr("r", function (d) {
                    return 0.5 * Math.max(d.width, d.height)
                })
                .attr("stroke", NODE_DEFAULT_COLOR)
                .attr("stroke-width", "2px")
                .attr("fill", "white");

            var nodeImage = mNode.append("image")
                .attr("xlink:href", function (d) {
                    return d.image
                })
                .attr("height", function (d) {
                    return d.height + ""
                })
                .attr("width", function (d) {
                    return d.width + ""
                })
                .attr("x", function (d) {
                    return -0.5 * d.width
                })
                .attr("y", function (d) {
                    return -0.5 * d.height
                })
                .attr("clip-path", function (d) {
                    return "circle(" + (0.48 * Math.max(d.width, d.height)) + "px)"
                });


            simulation.nodes(mNodesData);
            simulation.force(".edge").links(mEdgesData);

            simulation.on("tick", function () {
                mLink.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;
                    })

                mNode.attr("transform", function (d) {
                    return "translate(" + d.x + "," + d.y + ")"
                });
                mNode.attr("cx", function (d) {
                    return d.x = Math.max(d.width, Math.min(width - d.width, d.x));
                })
                    .attr("cy", function (d) {
                        return d.y = Math.max(d.height, Math.min(height - heightDelta - d.height, 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(0);
                d.fx = null;
                d.fy = null;
            }
        }
    }
}

initialData = {
    "nodes": [{
        "id": 0,
        "image": "images/0.jpg",
        "height": 40,
        "width": 40,
        "data": {
            "name": "Number0",
            "groupId": "Bla1",
            "desc": "Desc1",
            "leaderId": "123-123"
        }
    }, {
        "id": 1,
        "image": "images/1.jpeg",
        "height": 100,
        "width": 100,
        "data": {
            "name": "Number1",
            "groupId": "Bla2",
            "desc": "Desc1",
            "leaderId": "123-123"
        }
    }, {
        "id": 2,
        "image": "images/2.png",
        "height": 50,
        "width": 50,
        "data": {
            "name": "Number2",
            "groupId": "Bla3",
            "desc": "Desc1",
            "leaderId": "123-123"
        }
    }, {
        "id": 3,
        "image": "images/3.jpeg",
        "height": 40,
        "width": 40,
        "data": {
            "name": "Number3",
            "groupId": "Bla4",
            "desc": "Desc1",
            "leaderId": "123-123"
        }
    }],
    "edges": [{
        "source": 0,
        "target": 1,
        "width": 5,
        "data": {
            "counter": 500
        }
    }, {
        "source": 0,
        "target": 2,
        "width": 10,
        "data": {
            "counter": 500
        }
    }, {
        "source": 0,
        "target": 3,
        "width": 1,
        "data": {
            "counter": 500
        }
    }]
};


var graph = Graph('d3Graph');
graph.init();
graph.addNodes(initialData.nodes);
graph.addEdges(initialData.edges);
graph.draw();
//add();



function add() {
    graph.addNodes([{
        "id": 4,
        "image": "images/4.jpg",
        "height": 20,
        "width": 20,
        "data": {
            "name": "Number4",
            "groupId": "Bla4",
            "desc": "Desc4",
            "leaderId": "1234-1234"
        }
    }]);
    graph.addEdges([{
        "source": 4,
        "target": 3,
        "width": 1,
        "data": {
            "counter": 500
        }
    }])
    graph.draw();
}

1 个答案:

答案 0 :(得分:1)

jsfiddle

您多次调用draw方法,因此您必须小心对待它。

只应添加新添加的节点imagecircle标记, 所以你应该改变

mNode = svg.selectAll(".node")
            .data(mNodesData)
            .enter()
            .append("g")
            .attr("class", "node").
            merge(mNode);

var newNodes = svg.selectAll(".node")
            .data(mNodesData)
            .enter()
            .append("g")
            .attr("class", "node");

mNode=newNodes.merge(mNode);

并更改

var nodeCircle = mNode.append("circle")

var nodeCircle = newNodes.append("circle")

image相关代码应该以相同的方式更改。