I have a map. I want a user to be able to zoom and pan the map. Imagine Google Maps, but instead of being infinitely pannable, the map is a square (it doesn't wrap around again if you go past the edge of it).
I have implemented zoom and pan using scale()
and translate()
. These work well.
I am stuck on the final part - when a user zooms, I want to center the zoom around that point. It is hard to explain in words, so just imagine what happens when you mousewheel in Google Maps - that is what I want.
I have looked at every answer on SO with any of these terms in the title. Most are variations on this one, which basically say this is what I need to do:
ctx.translate(/* to the point where the mouse is */);
ctx.scale(/* to zoom level I want */)
ctx.translate(/* back to the point where the mouse was, taking zoom into account */);
However, no matter what I do, I cannot seem to get it to work. I can get it to zoom to a particular point after zooming, but whatever I do I cannot make that point equal to where the mouse pointer was.
Check out this fiddle. Imagine the square is a map and the circles are countries or whatever.
The best implementation I have found is this SO answer and the linked example. However, the code makes use of SVG and .createSVGMatrix()
and all sorts of things that, frankly, I can't understand. I would prefer a totally-canvas solution if possible.
Obviously I am not interested in doing this with a library. I want to understand why what I'm doing is not working.
Here's one technique for zooming at a point:
Drawing the map
Simplify things by not using transforms to draw the map (no need for translate,scale!).
All that's needed is the scaling version of
context.drawImage
.What you do is scale the original map to the desired size and then pull it upward and leftward from the scaling point that the user has selected.
Selecting the scaling point (the focal point):
The scaling focal point is actually 2 points!
The first focal point is the mouseX,mouseY where the user clicked to set their desired scaling point. It's important to remember that the mouse coordinate is in scaled space. The map that the user is seeing/clicking is scaled so their mouseX,mouseY is scaled also.
The second focal point is calculated by unscaling the mouse coordinate. This second point is the equivalent mouse position on the original unscaled map.
The second unscaled focal point is used to calculate how much to pull the scaled map leftward and upward from the first focal point.
Scaling the map
When the user indicates they want to scale the map larger or smaller:
Code:
Here's example code and a Demo: