Note that there are some explanatory texts on larger screens.

plurals
  1. POCSS3 zooming on mouse cursor
    primarykey
    data
    text
    <p>My goal is to create a plugin that enables zooming &amp; panning operations on a page area, just like how Google Maps currently works (meaning: scrolling with the mouse = zooming in/out of the area, click &amp; hold &amp; move &amp; release = panning).</p> <p>When scrolling, I wish to have a zoom operation centered on the mouse cursor.</p> <p>For this, I use on-the-fly CSS3 matrix transformations. The only, yet mandatory, constraint is that I cannot use anything else than CSS3 translate &amp; scale transformations, with a transform origin of 0px 0px.</p> <p>Panning is out of the scope of my question, since I have it working already. When it comes to zooming, I am struggling to figure out where the glitch is in my javascript code.</p> <p>The problem must be somewhere in the MouseZoom.prototype.zoom function, in the calculation of the translation on the x axis and y axis.</p> <p>First, here is my HTML code:</p> <pre><code>&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name="viewport" content="width = device-width, initial-scale = 1.0, user-scalable = no" /&gt; &lt;meta name="apple-mobile-web-app-capable" content="yes"&gt; &lt;meta name="apple-mobile-web-app-status-bar-style" content="black" /&gt; &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"&gt;&lt;/script&gt; &lt;script src="jquery.mousewheel.min.js"&gt;&lt;/script&gt; &lt;script src="StackOverflow.js"&gt;&lt;/script&gt; &lt;style type="text/css" media="all"&gt; #drawing { position: absolute; top: 0px; left: 0px; right:0; bottom:0; z-index: 0; background: url(http://catmacros.files.wordpress.com/2009/09/cats_banzai.jpg) no-repeat; background-position: 50% 50%; } &lt;/style&gt; &lt;title&gt;Test&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;div id="drawing"&gt;&lt;/div&gt; &lt;script&gt; var renderer = new ZoomPanRenderer("drawing"); &lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>As you can see, I am using Jquery and the jquery mouse wheel plugin from Brandon Aaron, which can be found here: <a href="https://github.com/brandonaaron/jquery-mousewheel/" rel="noreferrer">https://github.com/brandonaaron/jquery-mousewheel/</a></p> <p>Here is the content of the StackOverflow.js file:</p> <pre><code>/***************************************************** * Transformations ****************************************************/ function Transformations(translateX, translateY, scale){ this.translateX = translateX; this.translateY = translateY; this.scale = scale; } /* Getters */ Transformations.prototype.getScale = function(){ return this.scale; } Transformations.prototype.getTranslateX = function(){ return this.translateX; } Transformations.prototype.getTranslateY = function(){ return this.translateY; } /***************************************************** * Zoom Pan Renderer ****************************************************/ function ZoomPanRenderer(elementId){ this.zooming = undefined; this.elementId = elementId; this.current = new Transformations(0, 0, 1); this.last = new Transformations(0, 0, 1); new ZoomPanEventHandlers(this); } /* setters */ ZoomPanRenderer.prototype.setCurrentTransformations = function(t){ this.current = t; } ZoomPanRenderer.prototype.setZooming = function(z){ this.zooming = z; } /* getters */ ZoomPanRenderer.prototype.getCurrentTransformations = function(){ return this.current; } ZoomPanRenderer.prototype.getZooming = function(){ return this.zooming; } ZoomPanRenderer.prototype.getLastTransformations = function(){ return this.last; } ZoomPanRenderer.prototype.getElementId = function(){ return this.elementId; } /* Rendering */ ZoomPanRenderer.prototype.getTransform3d = function(t){ var transform3d = "matrix3d("; transform3d+= t.getScale().toFixed(10) + ",0,0,0,"; transform3d+= "0," + t.getScale().toFixed(10) + ",0,0,"; transform3d+= "0,0,1,0,"; transform3d+= t.getTranslateX().toFixed(10) + "," + t.getTranslateY().toFixed(10) + ",0,1)"; return transform3d; } ZoomPanRenderer.prototype.getTransform2d = function(t){ var transform3d = "matrix("; transform3d+= t.getScale().toFixed(10) + ",0,0," + t.getScale().toFixed(10) + "," + t.getTranslateX().toFixed(10) + "," + t.getTranslateY().toFixed(10) + ")"; return transform3d; } ZoomPanRenderer.prototype.applyTransformations = function(t){ var elem = $("#" + this.getElementId()); elem.css("transform-origin", "0px 0px"); elem.css("-ms-transform-origin", "0px 0px"); elem.css("-o-transform-origin", "0px 0px"); elem.css("-moz-transform-origin", "0px 0px"); elem.css("-webkit-transform-origin", "0px 0px"); var transform2d = this.getTransform2d(t); elem.css("transform", transform2d); elem.css("-ms-transform", transform2d); elem.css("-o-transform", transform2d); elem.css("-moz-transform", transform2d); elem.css("-webkit-transform", this.getTransform3d(t)); } /***************************************************** * Event handler ****************************************************/ function ZoomPanEventHandlers(renderer){ this.renderer = renderer; /* Disable scroll overflow - safari */ document.addEventListener('touchmove', function(e) { e.preventDefault(); }, false); /* Disable default drag opeartions on the element (FF makes it ready for save)*/ $("#" + renderer.getElementId()).bind('dragstart', function(e) { e.preventDefault(); }); /* Add mouse wheel handler */ $("#" + renderer.getElementId()).bind("mousewheel", function(event, delta) { if(renderer.getZooming()==undefined){ var offsetLeft = $("#" + renderer.getElementId()).offset().left; var offsetTop = $("#" + renderer.getElementId()).offset().top; var zooming = new MouseZoom(renderer.getCurrentTransformations(), event.pageX, event.pageY, offsetLeft, offsetTop, delta); renderer.setZooming(zooming); var newTransformation = zooming.zoom(); renderer.applyTransformations(newTransformation); renderer.setCurrentTransformations(newTransformation); renderer.setZooming(undefined); } return false; }); } /***************************************************** * Mouse zoom ****************************************************/ function MouseZoom(t, mouseX, mouseY, offsetLeft, offsetTop, delta){ this.current = t; this.offsetLeft = offsetLeft; this.offsetTop = offsetTop; this.mouseX = mouseX; this.mouseY = mouseY; this.delta = delta; } MouseZoom.prototype.zoom = function(){ var previousScale = this.current.getScale(); var newScale = previousScale + this.delta/5; if(newScale&lt;1){ newScale = 1; } var ratio = newScale / previousScale; var imageX = this.mouseX - this.offsetLeft; var imageY = this.mouseY - this.offsetTop; var previousTx = - this.current.getTranslateX() * previousScale; var previousTy = - this.current.getTranslateY() * previousScale; var previousDx = imageX * previousScale; var previousDy = imageY * previousScale; var newTx = (previousTx * ratio + previousDx * (ratio - 1)) / newScale; var newTy = (previousTy * ratio + previousDy * (ratio - 1)) / newScale; return new Transformations(-newTx, -newTy, newScale); } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload