Wednesday, May 24, 2017

Zoom causes graph to go out of the plotting area bounds

Leave a Comment

I'm having an issue with my graph. When I zoom, the line goes over the edge of the canvas area and over the x/y axis. I tried adding a clippath but that doesn't seem to work. If I inspect the DOM in the debugger I can see the clippath rectangle is position exactly where it needs to be.

//The canvasGroup is the area between the axis var clipGroup = canvasGroup.append("clipPath")                 .attr("id", "canvasGroup-clipPath");  var clipRect = clipGroup.append("rect")                 .attr("width", width)                 .attr("height", height);              //Function that does the zooming function doZoom()  {        paths.forEach(function(path)     {                        path.attr("transform", d3.event.transform);     });      gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));     gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale))); }     var zoom = d3.zoom()             .scaleExtent([1, 5])             .translateExtent([[0, 0], [width, height]])             .on("zoom", doZoom);  //Register the event listener              canvasGroup.call(zoom);       //now loop over the data sets and plot each one //For brevity, I'm skipping the loop and only showing the call to create the line var path = canvasGroup.append("path")             .attr("clip-path", "url(#canvasGroup-clipPath)")                 .attr("fill", "steelblue")                               .attr("class", "line")             .attr("id", lineId + "-line")             .style("stroke", lineColor)             .style("stroke-width", 1)                .style("fill", "none");                       paths.push(path); path.attr("d", function(d) { return plotline(i)}); 

Here is how it looks. Before zoom: Plot before zoom After zoom:After zoom plot overlaps the Y axis

1 Answers

Answers 1

The problem is caused by setting the clipping path on the path representing your data. When applying the clipping path the browser needs to decide which coordinate system should be used for the contents of <clipPath> element. This is controlled by the clipPathUnits attribute which defaults to userSpaceOnUse:

userSpaceOnUse
The contents of the <clippath> element represent values in the current user coordinate system in place at the time when the <clippath> element is referenced (i.e., the user coordinate system for the element referencing the <clippath> element via the clip-path attribute).

When transforming the path in your doZoom() function, you are actually establishing a new coordinate system for the path. And, apart from drawing the path itself, this coordinate system will be used to compute the position and dimension of the clipping path. Thus, by transforming the path according to the zoom behavior, you are applying the same transformation to the clipping path whereby moving it away from the desired position.

Although it is tempting to set the clipPathUnits attribute to its other valid value objectBoundingBox, this is most likely not what you want for this case as it further complicates matters. When setting this value the positions and lengths of the <clipPath>'s contents need to be specified as fractions of the bounding box!

Knowing all this, there is a much easier solution to it! You just need to apply the clip-path to an element which will not be transformed during zooming ,e.g. a parent group. Given the incomplete code you provided, this might very well work by setting the clipping path to canvasGroup:

// Set the clipping path on the parent group. canvasGroup.attr("clip-path", "url(#canvasGroup-clipPath)")      // Append the path to the group as before. var path = canvasGroup.append("path")                .attr("fill", "steelblue")                                  .attr("class", "line")                // ... 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment