草庐IT

javascript - 增加我的 D3 树布局的节点之间的差距

coder 2025-03-08 原文

如图所示,我试图增加树布局两侧最后节点之间的间隙,因为它们重叠

有没有办法在 D3 中做到这一点?

            {
            "name": "",
            "type": "network",
            "children": [{
                "name": "",
                "type": "lb",
                "children": [{
                        "name": "",
                        "type": "mm",
                        "id": "app",
                        "connServer": "s",
                        "size": 3938
                    }]
            },
            {
                "name": "",
                "type": "vm",
                "children": [{
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "appServer",
                        "id": "app1",
                        "connServer": "db1",
                        "size": 3938
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "webServer",
                        "id": "web1",
                        "connServer": "app1",
                        "size": 3534
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "dataServer",
                        "id": "db1",
                        "connServer": "app1",
                        "size": 7074
                    }]
                }]
            },
            {
                "name": "",
                "type": "vm",
                "children": [{
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "webServer",
                        "id": "web2",
                        "connServer": "app1",
                        "size": 721
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "dataServer",
                        "id": "db2",
                        "connServer": "db1",
                        "size": 721
                    }]
                }]
            },
            {
                "name": "",
                "type": "vm",
                "children": [{
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "appServer",
                        "id": "app2",
                        "connServer": "db3",
                        "size": 721
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "webServer",
                        "id": "web3",
                        "connServer": "app2",
                        "size": 721
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "dataServer",
                        "id": "db3",
                        "connServer": "app2",
                        "size": 721
                    }]
                }]
            },
            {
                "name": "",
                "type": "vm",
                "children": [{
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "appServer",
                        "id": "app3",
                        "connServer": "db4",
                        "size": 721
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "webServer",
                        "id": "web4",
                        "connServer": "app3",
                        "size": 721
                    }]
                },
                {
                    "name": "",
                    "type": "container",
                    "children": [{
                        "name": "",
                        "type": "dataServer",
                        "id": "db4",
                        "connServer": "app3",
                        "size": 721
                    }]
                }]
            },{
                "name": "",
                "type": "sto",
                "children": [{
                        "name": "",
                        "type": "mm",
                        "id": "app",
                        "connServer": "s",
                        "size": 3938
                    }]
            }]
        }

