2
votes

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

3
In D3, the zoom deals with the panning. So, you just need .on("zoom", redraw);. - Gerardo Furtado
@GerardoFurtado If I handle the panning with redraw i'll be regenerating the graph. I don't want to regenerate the graph, I just want to translate the graph according to the pan,i.e.usageAreaPath.attr("transform", d3.event.transform); but if there is a zoom then, I'd like to use the redraw contents. i.e. usageAreaPath.attr("d", area); usageLinePath.attr('d',line); - Abdullah Rasheed
@GerardoFurtado I'm sorry for the confusion, is it clear what I'm asking? - Abdullah Rasheed
How are we supposed to see the problem in your jsbin? How can we pan? How can we zoom? - ConnorsFan
when you first load the graph, you can zoom up and down by scrolling the mouse wheel up and down. - Abdullah Rasheed

3 Answers

1
votes

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.

1
votes

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.

-1
votes

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 + ")");


}