Search code examples
d3.jszoomingcenterpan

d3 click to center content at position of element or click


I have been trying to get the basics of how I make a pannable zoomable, and click to center zoom on element d3 work. This example is what I want to do but I am having trouble translating it outside of the geo context: https://bl.ocks.org/mbostock/2206340

What I have accomplished is the first two parts pan and zoom, see a basic fiddle here https://jsfiddle.net/e9fbn2xp/

How can I accomplish centering the the circle in the center of the viewable window, so it looks like the circle is zoomed to? Note that although this is a fixed position circle I will eventually have dynamic data, so ideally I could reference the circles position dynamically.

Here is my code:

HTML (note that this is React JSX syntax but that should be irrelevant to question)

     <div style={{width: 800}}>
            <svg style={{border: '1px solid black'}} id="viz" width="800" height="800">

            </svg>
        </div>

JAVASCRIPT

    var svg = d3.select("#viz")
    var width = svg.attr("width");
    var height = svg.attr("height");

    var testLayer = svg.append('g');     
    var aRect = testLayer.append("rect")     
    .attr("x", 0)         
    .attr("y", 0)          
    .attr("height", 800)    
    .attr("width", 800)  
    .attr("fill", 'green');

    var aCircle = testLayer.append("circle")
    .style("stroke", "gray")
    .style("fill", "white")
    .attr("r", 40)
    .attr("cx", 200)
    .attr("cy", 200)
    .on("mousedown", zoomToMe);

    function zoomToMe(){
        console.log("do the zoom")
    }

    var zoom = d3.zoom()
    .scaleExtent([.5, 40])
    .translateExtent([[0, 0], [width, height]])
    .on("zoom", zoomed);

    svg.call(zoom);

    function zoomed() {
    testLayer.attr("transform", d3.event.transform);
    }

    svg.on("click", function() {
        var coords = d3.mouse(this);
    })

Solution

  • I got a working solution and thought I would share the code in case others find it useful. It is a fairly different approach then my original but accomplishes the three goals, pan, mouse zoom, zoom to element. While these are three simple static circles the same concept should work with a dynamic dataset.

    fiddle: https://jsfiddle.net/uc7oprx3/5/

    HTML

    <svg id="viz" width="400" height="400" />
    

    JAVASCRIPT

        var zoom = d3.zoom()
        .scaleExtent([0.3,2])
        .on("zoom", zoomed);
    
        var svg = d3.select("#viz")
    
        var width = svg.attr("width");
        var height = svg.attr("height");
    
        var zoomer = svg.append("rect")
        .attr("width", width)
        .attr("height", height)
        .style("fill", "none")
        .style("pointer-events", "all")
        .call(zoom);
    
        var g = svg.append("g");
    
        var aCircle = g.append("circle")
        .style("stroke", "gray")
        .style("fill", "white")
        .attr("r", 40)
        .attr("cx", 200)
        .attr("cy", 200)
        .on("mousedown", () => centerNode(200, 200));
    
        var bCircle = g.append("circle")
        .style("stroke", "gray")
        .style("fill", "white")
        .attr("r", 40)
        .attr("cx", 400)
        .attr("cy", 400)
        .on("mousedown",  () => centerNode(400, 400));
    
        var cCircle = g.append("circle")
        .style("stroke", "gray")
        .style("fill", "white")
        .attr("r", 40)
        .attr("cx", 600)
        .attr("cy", 600)
        .on("mousedown",  () => centerNode(600, 600));
    
        function zoomed() {
        g.attr("transform", d3.event.transform);
        }
    
        function centerNode(xx, yy){
        g.transition()
        .duration(500)
        .attr("transform", "translate(" + (width/2 - xx) + "," + (height/2 - yy) + ")scale(" + 1 + ")")
        .on("end", function(){ zoomer.call(zoom.transform, d3.zoomIdentity.translate((width/2 - xx),(height/2 - yy)).scale(1))});
        }