Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue w/ Programmatic Zoom v4

I'm using d3.js v4. I'm currently implementing programmatic zoom. This Panning and Zooming Tutorial has helped tremendously. My zoom works with scrolling wheel, but I want to create buttons to zoom. I know what necessary for zooming and panning is a translation [tx, ty] and a scale factor k. I'm using timescale for my x-Axis. I've managed to get tx and scale factor of k, by getting the pixel value of p1 (point 1) and p2(point 2) on the x-axis and then using those values to get a k (Scale factor). Like such:

var k = 500 / (xScale(p2) - xScale(p1)); //500 is desired pixel diff. between p1 and p2, and xScale is my d3.scaleTime() accessor function.
// for this zoom i just want the first value and last value to be at scale difference of the entire width.

Then I calculate tx by this:

var tx = 0 - k * p1;

Then feeding it into a d3.zoomIdentity() and rescaling my xdomain. I created a button to zoom back out. The issue is when I zoom in and then try to use the button to zoom out, it zooms out, but shrinks the x-axis. I can't seem to findout why its shrinking the x-axis instead of zooming back out correctly.

My JSFiddle

https://jsfiddle.net/codekhalifah/Lmdfrho7/2/

What I've Tried

  • Read zoom Documentation
  • Read through chapter on zoom in D3.js in action

My Code

After wheel zoom is applied I run this function:

 function zoomed() {
        if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush  
var t = d3.event.transform;
console.log(t);
console.log(xScale.domain());
xScale.domain(t.rescaleX(x2).domain());
usageAreaPath.attr("d", area);
usageLinePath.attr('d',line);
weatherAreaPath.attr('d',weatherChart.area);
focus.select(".axis--x").call(xAxis);
focus.selectAll('.circle')
    .attr('cx', function(d) { return xScale(getDate(d)); })
    .attr('cy', function(d) { return yScale(d.kWh); })
    .attr("transform", "translate(0," + 80 + ")");

        ... other non related items
    }

The zoom works properly, but after zooming in and then manually attempting to zoom back normal position I want. My manual zoom button function

function programmaticZoom(){
 var currDataSet = usageLinePath.data()[0], //current data set
        currDataSetLength = currDataSet.length,//current data set length
        x1 = currDataSet[0].usageTime, //getting first data item
        x2 = currDataSet[currDataSetLength-1].usageTime, //2nd data item
        x1px = xScale(moment(x1)), //Get current point 1
        x2px = xScale(moment(x2)); // Get current point 2

        // calculate scale factor
        var k = width / (x2px - x1px); // get scale factor
        var tx = 0 - k * x1px; // get tx
        var t = d3.zoomIdentity.translate(tx).scale(k); //create zoom identity

        xScale.domain(t.rescaleX(window.x2).domain());
        usageAreaPath.attr("d", area);
        usageLinePath.attr('d',line);
        weatherAreaPath.attr('d',weatherChart.area);
        focus.select(".axis--x").call(xAxis);
        focus.selectAll('.circle')
            .attr('cx', function(d) { return xScale(getDate(d)); })
            .attr('cy', function(d) { return yScale(d.kWh); })
            .attr("transform", "translate(0," + 80 + ")");

}
like image 507
Abdullah Rasheed Avatar asked Oct 19 '22 01:10

Abdullah Rasheed


1 Answers

I've been looking at this for a little while now, and I got it sort of working, there's one thing I can't figure out but you might be able to since you seem more familiar with d3 than I am. In your programmaticZoom() function, I noticed that tx is the offset for where the start of the graph is, and k is the scale. Using this, I changed the block:

var k = width / (x2px - x1px); // get scale var tx = 0 - k * x1px; // get tx var t = d3.zoomIdentity.translate(tx).scale(k);

to

var k = 1; var t = d3.zoomIdentity.scale(k);

First, when k = 1.0, the graph will fit in the window perfectly. The reason I believe setting k to its former value was wrong is that, when you increase the value of k so that k > 1.0, it stretches the width of the graph past the screen. The content on the graph has to be readjusted so that it can take up as much space on the graph as possible, while still being within the bounds of the screen. With k < 1, which is what happens with width / (x2px - x1px);, the graph shrinks to be less than the size of the screen. Readjusting like this will only make the graph's content fit to take up the maximum that it can within the graph, but since the graph is smaller than the screen, it will be readjusted to fit the shrunken graph and appear smaller than the screen.

I got rid of tx entirely because it offsets where the graph starts at. When the graph is zoomed in, it's width is stretched past the screen. It makes sense to offset here because you need to have your offset equal to where you want to begin viewing the graph, and allow the parts you don't need to remain off of the screen. In the case where you're zooming out all the way, the graph is the size of the screen, so offsetting is going to cause your graph to not start at the beginning of the screen and instead push part of it off of the screen. In your case, with k already shrinking the graph, the offset causes the shrunken graph to appear in the middle of the screen. However if the graph was not shrunken, the offset would push part of the graph off of the screen.

By changing that, the zoom out button appears to work, however there is still a problem. In the zoomed() function, you set var t = d3.event.transform;. The d3.event.transform contains values of k, x, and y that need to be 1, 0, and 0 respectively when completely zoomed out. I cannot change these values in the programmaticZoom() function though, because d3.event.transform only exists after an event was fired, specifically the mouse wheel. If you are able to get these values to be k = 1, x = 0, y = 0 only when the zoom button is clicked, the issue should be completely fixed.

Hopefully that helped some, I'm not very familiar with d3 but this should solve most of your problem and hopefully give you an idea of what was going wrong.

like image 138
Nick G Avatar answered Nov 15 '22 04:11

Nick G