这是我用于构建树布局的代码,当我使用 separate() 时,树布局的子节点在空间中没有对齐,它们坚持自己的位置,没有单独的方法,折叠的子节点将被包裹在一起/在空间中对齐 /** * */ var tooltipMap = d3.map();

    // Get JSON data
    treeJSON = d3.json("network.json", function(error, treeData) {
        // Calculate total nodes, max label length
        var totalNodes = 0;
        var maxLabelLength = 15;
        // variables for drag/drop
        var selectedNode = null;
        var draggingNode = null;
        // panning variables
        var panSpeed = 200;
        var panBoundary = 20; // Within 20px from edges will pan when
        // dragging.
        // Misc. variables
        var i = 0;
        var duration = 750;
        var root;

        // size of the diagram
        var docWidth = $(document).width();
        var viewerWidth = docWidth / (1.361);
        var docHeight = $(document).height();
        var halfHeight = docHeight / 2;
        var quarterHeight = docHeight / 4;
        viewerHeight = halfHeight + quarterHeight;
        var tree = d3.layout.tree().size([ viewerHeight, viewerWidth ]);
        /*
         * var tree = d3.layout.tree().separation(function(a, b) { return ((a.parent ==
         * root) && (b.parent == root)) ? 3 : 1; }).size([ viewerHeight, viewerWidth -
         * 160 ]);
         */
        // define a d3 diagonal projection for use by the node paths
        // later on.
        var diagonal = d3.svg.diagonal().projection(function(d) {
            return [ d.x, -d.y ];
        });

        // A recursive helper function for performing some setup by
        // walking through all nodes

        function visit(parent, visitFn, childrenFn) {
            if (!parent)
                return;

            visitFn(parent);

            var children = childrenFn(parent);
            if (children) {
                var count = children.length;
                for ( var i = 0; i < count; i++) {
                    visit(children[i], visitFn, childrenFn);
                }
            }
        }

        // Call visit function to establish maxLabelLength
        visit(treeData, function(d) {
            totalNodes++;
            maxLabelLength = Math.max(d.name.length, maxLabelLength);

        }, function(d) {
            return d.children && d.children.length > 0 ? d.children : d.children;
        });

        function getConServers(element, event, status) {
            var conServerNode;
            var targetElement = event.target;
            var targetId = targetElement.id;
            if (targetId != null) {
                var server = tooltipMap.get(targetElement.id);
                if (server != null) {
                    var connectedServer = server.get("connId");
                    var outerTarget = d3.select("#" + "outer" + targetId);
                    var outerCon = d3.select("#" + "outer" + connectedServer);
                    if (status == "enter") {

                        outerTarget.style("stroke", "#48C127");
                        outerTarget.style("stroke-width", 3);
                        outerCon.style("stroke", "#F07A0B");
                        outerCon.style("stroke-width", 3);

                    } else if (status == "exit") {

                        outerTarget.style("stroke", "#fff");
                        outerCon.style("stroke", "#fff");
                        outerTarget.style("stroke-width", 1);
                        outerCon.style("stroke-width", 1);

                    }
                }
            }

        }

        // sort the tree according to the node names

        function sortTree() {
            tree.sort(function(a, b) {
                return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1;
            });
        }
        // Sort the tree initially incase the JSON isn't in a sorted
        // order.
        sortTree();

        // TODO: Pan function, can be better implemented.

        function pan(domNode, direction) {
            var speed = panSpeed;
            if (panTimer) {
                clearTimeout(panTimer);
                translateCoords = d3.transform(svgGroup.attr("transform"));
                if (direction == 'left' || direction == 'right') {
                    translateX = direction == 'left' ? translateCoords.translate[0] + speed : translateCoords.translate[0]
                            - speed;
                    translateY = translateCoords.translate[1];
                } else if (direction == 'up' || direction == 'down') {
                    translateX = translateCoords.translate[0];
                    translateY = direction == 'up' ? translateCoords.translate[1] + speed : translateCoords.translate[1]
                            - speed;
                }
                scaleX = translateCoords.scale[0];
                scaleY = translateCoords.scale[1];
                scale = zoomListener.scale();
                svgGroup.transition().attr("transform",
                        "translate(" + translateX + "," + translateY + ")scale(" + scale + ")");
                d3.select(domNode).select('g.node').attr("transform", "translate(" + translateX + "," + translateY + ")");
                zoomListener.scale(zoomListener.scale());
                zoomListener.translate([ translateX, translateY ]);
                panTimer = setTimeout(function() {
                    pan(domNode, speed, direction);
                }, 50);
            }
        }

        // Define the zoom function for the zoomable tree

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

        // define the zoomListener which calls the zoom function on the
        // "zoom" event constrained within the scaleExtents
        var zoomListener = d3.behavior.zoom().scaleExtent([ 0.1, 3 ]).on("zoom", zoom);

        function initiateDrag(d, domNode) {
            draggingNode = d;
            d3.select(domNode).select('.ghostCircle').attr('pointer-events', 'none');
            d3.selectAll('.ghostCircle').attr('class', 'ghostCircle show');
            d3.select(domNode).attr('class', 'node activeDrag');

            svgGroup.selectAll("g.node").sort(function(a, b) { // select
                // the
                // parent
                // and
                // sort
                // the
                // path's
                if (a.id != draggingNode.id)
                    return 1; // a is not the hovered element,
                // send "a" to the back
                else
                    return -1; // a is the hovered element,
                // bring "a" to the front
            });
            // if nodes has children, remove the links and nodes
            if (nodes.length > 1) {
                // remove link paths
                links = tree.links(nodes);
                nodePaths = svgGroup.selectAll("path.link").data(links, function(d) {
                    return d.target.id;
                }).remove();
                // remove child nodes
                nodesExit = svgGroup.selectAll("g.node").data(nodes, function(d) {
                    return d.id;
                }).filter(function(d, i) {
                    if (d.id == draggingNode.id) {
                        return false;
                    }
                    return true;
                }).remove();
            }

            // remove parent link
            parentLink = tree.links(tree.nodes(draggingNode.parent));
            svgGroup.selectAll('path.link').filter(function(d, i) {
                if (d.target.id == draggingNode.id) {
                    return true;
                }
                return false;
            }).remove();

            dragStarted = null;
        }

        // define the baseSvg, attaching a class for styling and the
        // zoomListener
        var baseSvg = d3.select("#tree-container").append("svg").attr("width", viewerWidth).attr("height", viewerHeight)
                .attr("class", "overlay").attr("id", "treesvg").call(zoomListener);

        // Define the drag listeners for drag/drop behaviour of nodes.
        dragListener = d3.behavior.drag().on("dragstart", function(d) {
            if (d == root) {
                return;
            }
            dragStarted = true;
            nodes = tree.nodes(d);
            d3.event.sourceEvent.stopPropagation();
            // it's important that we suppress the mouseover
            // event on the node being dragged. Otherwise it
            // will absorb the mouseover event and the
            // underlying node will not detect it
            // d3.select(this).attr('pointer-events',
            // 'none');
        }).on("drag", function(d) {
            if (d == root) {
                return;
            }
            if (dragStarted) {
                domNode = this;
                initiateDrag(d, domNode);
            }

            // get coords of mouseEvent relative to svg
            // container to allow for panning
            relCoords = d3.mouse($('svg').get(0));
            if (relCoords[0] < panBoundary) {
                panTimer = true;
                pan(this, 'left');
            } else if (relCoords[0] > ($('svg').width() - panBoundary)) {

                panTimer = true;
                pan(this, 'right');
            } else if (relCoords[1] < panBoundary) {
                panTimer = true;
                pan(this, 'up');
            } else if (relCoords[1] > ($('svg').height() - panBoundary)) {
                panTimer = true;
                pan(this, 'down');
            } else {
                try {
                    clearTimeout(panTimer);
                } catch (e) {

                }
            }

            d.x0 += d3.event.dy;
            d.y0 += d3.event.dx;
            var node = d3.select(this);
            node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
            updateTempConnector();
        }).on("dragend", function(d) {
            if (d == root) {
                return;
            }
            domNode = this;
            if (selectedNode) {
                // now remove the element from the
                // parent, and insert it into the new
                // elements children
                var index = draggingNode.parent.children.indexOf(draggingNode);
                if (index > -1) {
                    draggingNode.parent.children.splice(index, 1);
                }
                if (typeof selectedNode.children !== 'undefined' || typeof selectedNode._children !== 'undefined') {
                    if (typeof selectedNode.children !== 'undefined') {
                        selectedNode.children.push(draggingNode);
                    } else {
                        selectedNode._children.push(draggingNode);
                    }
                } else {
                    selectedNode.children = [];
                    selectedNode.children.push(draggingNode);
                }
                // Make sure that the node being added
                // to is expanded so user can see added
                // node is correctly moved
                expand(selectedNode);
                sortTree();
                endDrag();
            } else {
                endDrag();
            }
        });

        function endDrag() {
            selectedNode = null;
            d3.selectAll('.ghostCircle').attr('class', 'ghostCircle');
            d3.select(domNode).attr('class', 'node');
            // now restore the mouseover event or we won't be able to
            // drag a 2nd time
            d3.select(domNode).select('.ghostCircle').attr('pointer-events', '');
            updateTempConnector();
            if (draggingNode !== null) {
                update(root);
                centerNode(draggingNode);
                draggingNode = null;
            }
        }

        // Helper functions for collapsing and expanding nodes.

        function collapse(d) {
            if (d.children) {
                d._children = d.children;
                d._children.forEach(collapse);
                d.children = null;
            }
        }

        function expand(d) {
            if (d._children) {
                d.children = d._children;
                d.children.forEach(expand);
                d._children = null;
            }
        }

        var overCircle = function(d) {
            selectedNode = d;
            updateTempConnector();
        };
        var outCircle = function(d) {
            selectedNode = null;
            updateTempConnector();
        };

        // Function to update the temporary connector indicating
        // dragging affiliation
        var updateTempConnector = function() {
            var data = [];
            if (draggingNode !== null && selectedNode !== null) {
                // have to flip the source coordinates since we did this
                // for the existing connectors on the original tree
                data = [ {
                    source : {
                        x : selectedNode.y0,
                        y : selectedNode.x0
                    },
                    target : {
                        x : draggingNode.y0,
                        y : draggingNode.x0
                    }
                } ];
            }
            var link = svgGroup.selectAll(".templink").data(data);

            link.enter().append("path").attr("class", "templink").attr("d", d3.svg.diagonal()).attr('pointer-events',
                    'none');

            link.attr("d", d3.svg.diagonal());

            link.exit().remove();
        };

        // Function to center node when clicked/dropped so node doesn't
        // get lost when collapsing/moving with large amount of
        // children.

        function centerNode(source) {
            scale = zoomListener.scale();
            x = -source.y0;
            y = -source.x0;
            x = x * scale + viewerWidth / 2;
            y = y * scale + viewerHeight / 2;
            var mySvg = d3.select("#tree-container");
            mySvg.select('g').transition().duration(duration).attr("transform",
                    "translate(" + (x - 230) + "," + (y + 411) + ")scale(" + scale + ")");
            zoomListener.scale(scale);
            zoomListener.translate([ x, y ]);

            var shootX, shootY;

            // shootX=x+270;
            // shootY=y+350;

            shootX = "850.242468772961", shootY = "450.75";
            getShootImage(shootX, shootY);
        }

        /**
         * This function is used to add shooting image and text into the frame.
         * 
         * @param shootImgX
         * @param shootTextX
         * @return
         */
        function getShootImage(shootImgX, shootImgY) {
            d3.select("#shootCircle").remove();
            var imgUrl = "image/target-icon.png";
            baseSvg.append("image").attr("id", "shootCircle").attr("xlink:href", imgUrl).attr("x", shootImgX).attr("y",
                    shootImgY).attr("width", 50).attr("height", 50).style("fill", "red").classed("shoot", true);

            baseSvg.append("text").text("Shooting Gun").attr("x", shootImgX).attr("y", shootImgY + 50).attr("width", 50)
                    .attr("height", 50).style("font-weight", "bold");

            var loadButton = document.createElement("input");
            loadButton.setAttribute("type", "button");
            loadButton.setAttribute("value", "Add Load");
            loadButton.setAttribute("class", "btn btn-cust-info");
            loadButton.setAttribute("id", "addload");
            loadButton.setAttribute("x", shootImgX);
            loadButton.setAttribute("y", shootImgY);
            loadButton.setAttribute("width", 500);
            loadButton.setAttribute("height", 500);
            loadButton.style.align = "left";
        }

        // Toggle children function

        function toggleChildren(d) {
            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else if (d._children) {
                d.children = d._children;
                d._children = null;
            }
            return d;
        }

        // Toggle children on click.

        function click(d) {
            if (d3.event.defaultPrevented)
                return; // click suppressed
            d = toggleChildren(d);
            update(d);
            centerNode(d);
        }

        function update(source) {
            // Compute the new height, function counts total children of
            // root node and sets tree height accordingly.
            // This prevents the layout looking squashed when new nodes
            // are made visible or looking sparse when nodes are removed
            // This makes the layout more consistent.
            var levelWidth = [ 1 ];
            var childCount = function(level, n) {

                if (n.children && n.children.length > 0) {
                    if (levelWidth.length <= level + 1)
                        levelWidth.push(10);

                    levelWidth[level + 1] += n.children.length;
                    n.children.forEach(function(d) {
                        childCount(level + 1, d);
                    });
                }
            };
            childCount(0, root);
            var newHeight = d3.max(levelWidth) * 25; // 25 pixels per
            // line
            tree = tree.size([ newHeight, viewerWidth ]);

            // Compute the new tree layout.
            var nodes = tree.nodes(root).reverse(), links = tree.links(nodes);

            // Set widths between levels based on maxLabelLength.
            nodes.forEach(function(d) {
                d.y = (d.depth * (maxLabelLength * 7)); // maxLabelLength
                // * 10px
                // alternatively to keep a fixed scale one can set a
                // fixed depth per level
                // Normalize for fixed-depth by commenting out below
                // line
                // d.y = (d.depth * 500); //500px per level.

                /** My code -starts here * */
                if (nodes[i] != null && nodes[i] != 'undefined') {
                    if (nodes[i].type == "appServer") {
                        var toolMap = d3.map();
                        toolMap.set("connId", d.connServer);
                        toolMap.set("id", d.id);
                        toolMap.set("type", "Web server");
                        tooltipMap.set(d.id, toolMap);
                    } else if (nodes[i].type == "webServer") {
                        var toolMap = d3.map();
                        toolMap.set("connId", d.connServer);
                        toolMap.set("id", d.id);
                        toolMap.set("type", "Web server");
                        tooltipMap.set(d.id, toolMap);
                    } else if (nodes[i].type == "dataServer") {
                        var toolMap = d3.map();
                        toolMap.set("connId", d.connServer);
                        toolMap.set("id", d.id);
                        toolMap.set("type", "Web server");
                        tooltipMap.set(d.id, toolMap);
                    }
                }
                /** My code ends here */

            });

            // Update the nodes…
            node = svgGroup.selectAll("g.node").data(nodes, function(d) {
                return d.id || (d.id = ++i);
            });

            // Enter any new nodes at the parent's previous position.
            var nodeEnter = node.enter().append("g").call(dragListener).attr("class", "node").attr("transform",
                    function(d) {
                        return "translate(" + source.y0 + "," + source.x0 + ")";
                    }).on('click', click);

            // Adding the outer circle to highlight the connected servers
            nodeEnter.append("circle").attr({
                r : 15
            }).attr("id", function(d, i) {
                if (nodes[i].type == "appServer") {
                    return "outer" + nodes[i].id;
                } else if (nodes[i].type == "webServer") {
                    return "outer" + nodes[i].id;
                } else if (nodes[i].type == "dataServer") {
                    return "outer" + nodes[i].id;
                }
            }).style("fill", "transparent").style("stroke-width", function(d, i) {
                if (nodes[i].type == "appServer") {
                    return "1";
                } else if (nodes[i].type == "webServer") {
                    return "1";
                } else if (nodes[i].type == "dataServer") {
                    return "1";
                } else {
                    return "0";
                }
            }).style("stroke", "#fff");

            nodeEnter.append("circle").attr("r", 5).attr("id", function(d, i) {
                if (nodes[i].type == "appServer") {
                    return nodes[i].id;
                } else if (nodes[i].type == "webServer") {
                    return nodes[i].id;
                } else if (nodes[i].type == "dataServer") {
                    return nodes[i].id;
                }
            }).style("filter", function(d, i) {
                if (nodes[i].type == "vm") {
                    return "url(#virtualMac)";
                } else if (nodes[i].type == "container") {
                    return "url(#container)";
                } else if (nodes[i].type == "appServer") {
                    return "url(#appserver)";
                } else if (nodes[i].type == "webServer") {
                    return "url(#webserver)";
                } else if (nodes[i].type == "sto") {
                    return "url(#storage)";
                } else if (nodes[i].type == "dataServer") {
                    return "url(#dbserver)";
                } else if (nodes[i].type == "network") {
                    return "url(#network)";
                } else if (nodes[i].type == "lb") {
                    return "url(#loadbalancer)";
                }
            }).style("fill", function(d) {
                return d._children ? "lightsteelblue" : "#fff";
            }).on("mouseover", function(d) {
                getConServers(this, d3.event, "enter");
            }).on("mouseout", function(d) {
                getConServers(this, d3.event, "exit");
            });

            nodeEnter.append("text").attr("x", function(d) {
                return d.children || d._children ? -10 : 10;
            }).attr("dy", ".35em").attr('class', 'nodeText').attr("text-anchor", function(d) {
                return d.children || d._children ? "end" : "start";
            }).text(function(d) {
                return d.name;
            }).style("fill-opacity", 0);

            // phantom node to give us mouseover in a radius around it
            nodeEnter.append("circle").attr('class', 'ghostCircle').attr("r", 30).attr("opacity", 0.2) // change
            // this
            // to zero to
            // hide the
            // target area
            .style("fill", "red").attr('pointer-events', 'mouseover').on("mouseover", function(node) {
                overCircle(node);
            }).on("mouseout", function(node) {
                outCircle(node);
            });

            // Update the text to reflect whether node has children or
            // not.
            node.select('text').attr("x", function(d) {
                return d.children || d._children ? -10 : 10;
            }).attr("text-anchor", function(d) {
                return d.children || d._children ? "end" : "start";
            }).text(function(d) {
                return d.name;
            });

            // Change the circle fill depending on whether it has
            // children and is collapsed
            node.select("circle.nodeCircle").attr("r", 4.5).style("fill", function(d) {
                return d._children ? "lightsteelblue" : "#fff";
            });

            // Transition nodes to their new position.
            var nodeUpdate = node.transition().duration(duration).attr("transform", function(d) {
                return "translate(" + (d.x - 8) + "," + -d.y + ")";
            });

            // Fade the text in
            nodeUpdate.select("text").style("fill-opacity", 1);

            // Transition exiting nodes to the parent's new position.
            var nodeExit = node.exit().transition().duration(duration).attr("transform", function(d) {
                return "translate(" + source.y + "," + source.x + ")";
            }).remove();

            nodeExit.select("circle").attr("r", 0);

            nodeExit.select("text").style("fill-opacity", 0);

            // Update the links…
            var link = svgGroup.selectAll("path.link").data(links, function(d) {
                return d.target.id;
            });

            // Enter any new links at the parent's previous position.
            link.enter().insert("path", "g").attr("class", "link").attr("stroke-dasharray", function(d) {
                return (d.source.parent) ? "6,6" : "1,0";
            }).attr("d", function(d) {
                var o = {
                    x : source.x0,
                    y : source.y0
                };
                return diagonal({
                    source : o,
                    target : o
                });
            });

            // Transition links to their new position.
            link.transition().duration(duration).attr("d", diagonal);

            // Transition exiting nodes to the parent's new position.
            link.exit().transition().duration(duration).attr("d", function(d) {
                var o = {
                    x : source.x,
                    y : source.y
                };
                return diagonal({
                    source : o,
                    target : o
                });
            }).remove();

            // Stash the old positions for transition.
            nodes.forEach(function(d) {
                d.x0 = d.x;
                d.y0 = d.y;
            });
        }

        // Append a group which holds all nodes and which the zoom
        // Listener can act upon.

        var svgGroup = baseSvg.append("g");

        // Define the root
        root = treeData;
        root.x0 = viewerHeight / 2;
        root.y0 = 0;

        // Layout the tree initially and center on the root node.
        update(root);
        centerNode(root);
        addLoadSLAButtons();

    });

    function addLoadSLAButtons() {
        var slaDiv = document.createElement("div");
        slaDiv.setAttribute("id", "slaDiv");
        slaDiv.style.position = "relative";
        slaDiv.style.left = "35px";
        slaDiv.style.top = "-250px";
        slaDiv.style.width = "100px";

        var slaButton = document.createElement("input");
        slaButton.setAttribute("type", "button");
        slaButton.setAttribute("value", "View/Modify ServiceContract");
        slaButton.setAttribute("class", "btn btn-info");
        slaButton.setAttribute("id", "addSla");
        slaButton.style.position = "absolute";
        slaButton.setAttribute("onclick", "");// this will be modified once we
        // receive clarification on the
        // functionality.

        document.getElementById("tree-container").appendChild(slaDiv);
        document.getElementById("slaDiv").appendChild(slaButton);

        var relDiv = document.createElement("div");
        relDiv.setAttribute("id", "relDiv");
        relDiv.style.position = "relative";
        relDiv.style.left = "35px";
        relDiv.style.top = "-190px";
        relDiv.style.width = "100px";

        var loadButton = document.createElement("input");
        loadButton.setAttribute("type", "button");
        loadButton.setAttribute("value", "Add Load");
        loadButton.setAttribute("class", "btn btn-cust-info");
        loadButton.setAttribute("id", "addload");
        loadButton.style.position = "absolute";
        loadButton.style.align = "left";

        loadButton.setAttribute("onclick", ""); // reLoadPage()

        document.getElementById("tree-container").appendChild(relDiv);
        document.getElementById("relDiv").appendChild(loadButton);

    }

