Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3js: zooming when there are two y axes

reference: https://github.com/mbostock/d3/wiki/Zoom-Behavior

//make zoom
var zoomFirst = d3.behavior.zoom()
  .x(x)
  .y(y1)
  .scaleExtent([0, 3])
  .size([w, h])
  //.center([w/2+200, h/2-200])
  .on("zoom", zoomedFirst);

function zoomedFirst() {
  svgContainer.select(".x.axis").call(xAxis);
  svgContainer.selectAll(".y.axis.axisLeft").call(yAxisLeft);
  //set y2's scale manually
  svgContainer.select(".price")
    .attr("d", line1(priceData))
    .attr("class", "price");
  svgContainer.select(".difficulty")
    .attr("d", line2(difficultyData))
    .attr("class", "difficulty");
}

d3.behavior.zoom() supports autoscaling of x and y axes. However, I have to scale two y axes at the same time. When zoom() is triggered, I can get the current scale and translate info from d3.event.scale and d3.event.translate, but I cant figure out how to make appropriate scale for the second y axis(y2) with them.

I am also looking at https://github.com/mbostock/d3/wiki/Quantitative-Scales.

Since y1's range is automatically adjusted by zoom, if there is a way to get y1's current range, I can get its min and max and set y2's range based on them. However, the document doesn't specify a way to get range given a scale object.

like image 289
Maximus S Avatar asked Oct 02 '22 06:10

Maximus S


2 Answers

Calling y1.range() (without any arguments) will return you the [min, max] of the scale.

From the docs:

If values is not specified, returns the scale's current output range.

Most accessor functions in D3 work like this, they return you (get) the value if you call them without any arguments and set the value if you call them with arguments and return the this object for easy chaining:

d3Object.propertyName = function (_) {
    if (!arguments.length) return propertyName;
    propertyName = _;
    return this;
}

However, the zoom behaviour alters the domain and not the range of the scales.

From the docs:

Specifies an x-scale whose domain should be automatically adjusted when zooming.

Hence, you do do not need to get/set the range, but instead the domain of the scales y1 and y2: y2.domain(y1.domain()).

like image 73
musically_ut Avatar answered Oct 22 '22 12:10

musically_ut


Since the zoom function already manages all the ratios, a more abbreviated answer would be:

var zoomFirst = d3.behavior.zoom()
  .x(x)
  .y(y1)
  .scaleExtent([0, 3])
  .size([w, h])
  .on("zoom", function() {
    zoomSecond.scale(zoom.scale());
    zoomSecond.translate(zoom.translate());

    // Update visual. Both y domains will now be updated

  });

// Create copy for y2 scale
var zoomSecond = d3.behavior.zoom()
  .x(x)
  .y(y2)                // <-- second scale
  .scaleExtent([0, 3])  // <-- extent

This assumes you have called only zoomFirst to your visual.

like image 28
jbodily Avatar answered Oct 22 '22 10:10

jbodily