4

I am writing a cross platform web application that displays information using SVG. I need to have a multi-touch interface to pan and zoom the SVG graphic. From my research, it appears that Hammer.js is my best approach, and I am using the jQuery library version.

I am able to pan and zoom the image using the viewBox property of the SVG. The problem I'm having is using the center point information during the pinch zoom (vs. scaling from the top left corner). That center point, wherever it is in my image, should remain in the center of the pinch. Clearly, the solution is to modify the first two parts of the viewBox, but I'm having trouble determining the correct values. The center point (event.gesture.center) appears to be in browser page coordinates, so I need to figure out how to reliably scale this and then calculate the viewBox x and y coordinates. I've looked for examples, but couldn't find anything that quite related.

Is using the viewBox attribute the best way to scale SVG images? Are there better alternatives? Does anyone have sample code of zooming an SVG image from the pinch center?

Also, I'm noticing that even though I'm binding the event handling to the SVG element, the pinch gesture seems to work from anywhere on the page. It isn't the default browser handling of the pinch because it is only zooming the SVG image using my code. Here's the code I'm using for binding the pinch event:

$(window.demoData.svgElement).hammer().on("pinch", function (event) {
    event.preventDefault();
    ...
});

Thanks for any help on these two issues.

2
  • 1
    I know this doesn't answer your question, so putting it as a comment, but I have had a lot of good luck with jQuery PanZoom doing similar things to what you are asking about. github.com/timmywil/jquery.panzoom. You may want to try it out.
    – JasCav
    Commented Sep 14, 2013 at 23:00
  • Thank you very much for your comment. I've determined the answer to my question and was planning to write it up and post it. I now suspect that the library you mention might work better for me in the long run. Their demo worked well on my phone, but I'll need to compare it on a tablet with my other solution to see which will work best. As you say, it appears to do exactly what I want (zoom and pan SVG within a constraining element).
    – JSwartzen
    Commented Sep 16, 2013 at 0:19

3 Answers 3

3

I'm glad that some one else has been looking to solve these problems. It seems that SVG hasn't so far had enough attention.

I have been looking and working on solutions for several months. Firstly, you have three options for moving an SVG.

  1. Using the viewBox as you said. This is I think the best solution if you want to treat the image as a single item.
  2. You can use css transforms of the SVG element. the downside is this causes pixelation but means that you can use solutions that exist for other kinds of elements
  3. You can use svg transforms on elements or groups of elements within the SVG.

To answer the question of code a centered pinch can be described as follows. First you need to translate the pinch event center point from screen coordinate to SVG coordinates using the screenCTM (coordinate transform matrix). if point has coordinates (x,y) then the origin of you viewbox needs to undergo a transformation equivalent to

  • translation(-x, -y)
  • scale(scalefactor)
  • translation(x, y)

I have made this work 'mostly' in a library I have called hammerhead which runs on top of hammerjs. Here is an example of it in action.I won't give you code to exactly solve your problem as there is too much code to go where you have put '...' I ended up writing a viewBox object to manipulate. Just for reference here is the code I use to manipulate that.

scale: function(scale, center){
  var boxScale = 1.0/scale;
  center = center || this.center();
  var newMinimal = this.getMinimal().subtract(center).multiply(boxScale).add(center);
  var newMaximal = this.getMaximal().subtract(center).multiply(boxScale).add(center);
  var newViewBox = viewBox(newMinimal, newMaximal, this.getValidator());
  return newViewBox.valid()? newViewBox : null;
}

This method is not without problems. Change the viewBox is very computationally intensive I will not make for a pleasant scrolling experience on a phone except for an image with very few elements. It will work nicely if you change the view once in response to an action. for example left right up down keys. I have explored some of other options I mentioned above in these repos

  1. svg maneuver
  2. svg agile
  3. svgViewer

As I mentioned trying for quite sometime. These all use one of the approaches individually. To get both smooth scrolling and no pixilation I think will require a combination of both. For which I am working on yet another library.

1
1

The best method is to use svg transforms, with a combination of translate and scale you can perform a zoom centered on your pinch. You just need to figure out the formula. This example can help you (it uses css transforms but you get the idea): https://github.com/EightMedia/hammer.js/blob/1.0.x/examples/pinchzoom.html

2
  • they removed the examples folder, I fixed the link.
    – madmed
    Commented Jun 2, 2014 at 15:45
  • Unfortunately, the link is broken :(
    – Eduardo
    Commented Apr 25, 2022 at 9:06
1

There is another library designed specifically for this task called PanZoom. Unfortunately, it doesn't play well with Hammer. I've used it to zoom in / out of SVG files with great success.

https://github.com/timmywil/jquery.panzoom

Not the answer you're looking for? Browse other questions tagged or ask your own question.