Cover by Jeff Fulton, Steve Fulton

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

O'Reilly logo

Zooming and Panning an Image

In this section, we will examine some methods to zoom and pan an image on the canvas. The image we are going to use is from a recent vacation to Central California. It is a large .jpg file, measuring 3648×2736. Obviously, this is far too large to view in a single canvas, so we will build a simple application allowing us to zoom and pan the image on our 500×500 canvas.

Figure 4-11 is a scaled-down version of this image.

A scaled-down version of the image we will zoom and pan

Figure 4-11. A scaled-down version of the image we will zoom and pan

Creating a Window for the Image

The first thing we are going to do is create a logical window, the size of the canvas, where our image will reside. We will use the following two variables to control the dimensions of this window:

var windowWidth = 500;
var windowHeight = 500;

We will also create two variables to define the current top-left corner for the window. When we move on to the panning examples, we will modify these values to redraw the image based on this location:

var windowX = 0;
var windowY = 0;

Drawing the Image Window

To draw the image window, we will simply modify the standard context.drawImage() function call using the values in the four variables we just defined:

context.drawImage(photo, windowX, windowY, windowWidth, windowHeight, 0, 0,
    windowWidth,windowHeight);

Let’s take a closer look at this for a refresher on how the drawImage() function operates. The values are passed in order:

photo

The image instance we are going to use as our source for painting onto the canvas

windowX

The top-left x location to start copying from the source image

windowY

The top-left y location to start copying from the source image

windowWidth

The width of the rectangle to start copying from the source image

windowHeight

The height of the rectangle to start copying from the source image

0

The top-left x destination location for the image on the canvas

0

The top-left y destination location for the image on the canvas

windowWidth

The width in pixels for the destination copy (this can be modified to scale the image)

windowHeight

The height in pixels for the destination copy (this can be modified to scale the image)

When we draw from the image to the canvas, we will be modifying the windowX and windowY values to create a panning effect. Example 4-11 demonstrates how to get the image onto the canvas with the window location set to 0,0. Figure 4-12 shows an example of the output for Example 4-11.

Example 4-11. Placing an image on the canvas in a logical window

var photo = new Image();
photo.addEventListener('load', eventPhotoLoaded , false);

photo.src = "butterfly.jpg";

var windowWidth = 500;
var windowHeight = 500;

var windowX = 0;
var windowY = 0;

function eventPhotoLoaded() {
   drawScreen()
}

function drawScreen(){
  context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
    0, 0, windowWidth,windowHeight);
}
An image in a small logical window

Figure 4-12. An image in a small logical window

Panning the Image

To pan the window across the image, we simply need to modify the windowX and windowY coordinates. In Example 4-12, we will modify the windowX coordinate inside a frame tick interval. During each loop iteration, we will increase the value of windowX by 10. We need to be careful not to go off the far right side of the image, so we will subtract the windowWidth from the image.width, and use the result as the maximum windowX position:

windowX+ = 10;
if (windowX>photo.width - windowWidth){
  windowX = photo.width - windowWidth;
}

Example 4-12 contains the changes necessary to perform this panning operation.

Example 4-12. Simple image panning

var photo = new Image();
photo.addEventListener('load', eventPhotoLoaded , false);

photo.src = "butterfly.jpg";

var windowWidth = 500;
var windowHeight = 500;

var windowX = 0;
var windowY = 0;

function eventPhotoLoaded() {
   startUp();
}

function drawScreen(){

   context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
       0,0,windowWidth,windowHeight);

   windowX += 10;
   if (windowX>photo.width - windowWidth){
       windowX = photo.width - windowWidth;
   }

}

function startUp(){

   setInterval(drawScreen, 100 );
}

When you test Example 4-12, you will see the window pan across the image and stop at the rightmost edge. Next, we will start to implement zooming into this simple example.

Zoom and Pan the Image

To zoom in or out of an image, we need to change the final width and height values of the drawImage() function. Let’s examine how we would zoom out to 50% of the original size of the image while panning at the same time. The drawImage() function will look like this:

context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
   0, 0, windowWidth*.5,windowHeight*.5);

Example 4-13 modifies Example 4-12 and adds in the 50% zoom.

Example 4-13. Pan an image with a preset zoom out

var photo = new Image();
photo.addEventListener('load', eventPhotoLoaded , false);

photo.src = "butterfly.jpg";
var windowWidth = 500;
var windowHeight = 500;

var windowX = 0;
var windowY = 0;

function eventPhotoLoaded() {
   startUp();
}

function drawScreen(){

   context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
       0,0,windowWidth*.5,windowHeight*.5);

   windowX += 10;
   if (windowX>photo.width - windowWidth){
       windowX = photo.width - windowWidth;
   }

}

function startUp(){

   setInterval(drawScreen, 100 );
}

When we test this example, we will see that when zoomed out, the image on the canvas is 50% of its original size. To zoom in, we simply change the scale factor from .5 to a number greater than 1:

