I put together a little demo to show how to setup a canvas with a corresponding minimap that displays an overview of the contents of the canvas. You can use the mousewheel on either the main canvas or the minimap to zoom in and out. You can also drag either the image or the minimap frame to pan around. Notice how these actions (pan/zoom) from the canvas update the minimap and vice versa. I wrote the demo in the reusable coding style put forth in Bostock’s Towards Reusable Charts. The trickiest part was preventing the two different pan/zoom handlers (the canvas and the minimap) from getting out of sync with respect to setting the scale and transform values. Prior to getting that worked out, I kept hitting the common problem of trying to use multiple pan/zoom controls to operate against a single zoom behavior (discussed here, here and here). I managed to work it out by updating the transform and scale values on both controls at the correct time and place. If you have any suggestions for improvements, I’d love to hear them.
See the Pen d3 Minimap Pan and Zoom Demo by Bill White (@billdwhite) on CodePen
Thanks for the example, I was trying to see if how this would work for more typical D3 docs (i.e. force layout). I’m unclear though how I should be calling the addItem. I’m wondering if you can look at what I have to see what I’m missing: http://bl.ocks.org/gschrader/7682766
Sure, let me take a look and get back to you.
Have a look at my latest post on how you might approach this.
Thanks! I will have a look soon.
Pingback: Bill White's Blog | d3 Force Layout with Pan and Zoom Minimap
Hi,
I copied and pasted your codes to my computer and run SimpleHTTPServer of python ( `python -m SimpleHTTPServer 3000`). When I open the link in browser I had the error as follows:
`Uncaught TypeError: Cannot read property ‘getAttribute’ of null d3.min.js:3
ya.attr d3.min.js:3
minimap.target minimap.js:411
canvas minimap.js:173
ya.call d3.min.js:3
(anonymous function)“
Do you have idea of the reason?
Any help is kindly accepted.
Regards.
Looks like the target value is null that the minimap is trying to render. I would load up the demo you have in Chrome and use the developer tools to put a breakpoint on that minimap.target method and see if the argument being passed is null. If you are trying to use the demo by loading an XML file containing the SVG code, I suspect either that file is not getting loaded, or that the demo is trying to run before that file is loaded (which is why line 448 calls the addItem() method inside the load xml call. π
Hi,
First, thanks so much for writing this. I have learnt a lot about D3 by following through the code.
When I tried to use the canvas.reset() function, the code complained that `x.domain` was undefined. Also, the `zoomed` function is undefined. I corrected the code in my own version which seemed to work OK, does that look OK to you ?
canvas.reset = function() {
d3.transition().duration(750).tween("zoom", function() {
var ix = d3.interpolate(xScale.domain(), [-width / 2, width / 2]),
iy = d3.interpolate(yScale.domain(), [-height / 2, height / 2]),
iz = d3.interpolate(scale, 1);
return function(t) {
zoom.scale(iz(t)).x(xScale.domain(ix(t))).y(yScale.domain(iy(t)));
zoomHandler(iz(t));
};
});
};
Yes! That does indeed fix this glitch with the reset button. Not sure how I missed that but I’ve updated the posts accordingly! π
Pingback: Bill White's Blog | Book Review: Learning D3.js Mapping
When a Collapsible Tree is too damn huge also works the minimap? will be nice to implement this
I’m trying to figure out how to prevent the sudden jump in the main view when user taps reset. The part I have working correctly is after the user pans and zooms in the main view – it now animates smoothly from wherever it is. The part I can’t figure out is after the user drags in the minimap – it still jumps. Here’s my pen: http://codepen.io/anon/pen/BNybeo
Looks like a problem where the manipulation of the minimap is not setting the same properties as the manipulation of the canvas. If you drag the canvas around and then reset, it transitions back to the reset position smoothly (most times). Dragging the minimal around and clicking reset makes it jump back. Putting a console out in the reset method to show the value of translate reveals that, when it jumps, the start and end values of translate are both [0,0]. I suspect my code has a glitch that is not setting the correct x/y value on the canvas when the minimap is driving the movement. If I were trying to fix that glitch, I’d look at making sure the x/y is set on the canvas regardless of whether it is being moved by the drag handler or the minimal manipulation.
I figured it out and it now works as expected. On line 238 of my fork (…BNybeo), I changed d3… to svg…
Nice! I’ve integrated your fix into my sample. Thanks for working that out π
can I use this without using SVG image? Just using a PNG or jpg file. But these PNG and JPG will be high resolution files.
Not sure but you might be able to embed a JPEG or PNG using this technique. If you can embed the image as described in that link, it should work.
Hello Bill, many thanks for these great examples. Just noticed that the mini map window does not render or resize the zoom box in IE11, do you know how to get it to work? The left hand side works, just the right hand mini map does not show the outline.
Also, I’d like to add this feature to a dynamic tree layout that includes expanding and collapsing nodes, the data is fetched asynchronously and added to the layout at run time. I am already playing with zoom transitions that center an object if it is clicked and I also zoom into the object, so I am updating the x y according to the updated scale and offsets. Any recommendations or tips on how to approach this? I guess I also have the issue of needing to grow the SVG container as more nodes are added to the layout… I am new to d3 so any tips and thoughts will be much appreciated. Thanks once more for sharing your work.
The problem with the Pan and Zoom is that IE returns its transform strings without commas, so my utility method was not parsing the transform string correctly. Also, IE Stack Overflow style on the main SVG so you would get scrollbars in the demo in IE. I fixed that as well. I really hate IE; These days it seems like I spend half my time writing Microsoft-specific workarounds for code that runs fine in all other browsers. π
Oh that is an easy fix, I noticed that about your code. You should use the native d3js methods to resolve that issue, and the outline is just a CSS bug. But if you use the following, it may improve performance.
var currentTransform = d3.transform(activeNode.attr(“transform”)),
currentX = currentTransform.translate[0].toFixed(2),
currentY = currentTransform.translate[1].toFixed(2);
Excellent! Nice suggestion on the translation. I’ll use that instead.
The outline issue is due to the fact that the filters, shadows and clip paths for each demo use unique IDs. Because the blog post shows all the demos on the same page, the IDs conflict and you don’t see the shadows etc except for one (which is the winner of the namespace battle). At one point I tried to make them unique but I missed a few. I went back and fixed the IDs to all have unique names and also updated the CSS for each to reflect those IDs which fixed that problem. π
regarding how to get started on the click and zoom project: my only suggestion would be to follow Mike Rostock’s Click to Zoom example example. I suspect you’ve already come across these in looking for other examples but that would be my starting point and it is pretty much just trial and error to get the effect you’re looking for. Wish I could give you more help than that, but maybe that will get you started. π
There is a CSS bug that only affects IE where no box is rendered around the minimap container. Update the CSS for .background with the correct element ID:
From: url(#minimapDropShadow)
To: url(#minimapDropShadow_cwbjo)
NB: This is a problem on all your minimap examples, except for the force layout.
Now to see if I can make the zoom transitions smoother as they are very jerky on IE.
Hi Bill,
When I select the pan tool it works great, but I can’t figure out how to deselect it and shift back to the default pointer that allows me to click on different properties and see specs, such as the owner, property card, etc…
Never mind, I just figured it out. You have to go to the right hand panel, choose “selection” tab, and click on the pointer icon there.
Thanks for this! I needed to build something similar in D3 utilizing V4 and came up with a good solution. I figured it might be of some help to someone else who hit this example looking for how to do it in V4.
https://jsfiddle.net/Gwythian/7p86qv4c/4/
Cool. Glad to help a fellow Austinite!
here i can’t fine d3.demo in D3 V5 ,wthat is the use of this d3.demo