D3力图-缩放至节点

bxjv4tth  于 9个月前  发布在  其他
关注(0)|答案(1)|浏览(119)

我正在尝试建立一个'clickToZoom'函数。在节点单击时,视图应该关注被单击的节点。event.transform对象返回{k, x, y}。到目前为止,我认为我可以从被单击的节点接收这些值并设置svg.attr("transform", "newValues"),我确实这样做了。显然它不像预期的那样工作。
景色确实改变了,但似乎重新设定了。

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

        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)
                console.log(event.transform)
            }))
            .append("g")

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

        var data = {
            "nodes": [{
                "id": "A",
            },
            {
                "id": "B",
            },
            {
                "id": "C",
            },
            {
                "id": "D",
            },
            {
                "id": "E",
            },
            {
                "id": "F",
            },
            {
                "id": "G",
            },],
            "links": [{
                "source": "A",
                "target": "B"
            },
            {
                "source": "B",
                "target": "C"
            },
            {
                "source": "C",
                "target": "D"
            },
            {
                "source": "D",
                "target": "E"
            },
            {
                "source": "E",
                "target": "F"
            },
            {
                "source": "F",
                "target": "G"
            },]
        }

        var simulation = d3.forceSimulation()
            .force("size", d3.forceCenter(width / 2, height / 2))
            .force("charge", d3.forceManyBody().strength(-5000))
            .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")

        links = linksContainer.selectAll(".linkPath")
            .data(data.links)
            .enter()
            .append("path")
            .attr("class", "linkPath")
            .attr("stroke", "red")
            .attr("fill", "transparent")
            .attr("stroke-width", 3)

        nodes = nodesContainer.selectAll(".nodes")
            .data(data.nodes, function (d) {
                return d.id;
            })
            .enter()
            .append("g")
            .attr("class", "nodes")
            .call(d3.drag()
                .on("start", dragStarted)
                .on("drag", dragged)
                .on("end", dragEnded)
            )
            .on("click", function(d) {

                //d3.zoomTransform(d3.select(this))
                
                let nodeX = d.srcElement.__data__.x
                let nodeY = d.srcElement.__data__.y

                zoomToNode(nodeX, nodeY)                
            })

        nodes.selectAll("circle")
            .data(d => [d])
            .enter()
            .append("circle")
            .attr("class", "circle")
            .style("stroke", "blue")
            .attr("r", 40)

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

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

        function tick() {
            links.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;
        }

        function zoomToNode(thisX, thisY) {
            let transformValue = {"k": 2, "x": thisX, "y": thisY}

            console.log(transformValue)

            svg.attr("transform", transformValue)

            svg.attr("transform", event.transform)
        }
.link {
        stroke: #000;
        stroke-width: 1.5px;
    }

    .nodes {
        fill: whitesmoke;
        cursor: pointer;
    }

    .buttons {
        margin: 0 1em 0 0;
    }
<!DOCTYPE html>
<html lang="de">

<head>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
    <meta charset="utf-8">

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
    <!-- D3 -->
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>

<body>

</body>

</html>
svmlkihl

svmlkihl1#

这个example对于您想要的结果有合理的效用。基于此对您的原始更改是:

  • d3.zoom分配给变量,而不是在创建svg时调用它
  • 将附加到svg的第一个g分配给一个单独的变量,然后将linksContainernodesContainer附加到该g
  • 独立的zoom引用作用于此g,而不是svg

然后,您可以在点击处理程序中使用Observable中的代码(签名为function(event, d)):

svg.transition().duration(700).call(
    zoom.transform,
    d3.zoomIdentity.translate((width / 2), (height / 2)).scale(1.2).translate(-d.x, -d.y),
    d3.pointer(event)
);

字符串
"重置“是因为使用svg.attr("transform", ..some translation..)不会更新缩放,所以在设置此变换后再次平移/缩放时,它会重置为使用普通zoom时的最后一个点。
单击处理程序逻辑调用zoom.transform并将缩放设置为1.2(您可以调整),并相对于单击节点的xy进行平移。
如果在click处理程序中使用function(event, d)的签名,则可以比通过event更容易地引用d的坐标。

var width = 500,
    height = 200;

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

var g = svg.append("g")
svg.call(zoom)

////////////////////////
// outer force layout
var simulation = d3.forceSimulation()
    .force("size", d3.forceCenter(width / 2, height / 2))
    .force("charge", d3.forceManyBody().strength(-5000))
    .force("link", d3.forceLink().id(function (d) {
        return d.id
    }).distance(250))

linksContainer = g.append("g").attr("class", "linkscontainer")
nodesContainer = g.append("g").attr("class", "nodesContainer")

links = linksContainer.selectAll(".linkPath")
    .data(data.links)
    .enter()
    .append("path")
    .attr("class", "linkPath")
    .attr("stroke", "red")
    .attr("fill", "transparent")
    .attr("stroke-width", 3)

nodes = nodesContainer.selectAll(".nodes")
    .data(data.nodes, function (d) {
        return d.id;
    })
    .enter()
    .append("g")
    .attr("class", "nodes")
    .call(d3.drag()
        .on("start", dragStarted)
        .on("drag", dragged)
        .on("end", dragEnded)
    )
    .on("click", function(event, d) {
        //event.stopPropagation();
        svg.transition().duration(700).call(
            zoom.transform,
            d3.zoomIdentity.translate((width / 2), (height / 2)).scale(1.2).translate(-d.x, -d.y),
            d3.pointer(event)
        );
    })

nodes.selectAll("circle")
    .data(d => [d])
    .enter()
    .append("circle")
    .attr("class", "circle")
    .style("stroke", "blue")
    .attr("r", 40)

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

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

function tick() {
    links.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;
}
.link {
        stroke: #000;
        stroke-width: 1.5px;
    }

    .nodes {
        fill: whitesmoke;
        cursor: pointer;
    }

    .buttons {
        margin: 0 1em 0 0;
    }
<!DOCTYPE html>
<html lang="de">

<head>
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
    <meta charset="utf-8">

    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
    <!-- D3 -->
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <!-- fontawesome stylesheet https://fontawesome.com/ -->
    <script src="https://kit.fontawesome.com/98a5e27706.js" crossorigin="anonymous"></script>
</head>

<body>
<script>
var data = {
    "nodes": [{
        "id": "A",
    },
    {
        "id": "B",
    },
    {
        "id": "C",
    },
    {
        "id": "D",
    },
    {
        "id": "E",
    },
    {
        "id": "F",
    },
    {
        "id": "G",
    },],
    "links": [{
        "source": "A",
        "target": "B"
    },
    {
        "source": "B",
        "target": "D"
    },
    {
        "source": "C",
        "target": "F"
    },
    {
        "source": "D",
        "target": "A"
    },
    {
        "source": "E",
        "target": "B"
    },
    {
        "source": "F",
        "target": "A"
    },
    {
        "source": "F",
        "target": "G"
    },]
}
</script>

</body>

</html>

相关问题