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); });},