Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent panning outside of map bounds in d3v5

in d3 v3, I used this example to prevent panning outside of an SVG. Here's the relevant code:

.on("zoom", function() {
    // the "zoom" event populates d3.event with an object that has
    // a "translate" property (a 2-element Array in the form [x, y])
    // and a numeric "scale" property
    var e = d3.event,

    // now, constrain the x and y components of the translation by the
    // dimensions of the viewport
    tx = Math.min(0, Math.max(e.translate[0], width - width * e.scale)),
    ty = Math.min(0, Math.max(e.translate[1], height - height * e.scale));

    // then, update the zoom behavior's internal translation, so that
    // it knows how to properly manipulate it on the next movement
    zoom.translate([tx, ty]);

    // and finally, update the <g> element's transform attribute with the
    // correct translation and scale (in reverse order)
    g.attr("transform", ["translate(" + [tx, ty] + ")","scale(" + e.scale + ")"].join(" "));
}

In d3 v5, it doesn't work anymore. All the examples allow the map to pan ridiculous amounts off screen. My goal is that the rightmost edge of the map never goes further left than the rightmost edge of the div, etc. How can I accomplish this? Are there any more recent examples?

like image 861
whiterook6 Avatar asked Nov 24 '25 19:11

whiterook6


1 Answers

For the example you linked, these are the necessary changes inside the zoom function for that to work with D3 v5:

var e = d3.event.transform,
tx = Math.min(0, Math.max(e.x, width - width * e.k)),
ty = Math.min(0, Math.max(e.y, height - height * e.k));

Besides that, change the group translate function and remove zoom.translate([tx, ty]);.

Here is the original code with those changes:

<html>

<head>
  <title>Restricted zoom behavior in d3</title>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v5.min.js"></script>
  <style>

  </style>
</head>

<body>
  <script>
    // first, define your viewport dimensions
    var width = 960,
      height = 500;

    // then, create your svg element and a <g> container
    // for all of the transformed content
    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height)
      .style("background-color", randomColor),
      g = svg.append("g");

    // then, create the zoom behvavior
    var zoom = d3.zoom()
      // only scale up, e.g. between 1x and 50x
      .scaleExtent([1, 50])
      .on("zoom", function() {
        // the "zoom" event populates d3.event with an object that has
        // a "translate" property (a 2-element Array in the form [x, y])
        // and a numeric "scale" property
        var e = d3.event.transform,
          // now, constrain the x and y components of the translation by the
          // dimensions of the viewport
          tx = Math.min(0, Math.max(e.x, width - width * e.k)),
          ty = Math.min(0, Math.max(e.y, height - height * e.k));
        // then, update the zoom behavior's internal translation, so that
        // it knows how to properly manipulate it on the next movement
        // and finally, update the <g> element's transform attribute with the
        // correct translation and scale (in reverse order)
        g.attr("transform", [
          "translate(" + [tx, ty] + ")",
          "scale(" + e.k + ")"
        ].join(" "));
      });

    // then, call the zoom behavior on the svg element, which will add
    // all of the necessary mouse and touch event handlers.
    // remember that if you call this on the <g> element, the even handlers
    // will only trigger when the mouse or touch cursor intersects with the
    // <g> elements' children!
    svg.call(zoom);

    // then, let's add some circles
    var circle = g.selectAll("circle")
      .data(d3.range(300).map(function(i) {
        return {
          x: Math.random() * width,
          y: Math.random() * height,
          r: .01 + Math.random() * 50,
          color: randomColor()
        };
      }).sort(function(a, b) {
        return d3.descending(a.r, b.r);
      }))
      .enter()
      .append("circle")
      .attr("fill", function(d) {
        return d.color;
      })
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .attr("r", function(d) {
        return d.r;
      });

    function randomColor() {
      return "hsl(" + ~~(60 + Math.random() * 180) + ",80%,60%)";
    }
  </script>
</body>

</html>
like image 104
Gerardo Furtado Avatar answered Nov 27 '25 07:11

Gerardo Furtado



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!