Zoom image to cursor breaks when mouse is moved Zoom image to cursor breaks when mouse is moved javascript javascript

Zoom image to cursor breaks when mouse is moved


Actually, not too complicated. You just need to separate the mouse location updating logic from the zoom updating logic. Check out my fiddle:http://jsfiddle.net/qGGwx/41/

All I have done here is add a 'mousemove' listener on the container, and put the self.mouseLocation updating logic in there. Since it is no longer required, I also took out the mouseLocation updating logic from the 'mousewheel' handler. The animation code stays the same, as does the decision of when to start/stop the animation loop.

here's the code:

    self.container.on('mousewheel', function (e, delta) {        if (!self.running) {            self.running = true;            self.animateLoop();        }        self.delta = delta        self.smoothWheel(delta);        return false;    });    self.container.on('mousemove', function (e) {        var offset = self.image.offset();        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;    });


Before you check this fiddle out; I should mention:

First of all, within your .zoom() method; you shouldn't divide by currentscale:

self.currentLocation.x += ((self.mouseLocation.x - self.currentLocation.x) / self.currentscale);self.currentLocation.y += ((self.mouseLocation.y - self.currentLocation.y) / self.currentscale);

because; you already use that factor when calculating the mouseLocation inside the initmousewheel() method like this:

self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;

So instead; (in the .zoom() method), you should:

self.currentLocation.x += (self.mouseLocation.x - self.currentLocation.x);self.currentLocation.y += (self.mouseLocation.y - self.currentLocation.y);

But (for example) a += b - a will always produce b so the code above equals to:

self.currentLocation.x = self.mouseLocation.x;self.currentLocation.y = self.mouseLocation.y;

in short:

self.currentLocation = self.mouseLocation;

Then, it seems you don't even need self.currentLocation. (2 variables for the same value). So why not use mouseLocation variable in the line where you set the transform-origin instead and get rid of currentLocation variable?

newCss[compat[i] + 'transform-origin'] = self.mouseLocation.x + 'px ' + self.mouseLocation.y + 'px';

Secondly, you should include a mousemove event listener within the initmousewheel() method (just like other devs here suggest) but it should update the transform continuously, not just when the user wheels. Otherwise the tip of the pointer will never catch up while you're zooming out on "any" random point.

self.container.on('mousemove', function (e) {    var offset = self.image.offset();    self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;    self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;    self.zoom(self.currentscale);});

So; you wouldn't need to calculate this anymore within the mousewheel event handler so, your initmousewheel() method would look like this:

initmousewheel: function () {    var self = this;    self.container.on('mousewheel', function (e, delta) {        if (!self.running) {            self.running = true;            self.animateLoop();        }        self.delta = delta;        self.smoothWheel(delta);        return false;    });    self.container.on('mousemove', function (e) {        var offset = self.image.offset();        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;        self.zoom(self.currentscale); // <--- update transform origin dynamically    });}

One Issue:

This solution works as expected but with a small issue. When the user moves the mouse in regular or fast speed; the mousemove event seems to miss the final position (tested in Chrome). So the zooming will be a little off the pointer location. Otherwise, when you move the mouse slowly, it gets the exact point. It should be easy to workaround this though.

Other Notes and Suggestions:

  • You have a duplicate property (prevscale).
  • I suggest you always use JSLint or JSHint (which is available onjsFiddle too) to validate your code.
  • I highly suggest you to use closures (often refered to as Immediately Invoked Function Expression (IIFE)) to avoid the global scope when possible; and hide your internal/private properties and methods.


Add a mousemover method and call it in the init method:

mousemover: function() {    var self = this;    self.container.on('mousemove', function (e) {        var offset = self.image.offset();        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;        self.zoom(self.currentscale);    });},

Fiddle: http://jsfiddle.net/powtac/qGGwx/34/