D3 Pan+Zoom - Add item when zoomed results in wrong position - javascript

I have a regular grid with Pan+Zoom where I draw squares in subsequent orders, when a user click on a button, e.g.
However, if I zoom-in before adding a new item, it gets added to wrong position, i.e.
Demo of the issue http://jsbin.com/dewiq/2/edit
Any ideas?

You're mixing up two different ways of zooming in d3 -- zooming with scales ("semantic" zooming) and zooming with transforms ("geometric" zooming). See this answer for a detailed breakdown of the differences.
You've attached your scales to the zoom behaviour, so when you zoom or pan your scales get modified to reflect the change. However, you're only using that modification to redraw the axes, for the plot you're creating the zoom effect with a geometric transformation. That works until you try to redraw any of your plot (or, draw a new shape). When you do that, the modified scales and the transformed plot add together to double the zoom and pan effects, resulting in shapes that are out of position with the axes.
The solution is to simply do one or the other. Either connect your scales to the zoom behaviour, and then use those scales to redraw all your shapes after every zoom event; or use transformations -- but then your axes won't automatically change the tick spacing as you zoom in and out.

Related

D3.js v4 Separate Drag and Zoom/Pan

I have code that draws a square and plots points inside a canvas element. I use a d3.drag function to drag the square separate from the points and a d3.zoom function to zoom on BOTH the points and the square as well as pan both. The problem I'm having is two-fold:
Once zoomed, I sort of lose the ability to drag the square. Sort of in the sense that if you click around enough (typically the original spot of the square, not current), you can grab the square again.
Once I am able to grab the square again, the zoom renders to the very original zoom layer, not the current zoom.
Here is a jsFiddle
I found this answer that recommends adding:
square.on("mousedown", function() { d3.event.stopPropagation(); });
but have not had any luck.
TIA

Brushable and zoomable timeline with d3

