javascript 更改数据后,强制图形不更新

ryhaxcpt  于 2023-01-11  发布在  Java
关注(0)|答案(1)|浏览(114)

我得到了一个带有3个主节点的D3强制图形。这些节点包含一个属性 * shoes *,它保存一个整数。图形顶部有两个按钮,用于 * add * 或 * remove * shoes。只要单击其中一个按钮,我就想更新D3强制图形数据。基本上,蓝色节点中的整数值应该增加或减少。
我搜索并找到了几篇StackOverflow文章,其中解释了实现我的需求的步骤。不幸的是,我还不能成功地将这些文章Map到我的原型上。

    • 问题是**:它向最后一个数据节点添加了一个元素,但并不改变蓝色圆圈中的数量,而是在控制台输出中显示正确的值,并正确地增加或减少鞋子的数量。

我错过了什么?

var width = window.innerWidth,
            height = window.innerHeight;

        var buttons = d3.select("body").selectAll("button")
            .data(["add Shoes", "remove Shoes"])
            .enter()
            .append("button")
            .text(function(d) {
                return d;
            })

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height)
            .call(d3.zoom().on("zoom", function(event) {
                svg.attr("transform", event.transform)
            }))
            .append("g")

        ////////////////////////
        // outer force layout

        var data = {
            "nodes":[
                { "id": "A", "shoes": 1}, 
                { "id": "B", "shoes": 1},
                { "id": "C", "shoes": 0},
            ],
            "links": [
                { "source": "A", "target": "B"},
                { "source": "B", "target": "C"},
                { "source": "C", "target": "A"}
            ]
        };

        var simulation = d3.forceSimulation()
            .force("size", d3.forceCenter(width / 2, height / 2))
            .force("charge", d3.forceManyBody().strength(-1000))
            .force("link", d3.forceLink().id(function (d) { return d.id }).distance(250))
       
        linksContainer = svg.append("g").attr("class", "linkscontainer")
        nodesContainer = svg.append("g").attr("class", "nodesContainer")
       
        var links = linksContainer.selectAll("g")
            .data(data.links)
            .join("g")
            .attr("fill", "transparent")

        var linkLine = linksContainer.selectAll(".linkPath")
            .data(data.links)
            .join("path")
            .attr("stroke", "red")
            .attr("fill", "transparent")
            .attr("stroke-width", 3)
        
        nodes = nodesContainer.selectAll(".nodes")
            .data(data.nodes, function (d) { return d.id; })
            .join("g")
            .attr("class", "nodes")
            .attr("id", function (d) { return d.id; })
            .call(d3.drag()
                .on("start", dragStarted)
                .on("drag", dragged)
                .on("end", dragEnded)
            )

        nodes.selectAll("circle")
            .data(d => [d])
            .join("circle")
            .style("fill", "lightgrey")
            .style("stroke", "blue")
            .attr("r", 40)

        var smallCircle = nodes.selectAll("g")
            //.data(d => d.shoes)
            .data(d => [d])
            .enter()
            .filter(function(d) { 
                return d.shoes !== 0; 
            })
            .append("g")
            .attr("cursor", "pointer")
            .attr("transform", function(d, i) {
                const factor = (i / 40) * (15 / 2) * 5;
                return `translate(${40 * Math.cos(factor - Math.PI * 0.5)},${40 * Math.sin(factor - Math.PI * 0.5)})`;
            });
                        
        smallCircle.append('circle')
            .attr("class", "circle-small")
            .attr('r', 15)
            .attr("fill", "blue")

        smallCircle.append("text")
            .attr("font-size", 15)
            .attr("fill", "white")
            .attr("dominant-baseline", "central")
            .style("text-anchor", "middle")
            .attr("pointer-events", "cursor")
            .text(function(d) {
                return d.shoes;
            })

        simulation
            .nodes(data.nodes)
            .on("tick", tick)

        simulation
            .force("link")
            .links(data.links)

        function tick() {
            linkLine.attr("d", function(d) {
                var dx = (d.target.x - d.source.x),
                    dy = (d.target.y - d.source.y),
                    dr = Math.sqrt(dx * dx + dy * dy)

                return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
            })
                
            nodes
                .attr("transform", d => `translate(${d.x}, ${d.y})`);
        }

        function dragStarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragEnded(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }

        buttons.on("click", function(d) {
            if (d.srcElement.__data__ == "add Shoes") {
                
                data.nodes.forEach(function(item) {
                    item.shoes = item.shoes + 1
                })
            } else if (d.srcElement.__data__ == "remove Shoes") {
                data.nodes.forEach(function(item) {
                    if (!item.shoes == 0) {
                        item.shoes = item.shoes - 1
                    }
                })
            }

            restart()
        })

        function restart() {
            // Apply the general update pattern to the nodes.
    
            smallCircle = nodes.selectAll("g")
                .data(d => [d])
                .enter()
                .filter(function(d) { 
                    return d.shoes !== 0; 
                })
                .append("g")
                .attr("cursor", "pointer")
                .attr("transform", function(d, i) {
                    const factor = (i / 40) * (15 / 2) * 5;
                    return `translate(${40 * Math.cos(factor - Math.PI * 0.5)},${40 * Math.sin(factor - Math.PI * 0.5)})`;
                });

            smallCircle.append("circle")
                .attr("class", "circle-small")
                .attr('r', 15)
                .attr("fill", "blue")

            smallCircle.append("text")
                .attr("font-size", 15)
                .attr("fill", "white")
                .attr("dominant-baseline", "central")
                .style("text-anchor", "middle")
                .text(function(d) {
                    return d.shoes;
                })

            smallCircle.exit().remove();

            // Update and restart the simulation.
            simulation.nodes(data.nodes);
            simulation.restart()
        }
body {
        background: whitesmoke,´;
        overflow: hidden;
        margin: 0px;
    }
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>D3v7</title>
    <!-- d3.js framework -->
    <script src="https://d3js.org/d3.v7.js"></script>
</head>

<body>
 
</body>

</html>
bvn4nwqk

bvn4nwqk1#

首先,不要乱用私有变量,通常赋值为__foo__

buttons.on("click", function(d) {
    if (d.srcElement.__data__ == "add Shoes") { etc...

......只需:

buttons.on("click", function(_, d) {
    if (d == "add Shoes") {

回到问题上来:这里的问题是输入-更新-退出模式不正确。应该是:

//the update selection:
let smallCircle = nodes.selectAll("g")
    .data(d => d.shoes ? [d] : []);

//the exit selection:
smallCircle.exit().remove();

//the enter selection:
const smallCircleEnter = smallCircle.enter()
    .append("g")
    //etc...

//appending elements in the enter selection only:
smallCircleEnter.append("circle")
    //etc...

smallCircleEnter.append("text")
    //etc...

//merging the enter and the update selections:
smallCircle = smallCircleEnter.merge(smallCircle);

//modifying the update selection
smallCircle.select("text")
    .text(function(d) {
      return d.shoes;
    });

此外,如果鞋子的数量为零,我将移除该过滤器并传递一个空数组。
下面是经过这些更改的代码:
一个一个三个一个一个一个一个一个四个一个一个一个一个一个五个一个

相关问题