views:

55

answers:

1

I have a draggeable image contained within a box. You can zoom in and zoom out on the image in the box which will make the image larger or smaller but the box size remains the same. The box's height and width will vary as the browser is resized. The top and left values for the image will change as it is dragged around.

I'm trying to keep whatever the point the box was centered on in the image, in the center. Kind of like how zoom on Google Maps works or the zoom on Mac OS X zooms.

What I'm doing right now is calculating the center of the box (x = w/2, y = h/2) and then using the top and left values for the image to calculate the position of the image in the center of the box. (x -= left, y -= top).

Then I zoom the image by growing or shrinking it and I use the scale change to adjust the coordinates (x = (x * (old_width/new_width), y = (y * (old_height/new_height)).

I then reposition the image so that its center is what it was before zoom by grabbing the coordinates it is currently centered on (that changed with the resize) and adding the difference between the old center values and the new values to the top and left values (new_left = post_zoom_left + (old_center_x - new_center_x), new_top = post_zoom_top + (old_center_y - new_center_y).

This works ok for zoom in, but zoom out seems to be somewhat off.

Any suggestions?

My code is below:

app.Puzzle_Viewer.prototype.set_view_dimensions = function () {

  var width, height, new_width, new_height, coordinates, x_scale,
    y_scale;

  coordinates = this.get_center_position();
  width = +this.container.width();
  height = +this.container.height();
  //code to figure out new width and height
  //snip ...
  x_scale = width/new_width;
  y_scale = height/new_height;
  coordinates.x = Math.round(coordinates.x * x_scale);
  coordinates.y = Math.round(coordinates.y * y_scale);
  //resize image to new_width & new_height
  this.center_on_position(coordinates);
};

app.Puzzle_Viewer.prototype.get_center_position = function () {

  var top, left, bottom, right, x, y, container;

  right = +this.node.width();
  bottom = +this.node.height();
  x = Math.round(right/2);
  y = Math.round(bottom/2);
  container = this.container.get(0);
  left = container.style.left;
  top = container.style.top;
  left = left ? parseInt(left, 10) : 0;
  top  = top ? parseInt(top, 10) : 0;
  x -= left;
  y -= top;
  return {x: x, y: y, left: left, top: top};
};

app.Puzzle_Viewer.prototype.center_on_position = function (coordinates) {

  var current_center, x, y, container;

  current_center = this.get_center_position();
  x = current_center.left + coordinates.x - current_center.x;
  y = current_center.top + coordinates.y - current_center.y;
  container = this.container.get(0);
  container.style.left = x + "px";
  container.style.top = y + "px";
};
+3  A: 

[Working demo]

Data

  • Resize by: R
  • Canvas size: Cw, Ch
  • Resized image size: Iw, Ih
  • Resized image position: Ix, Iy
  • Click position on canvas: Pcx, Pcy
  • Click position on original image: Pox, Poy
  • Click position on resized image: Prx, Pry

Method

  1. Click event position on canvas -> position on image: Pox = Pcx - Ix, Poy = Pcy - Iy
  2. Position on image -> Pos on resized image: Prx = Pox * R, Pry = Poy * R
  3. top = (Ch / 2) - Pry, left = (Cw / 2) - Prx
  4. ctx.drawImage(img, left, top, img.width, img.height)

Implementation

// resize image
I.w *= R;
I.h *= R;

// canvas pos -> image pos
Po.x = Pc.x - I.left;
Po.y = Pc.y - I.top;

// old img pos -> resized img pos
Pr.x = Po.x * R;
Pr.y = Po.y * R;

// center the point
I.left = (C.w / 2) - Pr.x;
I.top  = (C.h / 2) - Pr.y;

// draw image
ctx.drawImage(img, I.left, I.top, I.w, I.h);

This is a general formula that works for zooming in or out, and can handle any point as the new center. To make it specific to your problem:

  • Pcx = Cw / 2, Pcy = Ch / 2 (alway use the center)
  • R < 1 for zooming out, and R > 1 for zooming in
galambalazs
Wow, thank you.
apphacker