rveciana - d3js-v4-map-zooming

D3js v4 map zooming

Open raw page in new tab

I didn’t find a good example on zooming a map with d3 version 4.

The idea is that when moving the mouse wheel, the location pointed by the mouse stays at the same place to avoid the weird sensation that happens when the map is not translated and it’s zoomed at the current center.

The code isn’t very nice, but I think that explains well a working solution. It would be better using the d3-zoom, but I didn’t find the way.

<!DOCTYPE html>
<meta charset="utf-8">

.background {
  fill: none;
  pointer-events: all;

#states {
  fill: #aaa;

#state-borders {
  fill: none;
  stroke: #000;
  stroke-width: 1.5px;
  stroke-linejoin: round;
  stroke-linecap: round;
  pointer-events: none;

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>

var width = 960,
    height = 500;

var projection = d3.geoMercator()
    .center([2.8, 41.9])
    .translate([width/2, height/2]);

var path = d3.geoPath()

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var g = svg.append("g")
        var currScale = projection.scale();
        var newScale = currScale - 2*event.deltaY;
        var currTranslate = projection.translate();
        var coords = projection.invert([event.offsetX, event.offsetY]);
        var newPos = projection(coords);

        projection.translate([currTranslate[0] + (event.offsetX - newPos[0]), currTranslate[1] + (event.offsetY - newPos[1])]);
        g.selectAll("path").attr("d", path);

    .call(d3.drag().on("drag", function(){
        var currTranslate = projection.translate();
        projection.translate([currTranslate[0] + d3.event.dx,
                              currTranslate[1] + d3.event.dy]);
        g.selectAll("path").attr("d", path);

    .attr("class", "background")
    .attr("width", width)
    .attr("height", height)

d3.json("lim.json", function(error, us) {
  if (error) throw error;

      .data(topojson.feature(us, us.objects.limits).features)
      .attr("id", "state-borders")
      .attr("d", path);