最佳答案

更好的方法是使用 separation() 方法。请查看文档了解详细信息,原则上使用该方法定义节点之间的最小距离,但在您的情况下,您需要限制仅对某些节点进行 gapos,这就是为什么在 separation()) 中有条件

var tree = d3.layout.tree()
    .separation(function(a, b) { return ((a.parent == root) && (b.parent == root)) ? 3 : 1; })
    .size([height, width - 160]);

下图是没有调用 separation() 的代码行的树:(我在前面的示例中添加了节点 AA 和 ZZ,还有 BB 和 CC,但这些节点至少在这种情况下显示为 OK)

...这里是那一行:

现场 jsfiddles:

herehere

只是为了好玩,我进一步修改了从“?3:1;})”到“?5:.6;})”的行,我得到了:

关于javascript - 增加我的 D3 树布局的节点之间的差距,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20797521/

有关javascript - 增加我的 D3 树布局的节点之间的差距的更多相关文章

  1. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  2. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  3. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  4. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  5. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

  6. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

  7. jquery - 我的 jquery AJAX POST 请求无需发送 Authenticity Token (Rails) - 2

    rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送

  8. ruby - nanoc 和多种布局 - 2

    是否可以为特定(或所有)项目使用多个布局?例如,我有几个项目,我想对其应用两种不同的布局。一个是绿色的,一个是蓝色的(但是)。我想将它们编译到我的输出目录中的两个不同文件夹中(例如v1和v2)。我一直在玩弄规则和编译block,但我不知道这是怎么回事。因为,每个项目在编译过程中只编译一次,我不能告诉nanoc第一次用layout1编译,第二次用layout2编译。我试过这样的东西,但它导致输出文件损坏。compile'*'doifitem.binary?#don’tfilterbinaryitemselsefilter:erblayout'layout1'layout'layout2'

  9. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  10. [工业相机] 分辨率、精度和公差之间的关系 - 2

    📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年

随机推荐