Cyclops
Cyclops

Reputation: 31

d3.js area graph with initial zoom

I am trying ti implement a zoom and brush chart with D3.js with an initial zoom. The initial zoom covers only about 10% of the brushable extent - this works correctly. When I zoom on the chart the first time, the zoom does not take into account the initial state.

Here's the graph initially:

enter image description here

And after the first zoom:

enter image description here

Here's a plunkr, and the relevant code:

 svg.append("rect")
      .attr("class", "zoom")
      .attr("width", width)
      .attr("height", height)
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
      .call(zoom);

    //To set initial zoom level in graph
      var d0 = data[0].date,
      d1 = data[data.length-1].date;
     svg.call(zoom).transition()
    .duration(1500)
    .call(zoom.transform, d3.zoomIdentity
          .scale(width / (x(d1) - x(d0)))
          .translate(-x(d0), 0)); 

  function brushed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
    var s = d3.event.selection || x2.range();
    x.domain(s.map(x2.invert, x2));
    focus.select(".area").attr("d", area);
    focus.select(".axis--x").call(xAxis);
    svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
        .scale(width / (s[1] - s[0]))
        .translate(-s[0], 0));
  }

  function zoomed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
    var t = d3.event.transform;
    x.domain(t.rescaleX(x2).domain());
    focus.select(".area").attr("d", area);
    focus.select(".axis--x").call(xAxis);
    context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
  }

Why are zoom events not relative to the initial zoom state?

Upvotes: 3

Views: 347

Answers (1)

Andrew Reid
Andrew Reid

Reputation: 38151

Your code works fine if, you don't zoom over the chart plot area: you can zoom on the axes or margins for example. This is a hint as to what is happening:

The zoom is applied to two elements:

svg.append("rect")
   ...
   .call(zoom);

And:

svg.call(zoom)

This means we are keeping track of two zoom states - but you only apply an initial state to one:

svg.call(zoom).transition()
  .duration(1500)
  .call(zoom.transform, d3.zoomIdentity
      .scale(width / (x(d1) - x(d0)))
      .translate(-x(d0), 0)); 

This is why you can zoom in the margins and the chart works as intended, but doesn't work if you zoom over the rect that covers the plot area. Let's just apply the zoom once on one element:

  var rect = svg.append("rect")
    ...
    .call(zoom);

  rect.transition()
   .duration(1500)
   .call(zoom.transform, d3.zoomIdentity
      .scale(width / (x(d1) - x(d0)))
      .translate(-x(d0), 0));      

Here's an updated plunkr

I've removed the script.js file as it duplicated script in the index.html file.

I had issues with your original plunkr in Firefox, I have not addressed those, I used Chrome to view

Upvotes: 2

Related Questions