Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3v4 - zooming (Equivalent to d3.zoom.x)

I'm migrating my d3 code from v3 to v4 and having issues finding an equivalent for the d3.zoom.x, d3.zoom.y properties.

Here's a short part of the code, including the most important elements:

            this.init = function(obj, def) {

            /* X-axis */
            x = d3.scaleTime()
                .range([0, width]);


            xAxis = d3.axisBottom(x)
                .ticks(ticks);

            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + height + ")");

            /* Y-axis */
            for (var i = 0; i < 1; i++) {
                // Returns the y and yAxis as arrays
                createYAxis(i);
            }

            initZoom();

        };
            // Zoom trigger
            var drawUpdXY = function(){
                setPause(true);
                drawUpd();
                zoomY.y(y[0]);
                zoomX.x(x);


            zoom = d3.zoom()
                .on("zoom", drawUpdXY);

            zoomX = d3.zoom()
                .on("zoom", drawUpdX);

            zoomY = d3.zoom()
                .on("zoom", drawUpdY);
            };     

I've tried to use this to replace "zoom.x(x); zoom.y(y[0])"

      xAxis.scale(d3.event.transform.rescaleX(x));
      yAxis[0].scale(d3.event.transform.rescaleY(y[0]));

However, these only rescale the axis, leaving the data non-zommed.

like image 500
Peter Buju Avatar asked Sep 08 '16 09:09

Peter Buju


1 Answers

I can't speak to how d3 v3 handled zoom, since I've only recently started working with D3, but the code below should get you pointed in the right direction. I'll do my best to describe what's going. I invite corrections, improvements, and better explanations.

First, a MCVE

<html>

<head>
  <meta charset="UTF-8">
  <title>Energy Visualizer</title>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/d3-color.v1.min.js"></script>
  <script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
  <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
  <style>
    body {
      background-color: #4e5965;
    }
  </style>
  </svg>
</head>

<body>
  <div class="chart" />
  <script>
    var svg = d3.select(".chart").append("svg")
      .attr("width", 200)
      .attr("height", 200);

    var data = [{
      "x": 2,
      "y": 8,
      "color": "green"
    }, {
      "x": 4,
      "y": 6,
      "color": "red"
    }];

    var width = svg.attr("width");
    var height = svg.attr("height");

    x_scale = d3.scaleLinear().domain([0, 10]).range([0, width]);
    y_scale = d3.scaleLinear().domain([0, 10]).range([0, height]);

    var circles = svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return x_scale(d.x);
      })
      .attr("cy", function(d) {
        return y_scale(d.y);
      })
      .attr("r", 5)
      .attr("fill", function(d) {
        return d.color
      });

    var xAxis = d3.axisBottom(x_scale);
    var yAxis = d3.axisRight(y_scale);

    var x_axis = svg.append("g").call(xAxis);
    var y_axis = svg.append("g").call(yAxis);
  </script>
</body>

</html>

This is just a simple scatter plot with a red and green circle. You can see how I've broken out the components of the x and y axis. This comes into play with d3.zoom().

Next, let's add a zoom component.

<html>

<head>
  <meta charset="UTF-8">
  <title>Energy Visualizer</title>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/d3-color.v1.min.js"></script>
  <script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
  <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
  <style>
    body {
      background-color: #4e5965;
    }
  </style>
  </svg>
</head>

<body>
  <div class="chart" />
  <script>
    var svg = d3.select(".chart").append("svg")
      .attr("width", 200)
      .attr("height", 200);

    var zoom = d3.zoom()
      .on("zoom", zoomed);

    svg.call(zoom);

    var data = [{
      "x": 2,
      "y": 8,
      "color": "green"
    }, {
      "x": 4,
      "y": 6,
      "color": "red"
    }];

    var width = svg.attr("width");
    var height = svg.attr("height");

    x_scale = d3.scaleLinear().domain([0, 10]).range([0, width]);
    y_scale = d3.scaleLinear().domain([0, 10]).range([0, height]);

    var circles = svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return x_scale(d.x);
      })
      .attr("cy", function(d) {
        return y_scale(d.y);
      })
      .attr("r", 5)
      .attr("fill", function(d) {
        return d.color
      });

    var xAxis = d3.axisBottom(x_scale);
    var yAxis = d3.axisRight(y_scale);

    var x_axis = svg.append("g").call(xAxis);
    var y_axis = svg.append("g").call(yAxis);


    function zoomed() {
      var new_x_scale = d3.event.transform.rescaleX(x_scale);
      var new_y_scale = d3.event.transform.rescaleY(y_scale);

      x_axis.transition()
        .duration(0)
        .call(xAxis.scale(new_x_scale));

      y_axis.transition()
        .duration(0)
        .call(yAxis.scale(new_y_scale));

      circles
        .attr("cx", function(d) {
          return new_x_scale(d.x)
        })
        .attr("cy", function(d) {
          return new_y_scale(d.y)
        });

    }
  </script>
</body>

</html>

Here, you can see that chart allows zoom-in, zoom-out and dragging across both axes. I beleive you are wanting only a single axis zoom, so in that case, only rescale the x-axis and the x component of each circles position.

One last snippet doing what I think you want.

<html>

<head>
  <meta charset="UTF-8">
  <title>Energy Visualizer</title>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/d3-color.v1.min.js"></script>
  <script src="https://d3js.org/d3-interpolate.v1.min.js"></script>
  <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
  <style>
    body {
      background-color: #4e5965;
    }
  </style>
  </svg>
</head>

<body>
  <div class="chart" />
  <script>
    var svg = d3.select(".chart").append("svg")
      .attr("width", 200)
      .attr("height", 200);

    var zoom = d3.zoom()
      .on("zoom", zoomed);

    svg.call(zoom);

    var data = [{
      "x": 2,
      "y": 8,
      "color": "green"
    }, {
      "x": 4,
      "y": 6,
      "color": "red"
    }];

    var width = svg.attr("width");
    var height = svg.attr("height");

    x_scale = d3.scaleLinear().domain([0, 10]).range([0, width]);
    y_scale = d3.scaleLinear().domain([0, 10]).range([0, height]);

    var circles = svg.selectAll("circle")
      .data(data)
      .enter()
      .append("circle")
      .attr("cx", function(d) {
        return x_scale(d.x);
      })
      .attr("cy", function(d) {
        return y_scale(d.y);
      })
      .attr("r", 5)
      .attr("fill", function(d) {
        return d.color
      });

    var xAxis = d3.axisBottom(x_scale);
    var yAxis = d3.axisRight(y_scale);

    var x_axis = svg.append("g").call(xAxis);
    var y_axis = svg.append("g").call(yAxis);


    function zoomed() {
      var new_x_scale = d3.event.transform.rescaleX(x_scale);

      x_axis.transition()
        .duration(0)
        .call(xAxis.scale(new_x_scale));

      circles
        .attr("cx", function(d) {
          return new_x_scale(d.x)
        });

    }
  </script>
</body>

</html>
like image 102
Austin A Avatar answered Oct 29 '22 16:10

Austin A