SVG image zooming and panning with RaphaelJs


Easy way to do image zooming and panning without sending images to the server to be processed and trimmed with RaphaelJS and a few simple steps.

View the demo here.

1. Set initial zoom, image window width and height:

1 var mapZoom = 1;
2
3 var mapWidth = 400;
4 var mapHeight = 400;
5 $('#mapHolder').css('width', mapWidth);
6 $('#mapHolder').css('height', mapHeight);

2. Create a RaphaelJS object and add a full-size image to it.

1 var paper = Raphael("mapHolder", mapWidth, mapHeight);
2 var mapImage = paper.image("http://dashasalo.com/files/2011/09/figure1-worldatlas-large.jpg", 0, 0, mapWidth, mapHeight);

You should see your image resized to specified mapWidth and mapHeight.

3. Next step is to draw a menu of controls. I used RaphaelJS to draw the controls but you can also use images if you like.

1 var mapMenuHolderWidth = 40;
2 var mapMenuHolderHeight = 216;
3 var mapMenuHolderX = mapWidth - mapMenuHolderWidth - 6; // menu right margin
4 var mapMenuHolderY = 5; // menu top margin
5
6 // draw menu panel
7 var mapMenuHolder = paper.rect(mapMenuHolderX, mapMenuHolderY, mapMenuHolderWidth, mapMenuHolderHeight, 12).attr({fill: 'black', stroke: '#aaa', 'stroke-width': 2, opacity: 0.7});

Now controls themselves. You can find very nice icons and paths used to draw them on RaphaelJs website. SVG paths for icons were quite long and made code unreadable so I removed them from this code snippet. If you need paths have a look at the demo source code.

01 var maxIcon = paper.path("M22...051z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 10)});
02
03 var minIcon = paper.path("M22...884z").attr({fill: "#bbb", stroke: "#000", translation: (mapMenuHolderX+5) + ', '+(mapMenuHolderY + 7+ 34)});
04
05 var arrowTop = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, -25', rotation: 180});
06
07 var arrowLeft = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 4', rotation: 90});
08
09 var arrowRight = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 33', rotation: -90});
10
11 var arrowBottom = paper.path("M239...73z").attr({fill: "#bbb", stroke: "#000", scale: 0.10, translation: '245, 63'});

I used one path for all the arrows and just rotated it to make it point different direction.

4. Now we add a bunch of handlers to the controls to catch the click event.

01 // maximize icon clicked
02 maxIcon.click(function(e){
03     mapZoom++;
04     mapImage.scale(mapZoom, mapZoom);
05     e.stopPropagation();
06     e.preventDefault();
07   });
08
09 // minimize icon clicked
10   minIcon.click(function(e){
11     if (mapZoom == 1) return;
12     mapZoom--;
13     mapImage.scale(mapZoom, mapZoom);
14     adjustMapEdge();
15     e.stopPropagation();
16     e.preventDefault();
17   });
18
19 // top arrow clicked
20 arrowTop.click(function(e){
21     mapImage.translate(0, 20);
22     adjustMapEdge();
23     e.stopPropagation();
24     e.preventDefault();
25 });
26
27 // bottom arrow clicked
28 arrowBottom.click(function(e){
29     mapImage.translate(0, -20);
30     adjustMapEdge();
31     e.stopPropagation();
32     e.preventDefault();
33 });
34
35 // left arrow clicked
36 arrowLeft.click(function(e){
37     mapImage.translate(20, 0);
38     adjustMapEdge();
39     e.stopPropagation();
40     e.preventDefault();
41 });
42
43 // right arrow clicked
44 arrowRight.click(function(e){
45     mapImage.translate(-20, 0);
46     adjustMapEdge();
47     e.stopPropagation();
48     e.preventDefault();
49 });

We will have a look at adjustMapEdge function in a second.

You can also add nice hover effects to the controls with mouseover and mousedown events (look at my demo).

5. AdjustMapEdge function makes sure that image doesn’t move out of its window boundaries. For instance, if user moves up for long enough eventually he/she will hit the image top edge. When this happens we shouldn’t allow the image move any further up and just use its current location even if the user drags the image up again. This way from this position the user can only move down, left or right.

1 var adjustMapEdge = function()
2 {
3     if (mapImage.attr('x') > 0) mapImage.attr({'x': 0});
4     if (mapImage.attr('x') < (mapWidth - mapImage.attr('width'))) mapImage.attr({'x': (mapWidth - mapImage.attr('width'))});
5
6     if (mapImage.attr('y') > 0) mapImage.attr({'y': 0});
7     if (mapImage.attr('y') < (mapHeight - mapImage.attr('height') )) mapImage.attr({'y': (mapHeight - mapImage.attr('height') )});
8 };

6. Panning the image. Panning the image can be split into 3 basic steps – grab the image, hold the mouse button and drag the image, release the mouse button and drop the image.

As there is no reliable way to say whether mouse button is being hold when the mouse moves, we use a javascript variable to store the click state. In the code snippet below when mouse button is clicked I set clicking variable to true. Once the mouse button is released set clicking to false. And while mouse moves check if clicking is still true (hasn’t been released).

01 // coordinates of the last drag position
02 var lastX = 0, lastY = 0;
03
04 // record the click event on the image for drag and drop
05 var clicking = false;
06
07 // set clicking to true to record start of drag and drop
08 // and update the last coordinates
09 $('#mapHolder').mousedown(function(e){
10         clicking = true;
11         lastX = e.pageX;
12         lastY = e.pageY;
13         $('#mapHolder').css('cursor', 'move');
14         e.stopPropagation();
15         e.preventDefault();
16 });
17
18 // when the mouse button is released set clicking to false
19 // to stop drag and drop
20 $(document).mouseup(function(e){
21         clicking = false;
22         $('#mapHolder').css('cursor', 'default');
23         e.stopPropagation();
24         e.preventDefault();
25 });
26
27 $('#mapHolder').mousemove(function(e){
28         // when mouse if moved check if user is also holding a mouse button
29         if (clicking == false) return;
30
31         var currentMapPosX = 0, currentMapPosY = 0;
32
33         // get difference between current and previous mouse position
34         if ((mapImage.attr('x') <= 0) && (mapImage.attr('x') >= (mapWidth - mapImage.attr('width'))))
35         {
36             currentMapPosX = e.pageX - lastX;
37         }
38
39         if ((mapImage.attr('y') <= 0) && (mapImage.attr('y') >= (mapHeight - mapImage.attr('height'))))
40         {
41             currentMapPosY = e.pageY - lastY;
42         }
43
44         // move the image
45         mapImage.translate(currentMapPosX, currentMapPosY);
46
47         // record previous position
48         lastX = e.pageX;
49         lastY = e.pageY;
50
51         // check for image edge
52         adjustMapEdge();
53 });

Soure: dashasalo.com/2011/04/13/svg-image-zooming-and-panning-with-raphaeljs

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s