I want to make a timeline where the user can choose between scrolling to zoom or select a region to zoom.
There are some examples of the first like:
https://bl.ocks.org/mbostock/4015254
Or zooming in on a area with brush:
https://bl.ocks.org/mbostock/f48fcdb929a620ed97877e4678ab15e6
But I cannot find an example that does both. How can I do both? Or are there any examples that I missed?
This is not the most straightforward thing to implement. As you will notice, the brush based zooming does not rely on d3.zoom but instead performs the zooming via listeners that fire on events to do whatever is needed to scale the axes and move the plot elements accordingly.
In comparison, all the scrolling based zoom examples typically rely on d3.zoom which utilizes a d3.zoom() behavior that keeps track of all the transforms performed on the plot while panning/zooming and is solely responsible for updating the various chart elements. The difficulty lies in the fact that the 2 approaches are quite different and if you manually change the chart view via brushing, you need to figure out a way to update the internal zoom transform that d3.zoom references so that it is aware of the changes made via the brush based zoom events.
This is not at all easy to do because d3.zoom was not designed to be fed information from elsewhere and the internal record of transforms that were performed are not meant to be updateable/mutable. You can update the transform via selection.call(zoom.transform, d3.zoomIdentity); but that unfortunately also fires a whole bunch of events related to the actual zoom behavior, which is not something you want since you already handled all the zoom behavior with your brush based zoom. An ugly, but effective workaround that I was able to use to reset the zoom transform was to mutate the actual .__zoom field of the DOM node that is bound to the d3.zoom behavior as follows:
// WARNING: Ugly mutation of __zoom property of pan/scroll-zoom rect to
// reset the transform without having to fire events associated with zoom
// d3.select(".zoom").node().__zoom = {k: 1, x: 0, y: 0}; <-- Fails since __zoom contains other hidden objects
scrollZoom.node().__zoom["k"] = 1;
scrollZoom.node().__zoom["x"] = 0;
scrollZoom.node().__zoom["y"] = 0;
So for example: If you want a 2D brush for rectangle zooming, but also d3.zoom based zooming for panning and mouse-scrolling, then anytime you use the 2D brush to zoom, you will want to reset the d3.zoom transform back to the identity transform as above. This prevents and ugly and jarring jitter in panning/scrolling response when chaining 2D brush based zooming actions with panning/mouse-scrolling actions due to the transform on record with d3.zoom not being in-sync with the view on display (due to the 2D brush based zoom changing the view without d3.zoom's knowledge).
Here is something else that is important to note:
d3.zoom has a limitation in that it currently only supports a common zoom scale for both X and Y axes (Source). This unfortunately means that there is no way to map a 2-D brush based zoom to a d3.zoom based approach since 2D brush based zooming produces different zoom scaling in X and Y. If you want to do things with minimal issues, using a consistent approach, I'd recommend looking into using d3.xyzoom. This is a fork of d3.zoom that implements support for different scales for X and Y axes. This would enable you to calculate the corresponding X and Y zoom scaling and translation values for any 2D brush selection, which you could then feed into d3.zoom, thus enabling you to perform all the zooming using a common approach (which also results in the least amount of code duplication).
That being said, if you are solely interested in a 1-D brush based zoom, you should be able to map that to a d3.zoom approach so that you don't have to deal with 2 different paths for handling the view and scaling of all the axes and other graphical elements in your chart. Here is a good example of this:
https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172
I apologize for the length of this post and if it is a bit rambling. I am working on putting together a block on my work in a couple of days and I'll try to circle back here and post a link when I get around to doing so. I only started learning D3 a week ago, so I'm learning along the way.

Zooming lines in d3js

I'm just trying to plot a function in javascript by using the d3.js library. I need to add the zoom and pan events. I've done it by following this example:
http://bl.ocks.org/mbostock/7ec977c95910dd026812
I've drawn some lines on this plane.
My problem is that when I zoom it, the linewidth changes too.
What I like is that line still having it's original width when zooming. I think I have to redraw it, but I'm not sure if this is the best way.
Can you tellme if there is a better way of doing this, or show me any simple example?.
Thank you!
As you have not supplied any code it is a bit hard to tell. From your description the grid lines you are rendering are being transformed by the zoom matrix.
You can either draw the grid independently of the zoom but you will have to switch between zoomed and grid transforms and will require more code or just re-render the grid lines with the width set to the width times the inverse scale. Eg if your grid lines are two pixels then you can can keep them that width by setting their render width to 2*(1/scale) which is the same as 2/scale. Scale being the amount of zoom. The same applies to any objects you wish to keep at a fixed render size.
There is no easy way to avoid re-rendering the gridlines
This is what I was looking for:
http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke

webgl-surface-plot: Clips at Different Rotations

I am trying to use webgl-surface-plot to give a 3D surface plot of data. At several orientations, the axes labels get clipped. At other orientations, part of the graph gets clipped. Is there a way to expand the canvas size and/or move the axes labels closer to the axes?
You can change the default scale/zoom in the code and then just increase the canvas dimensions via the input options. I plan to add the starting zoom as an option to the chart, when I get a chance. I want to fix the axis labels as well. Currently they are at a fixed distance from the axes. They should, instead, be at a distance dictated by the length of the tick labels. Fixing this will be quite tricky, though.

Recover scale, when I make a custom zoom

I have a problem, when zooming with a rectangle who drew on the chart, it should make a zoom into the selected area. The error occurs when I try to return me zooming out, this should work but for some reason this is locked and I can not return to the starting position.
Here is the code to perform the zoom with the rectangle, first pressing the shift key and click to draw down the area on which you need to perform zoom in.
http://jsfiddle.net/cristian540/VFqeV/8/
This is happening because you have set .scaleExtent([1, Infinity]), and when you zoom using a rectangle selection you aren't resetting the scale.
Perhaps you'd be better turning off .scaleExtent, since there is only a single scale value for d3.behavior.zoom whereas the zoom rectangle effectively has two (one for x- and one for y-).

Categories