context.drawImage(photo, windowX, windowY, windowWidth,windowHeight,
   0,0,windowWidth*2,windowHeight*2);

Example 4-14 changes this single line from Example 4-13 to zoom in rather than zoom out.

Example 4-14. Pan an image with a preset zoom amount

var photo = new Image();
photo.addEventListener('load', eventPhotoLoaded , false);

photo.src = "butterfly.jpg";

var windowWidth = 500;
var windowHeight = 500;

var windowX = 0;
var windowY = 0;


function eventPhotoLoaded() {
   startUp();
}

function drawScreen(){

   context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
       0,0,windowWidth*2,windowHeight*2);

   windowX += 10;
   if (windowX>photo.width - windowWidth){
       windowX = photo.width - windowWidth;
   }
}

function startUp(){

   setInterval(drawScreen, 100 );
}

Application: Controlled Pan and Zoom

Our final example for this section will be a simple application allowing the user to zoom and pan a photo.

The zoom scale

We are going to create a set of variables to handle the current zoom scale, the factor by which the zoom scale is increased or decreased, as well as the maximum and minimum zoom values:

var currentScale = .5;
var minScale = .2
var maxScale = 3;
var scaleIncrement = .1;

We will apply these values to the drawImage() function:

context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
    0,0,windowWidth*currentScale,windowHeight*currentScale);

Keyboard input

Now we need to create a keyboard listener function. The following function seems to work best in all browsers tested—it’s certainly not the only keyboard event listener, but it is tried and true throughout this book:

document.onkeydown = function(e){
   e = e?e:window.event;
}

Note

This function utilizes the ternary operator. If the statement before the ? is true, the statement following the ? is executed. If it is false, the statement after the : is executed. This is a shorthand version of the classic if/else construct.

We will add a switch/case statement, combining all the functions we have put into the previous zoom and pan examples, along with a new set of code for the y direction panning that we have not implemented before. It is very similar to the x direction panning: the left arrow key will pan the image to the left; the right arrow key will pan the image to the right:

case 38:
      //up
      windowY-=10;
      if (windowY<0){
         windowY = 0;
      }
      break;
case 40:
      //down
      windowY+=10;
      if (windowY>photo.height - windowHeight){
         windowY = photo.height - windowHeight;
      }
      break;
case 37:
      //left
      windowX-=10;
      if (windowX<0){
         windowX = 0;
      }
      break;
case 39:
       //right
      windowX+=10;
      if (windowX>photo.width - windowWidth){
         windowX = photo.width - windowWidth;
      }
      break;

We also need to add in two cases for the + and - keys to perform zoom in and zoom out actions:

case 109:
       //-
       currentScale-=scaleIncrement;
       if (currentScale<minScale){
          currentScale = minScale;
       }
       break;
case 107:
       //+
       currentScale+=scaleIncrement;
       if (currentScale>maxScale){
          currentScale = maxScale;
       }

When the user presses the + or - key, the currentScale variable is either incremented or decremented by the scaleIncrement value. If the new value of currentScale is greater than maxScale or lower than minScale, we set it to maxScale or minScale, respectively.

Example 4-15 puts this entire application together. It doesn’t take many lines of code to create the simple interactions.

Example 4-15. Image pan and zoom application

var photo = new Image();
photo.addEventListener('load', eventPhotoLoaded , false);

photo.src = "butterfly.jpg";

var windowWidth = 500;
var windowHeight = 500;

var windowX = 0;
var windowY = 0;
var currentScale = .5;
var minScale = .2
var maxScale = 3;
var scaleIncrement = .1;

function eventPhotoLoaded() {
   startUp();
}

function drawScreen(){

   //draw a background so we can see the Canvas edges
   context.fillStyle = "#ffffff";
   context.fillRect(0,0,500,500);

   context.drawImage(photo, windowX, windowY, windowWidth, windowHeight,
     0,0,windowWidth*currentScale,windowHeight*currentScale);

}

function startUp(){

   setInterval(drawScreen, 100 );
}

document.onkeydown = function(e){

   e = e?e:window.event;
   console.log(e.keyCode + "down");

   switch (e.keyCode){
      case 38:
            //up
            windowY-=10;
            if (windowY<0){
               windowY = 0;
            }
            break;
      case 40:
            //down
            windowY+=10;
            if (windowY>photo.height - windowHeight){
               windowY = photo.height - windowHeight;
            }
            break;
      case 37:
            //left
            windowX-=10;
            if (windowX<0){
               windowX = 0;
            }
            break;
      case 39:
            //right
            windowX+=10;
            if (windowX>photo.width - windowWidth){
               windowX = photo.width - windowWidth;
            }
            break;
      case 109:
            //-
            currentScale-=scaleIncrement;
            if (currentScale<minScale){
               currentScale = minScale;
            }
            break;
      case 107:
            //+
            currentScale+=scaleIncrement;
            if (currentScale>maxScale){
               currentScale = maxScale;
         }
   }

}

When testing Example 4-15, use the arrow keys to pan across the photo, and the + and - keys to zoom in and out.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required