Abdullah Rasheed
Abdullah Rasheed

Reputation: 3752

D3 v4 Zoom x-Axis Zooming and Panning Issue

I created a graph using D3 v4 that allows x-axis panning and zooming. I'm updating the graph differently when panning and zooming in order to decrease the amount of times we regenerate the graph.

As For Panning

When the user pans left or right i simply update the transform attribute as such:

path.attr("transform", d3.event.transform).

It works well.

As For Zooming

I rescale the domain and re-generate the graph when the user zooms in order to achieve the desired behavior as such:

t = d3.event.transform;
xScale.domain(t.rescaleX(x2).domain()); //update xScale domain

xAxis =d3.axisBottom(xScale)...
focus.select(".axis--x").call(xAxis);
usageAreaPath.attr("d", area); //area being a reference to the generator
usageLinePath.attr('d',line); //line being the name of the generator

The problem is, performing a pan after zoom or zoom after pan results in the graph jumping to different zoom levels. I'm not sure what I'm doing wrong, or how to rebind the scale after zooming/panning to update the proper scale, but I have attached a JSBIN below:

http://jsbin.com/webowajola/edit?js,output

Upvotes: 2

Views: 1482

Answers (3)

roland
roland

Reputation: 900

There are two separate problems with your code:

1. Translate

You used .attr("transform", "translate( 0, 80)") the wrong way. Remove all the .attr("transform", "translate(0," + 80 + ")") from your <path>, <circle>, axes and area and set the offset like this:

// Usage x-axis
var gX = focus.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + (height + margin.top) + ")")
    .call(xAxis);

// Focus Viewpoint
var focus = svg.append("g")
    .attr("class", "focus")
    .attr('transform', "translate(" 
        + margin.left + "," + (margin.top + 80) 
        + ")"
     );

Doing this way, the whole chart is shifted downwards together in the <g>.

2. panRight()

You are moving the circles and the lines twice: First with the new scale, second with the .attr("transform", d3.event.transform). So just remove following three lines

usageAreaPath.attr("transform", d3.event.transform);
usageLinePath.attr("transform", d3.event.transform);
circles.attr("transform", d3.event.transform);

Here the working jsbin

Edit

I just realized, that the zoomed function is not working at all. The redraw function is called all the time during panning (also in my solution). Therefore the panning is executed twice. If the "if statement" in the zoomed function would work, the panRight() function would be necessary. So focus on this if/else statement. One option could be to distinguish between mouse wheel and drag event, e.g. if(d3.event.sourceEvent.toString() === "[object MouseEvent]"). So the redraw function will not be called during panning, but some scaling issues remain. Maybe this could help.

Upvotes: 1

KEKUATAN
KEKUATAN

Reputation: 958

i dont know whats is you aim for, i thing the answer above me it settle your thing, maybe you just didnt stop the event use d3.event.defaultPrevented() if you want stop the event

    function panRight(){
          d3.event.defaultPrevented()//use it so it will reset the event
          console.log('panRight',d3.event.transform);

          var new_xScale = d3.event.transform.rescaleX(x2)
          gX.call(xAxis.scale(new_xScale));
          usageAreaPath.attr("transform", d3.event.transform);
          usageLinePath.attr("transform", d3.event.transform);
          circles.attr("transform", d3.event.transform);
        }
//the result is normal ordinary chart

but if you playing with unremarking the d3.event.defaultPrevented() on redraw() the result is one weird chart

function redraw(){
  //d3.event.defaultPrevented()
  var domain, 
  minBuffer, 
  maxBuffer, 
  panDelta1,
  panDelta2,
  t;

  t = d3.event.transform;
  console.log('redraw',d3.event.transform);      
  // console.log(t.rescaleX(x2).domain());
  // console.log(xScale.domain());
  //d3.event.defaultPrevented()
  xScale.domain(t.rescaleX(x2).domain());

  xAxis = d3.axisBottom(xScale).tickSize(0).tickFormat(d3.timeFormat('%b'));
  focus.select(".axis--x").call(xAxis);

  usageAreaPath.attr("d", area);

  usageLinePath.attr('d',line);



  //d3.event.defaultPrevented()
  focus.selectAll('.circle')
      .attr('cx', function(d) { return xScale(getDate(d)); })
      .attr('cy', function(d) { return yScale(d.kWh); })
      .attr("transform", "translate(0," + 80 + ")");


}

Upvotes: -1

Naveen Kumar H S
Naveen Kumar H S

Reputation: 1304

If you are updating the graph differently when panning and zooming in order to decrease the amount of times we regenerate the graph just add the check in the zoomed function as below,

function zoomed(){
    if(d3.event.transform.x !== 0 &&  d3.event.transform.y !== 0){
        redraw();
    }
}

Here is the link to the updated working jsbin.

I hope it will solve your graph regeneration problem.

Upvotes: 1

Related Questions