Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
150 views
in Technique[技术] by (71.8m points)

javascript - Migrating d3.js tree from version 3 to 6 - what am I missing?

So I've been trying to migrate this great d3.js tree (http://bl.ocks.org/robschmuecker/7880033) from d3.js version 3 to version 6. Currently I am trying to get the drag and drop working (see below for the code). But It just doesn't work the way it is supposed to be. After rearranging a node via drag and drop, the tree isn't properly drawn. What's more, drag and drop doesn't seem to work for all nodes (except for the root node, which is intended). I couldn't find out so far why this happens. I suspect that the problem could be that the tree data is not properly updated after updating the children. Unfortunately, not many tutorials for d3.js are based on version 6 (at least to my knowledge) so I wasn't really able to find much useful information.

Any tips would be appreciated. Thanks!

<!DOCTYPE html>
<html lang="en">
<head>
    <title>d3.js tree V6</title>
    <meta charset="utf-8"/>
    <script src="https://d3js.org/d3.v6.min.js"></script>
    <style>
        body {
            font-family: Arial;
        }
        #title {
            font-size: 30px;
            margin-top: 20px;
        }
        .container {
            margin: 0 auto;
        }
        .text-center {
            text-align: center;
        }

        .link {
            fill: none;
            stroke: #ccc;
            stroke-width: 1.5px;
        }

        svg {
            display: block;
            margin: auto;
        }

        .node {
            cursor: pointer;
        }

        .ghostCircle.show{
            display:block;
        }

        .ghostCircle, .activeDrag .ghostCircle{
            display: none;
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- title -->
        <h1 class="text-center" id="title">d3.js tree V6</h1>
        <div id="concept-tree">
        </div>
    </div>


    <script>
        var treeData = {
                "name": "Top Level",
                "parent": "null",
                "children": [
                    {
                        "name": "Level 2: A",
                        "parent": "Top Level",
                        "children": [
                            {
                                "name": "Son of A",
                                "parent": "Level 2: A"
                            },
                            {
                                "name": "Daughter of A",
                                "parent": "Level 2: A"
                            }
                        ]
                    },
                    {
                        "name": "Level 2: B",
                        "parent": "Top Level"
                    }
                ]
            };

        // +++ INITIALIZATIONS +++ //
        var margin = {top: 20, right: 90, bottom: 30, left: 90},
            width = 1500 - margin.left - margin.right,
            height = 750 - margin.top - margin.bottom;

        var duration = 750;

        const svg = d3.select("#concept-tree").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom),
            g = svg.append("g")
                .attr("transform",
                    "translate(" + margin.left + "," + margin.top + ")");

        root = d3.hierarchy(treeData, function(d) { return d.children; });
        root.x0 = height / 2;
        root.y0 = 0;

        // declares a tree layout and assigns the size
        var treemap = d3.tree().size([height, width]);

        var i = 0;

        var selectedNode = null;
        var draggingNode = null;


        // +++ FUNCTIONS +++ //
        function diagonal(s, d) {

            path = `M ${s.y} ${s.x}
            C ${(s.y + d.y) / 2} ${s.x},
              ${(s.y + d.y) / 2} ${d.x},
              ${d.y} ${d.x}`

            return path
        }

        svg.call(d3.zoom()
            .extent([[0, 0], [width, height]])
            .scaleExtent([1, 8])
            .on("zoom", zoomed));

        function zoomed({transform}) {
            g.attr("transform", transform);
        }

        // collapse the node and all it's children
        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 centerNode(source) {
            /*scale = zoomEvent.scale();
            x = -source.y0;
            y = -source.x0;
            x = x * scale + viewerWidth / 2;
            y = y * scale + viewerHeight / 2;
            d3.select('g').transition()
                .duration(duration)
                .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")");
            zoomListener.scale(scale);
            zoomListener.translate([x, y]);*/
        }

        // toggle children on click
        function click(event, d) {
            if (event.defaultPrevented) return;

            if (d.children) {
                d._children = d.children;
                d.children = null;
            } else {
                d.children = d._children;
                d._children = null;
            }
            update(d);
            centerNode(d);
        }

        // for dragging nodes
        function dragEvent() {
            function dragstarted(event, d) {
                if (d == root) {
                    return
                }
                event.sourceEvent.stopPropagation();
            }
            function dragged(event, d) {
                var nodes = root.descendants();
                if (d == root) {
                    return
                }
                var domNode = this;
                initiateDrag(d, domNode);

                // move node
                d.x0 += event.dy;
                d.y0 += event.dx;
                var node = d3.select(this);
                node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")");
            }

            function dragended(event, d) {
                if (d == root) {
                    return
                }

                var domNode = this;

                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(draggingNode);
                        centerNode(draggingNode);
                        draggingNode = null;
                    }
                }
                if (selectedNode) {
                    console.log(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();
                }
            }

            return d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended);
        }


        function initiateDrag(d, domNode) {
            var nodes = d.descendants().slice(1),
                links = d.descendants();

            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');

            // remove link paths
            g.selectAll("path.link")
                .data(links, function(d) {
                    return d.id;
                }).remove();
            // remove child nodes
            g.selectAll("g.node")
                .data(nodes, function(d) {
                    return d.id;
                }).remove();
        }

        // main function
        function update(source) {

            // Assigns the x and y position for the nodes
            var treeData = treemap(root);

            // Compute the new tree layout.
            var nodes = treeData.descendants(),
                links = treeData.descendants().slice(1);

            // +++ NODES +++ //
            // update nodes
            var node = g.selectAll("g.node")
                .data(nodes, function(d) {
                    return d.id || (d.id = ++i);
                });

            // enter nodes
            var nodeEnter = node.enter().append("g")
                .attr("class", "node")
                .call(dragEvent())
                .attr("transform", function(d) {
                    return "translate(" + source.y0 + "," + source.x0 + ")";
                })
                .on("click", click);

            nodeEnter.append("circle")
                .attr('class', 'nodeCircle')
                .attr("r", 5)
                .style("fill", function(d) {
                    return d._children ? "blue" : "lightsteelblue";
                });

            nodeEnter.append("text")
                .attr("x", function(d) {
                    return d.children || d._children ? -10 : 10;
                })
                .att

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...