[OpenLayers-Commits] r11544 - in trunk/openlayers: lib lib/OpenLayers lib/OpenLayers/Control lib/OpenLayers/Handler tests tests/Control

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Fri Feb 25 21:22:48 EST 2011


Author: tschaub
Date: 2011-02-25 18:22:45 -0800 (Fri, 25 Feb 2011)
New Revision: 11544

Added:
   trunk/openlayers/lib/OpenLayers/Control/PinchZoom.js
   trunk/openlayers/tests/Control/PinchZoom.html
Modified:
   trunk/openlayers/lib/OpenLayers.js
   trunk/openlayers/lib/OpenLayers/Control/TouchNavigation.js
   trunk/openlayers/lib/OpenLayers/Events.js
   trunk/openlayers/lib/OpenLayers/Handler/Pinch.js
   trunk/openlayers/tests/list-tests.html
Log:
Adding a PinchZoom control for smooth zooming on multi-touch devices.  p=bbinet,me r=crschmidt (closes #3105)

Added: trunk/openlayers/lib/OpenLayers/Control/PinchZoom.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Control/PinchZoom.js	                        (rev 0)
+++ trunk/openlayers/lib/OpenLayers/Control/PinchZoom.js	2011-02-26 02:22:45 UTC (rev 11544)
@@ -0,0 +1,192 @@
+/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the Clear BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Handler/Pinch.js
+ */
+
+/**
+ * Class: OpenLayers.Control.PinchZoom
+ *
+ * Inherits:
+ *  - <OpenLayers.Control>
+ */
+OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
+
+    /** 
+     * Property: type
+     * {OpenLayers.Control.TYPES}
+     */
+    type: OpenLayers.Control.TYPE_TOOL,
+
+    /**
+     * Property: containerOrigin
+     * {Object} Cached object representing the layer container origin (in pixels).
+     */
+    containerOrigin: null,
+
+    /**
+     * Property: pinchOrigin
+     * {Object} Cached object representing the pinch start (in pixels).
+     */
+    pinchOrigin: null,    
+    
+    /**
+     * Property: currentCenter
+     * {Object} Cached object representing the latest pinch center (in pixels).
+     */
+    currentCenter: null,    
+
+    /**
+     * APIProperty: autoActivate
+     * {Boolean} Activate the control when it is added to a map.  Default is
+     *     true.
+     */
+    autoActivate: true,
+    
+    /**
+     * Constructor: OpenLayers.Control.PinchZoom
+     * Create a control for zooming with pinch gestures.  This works on devices
+     *     with multi-touch support.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *                    the control
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.handler = new OpenLayers.Handler.Pinch(this, {
+            start: this.pinchStart,
+            move: this.pinchMove,
+            done: this.pinchDone
+        }, this.handlerOptions);
+    },
+    
+    /**
+     * APIMethod: activate
+     * Activate this control.  Must be called after the control is added to a 
+     * map.
+     *
+     * Returns:
+     * {Boolean} The control was successfully activated.
+     */
+    activate: function() {
+        var activated = OpenLayers.Control.prototype.activate.apply(this,arguments);
+        if (activated) {
+            this.map.events.on({
+                moveend: this.updateContainerOrigin,
+                scope: this
+            });
+            this.updateContainerOrigin();
+        }
+        return activated;
+    },
+
+    /**
+     * APIMethod: deactivate
+     * Deactivate this control.
+     *
+     * Returns:
+     * {Boolean} The control was successfully deactivated.
+     */
+    deactivate: function() {
+        var deactivated = OpenLayers.Control.prototype.deactivate.apply(this,arguments);
+        if (this.map && this.map.events) {
+            this.map.events.un({
+                moveend: this.updateContainerOrigin,
+                scope: this
+            });
+        }
+        return deactivated;
+    },
+    
+    /**
+     * Method: updateContainerOrigin
+     * Must be called each time the layer container origin changes.
+     */
+    updateContainerOrigin: function() {
+        var container = this.map.layerContainerDiv;
+        this.containerOrigin = {
+            x: parseInt(container.style.left, 10),
+            y: parseInt(container.style.top, 10)
+        };
+    },
+
+    /**
+     * Method: pinchStart
+     *
+     * Parameters:
+     * evt - {Event}
+     * pinchData - {Object} pinch data object related to the current touchmove
+     *     of the pinch gesture. This give us the current scale of the pinch.
+     */
+    pinchStart: function(evt, pinchData) {
+        this.pinchOrigin = evt.xy;
+    },
+    
+    /**
+     * Method: pinchMove
+     *
+     * Parameters:
+     * evt - {Event}
+     * pinchData - {Object} pinch data object related to the current touchmove
+     *     of the pinch gesture. This give us the current scale of the pinch.
+     */
+    pinchMove: function(evt, pinchData) {
+        var scale = pinchData.scale;
+        var containerOrigin = this.containerOrigin;
+        var pinchOrigin = this.pinchOrigin;
+        var current = evt.xy;
+
+        var dx = Math.round((current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
+        var dy = Math.round((current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
+
+        this.applyTransform(
+            "translate(" + dx + "px, " + dy + "px) scale(" + scale + ")"
+        );
+        this.currentCenter = current;
+    },
+    
+    /**
+     * Method: applyTransform
+     * Applies the given transform to layers.
+     */
+    applyTransform: function(transform) {
+        var style = this.map.layerContainerDiv.style;
+        style['-webkit-transform'] = transform;
+        style['-moz-transform'] = transform;
+    },
+    
+    /**
+     * Method: pinchDone
+     *
+     * Parameters:
+     * evt - {Event}
+     * start - {Object} pinch data object related to the touchstart event that
+     *     started the pinch gesture.
+     * last - {Object} pinch data object related to the last touchmove event
+     *     of the pinch gesture. This give us the final scale of the pinch.
+     */
+    pinchDone: function(evt, start, last) {
+        var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
+        var resolution = this.map.getResolutionForZoom(zoom);
+
+        var location = this.map.getLonLatFromPixel(this.pinchOrigin);
+        var zoomPixel = this.currentCenter;        
+        var size = this.map.getSize();
+        
+        location.lon += resolution * ((size.w / 2) - zoomPixel.x);
+        location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
+
+        this.map.setCenter(location, zoom);
+
+        var style = this.map.layerContainerDiv.style;
+        style['-webkit-transform'] = "";
+        style['-moz-transform'] = "";
+    },
+
+    CLASS_NAME: "OpenLayers.Control.PinchZoom"
+
+});

Modified: trunk/openlayers/lib/OpenLayers/Control/TouchNavigation.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Control/TouchNavigation.js	2011-02-26 00:33:10 UTC (rev 11543)
+++ trunk/openlayers/lib/OpenLayers/Control/TouchNavigation.js	2011-02-26 02:22:45 UTC (rev 11544)
@@ -5,14 +5,15 @@
 
 /**
  * @requires OpenLayers/Control/DragPan.js
+ * @requires OpenLayers/Control/PinchZoom.js
  * @requires OpenLayers/Handler/Click.js
  */
 
 /**
  * Class: OpenLayers.Control.TouchNavigation
  * The navigation control handles map browsing with touch events (dragging,
- *     double-tapping, and tap with two fingers).  Create a new navigation
- *     control with the <OpenLayers.Control.TouchNavigation> control.
+ *     double-tapping, tap with two fingers, and pinch zoom).  Create a new 
+ *     control with the <OpenLayers.Control.TouchNavigation> constructor.
  *
  * Inherits:
  *  - <OpenLayers.Control>
@@ -32,6 +33,18 @@
     dragPanOptions: null,
 
     /**
+     * Property: pinchZoom
+     * {<OpenLayers.Control.PinchZoom>}
+     */
+    pinchZoom: null,
+
+    /**
+     * APIProprety: pinchZoomOptions
+     * {Object} Options passed to the PinchZoom control.
+     */
+    pinchZoomOptions: null,
+
+    /**
      * APIProperty: documentDrag
      * {Boolean} Allow panning of the map by dragging outside map viewport.
      *     Default is false.
@@ -70,6 +83,10 @@
             this.dragPan.destroy();
         }
         this.dragPan = null;
+        if (this.pinchZoom) {
+            this.pinchZoom.destroy();
+            delete this.pinchZoom;
+        }
         OpenLayers.Control.prototype.destroy.apply(this,arguments);
     },
 
@@ -80,6 +97,7 @@
         if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
             this.dragPan.activate();
             this.handlers.click.activate();
+            this.pinchZoom.activate();
             return true;
         }
         return false;
@@ -92,11 +110,12 @@
         if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
             this.dragPan.deactivate();
             this.handlers.click.deactivate();
+            this.pinchZoom.deactivate();
             return true;
         }
         return false;
     },
-
+    
     /**
      * Method: draw
      */
@@ -119,6 +138,9 @@
             }, this.dragPanOptions)
         );
         this.dragPan.draw();
+        this.pinchZoom = new OpenLayers.Control.PinchZoom(
+            OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
+        );
     },
 
     /**

Modified: trunk/openlayers/lib/OpenLayers/Events.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Events.js	2011-02-26 00:33:10 UTC (rev 11543)
+++ trunk/openlayers/lib/OpenLayers/Events.js	2011-02-26 02:22:45 UTC (rev 11544)
@@ -821,10 +821,20 @@
             // noone's listening, bail out
             return;
         }
-        // add clientX & clientY to all events - only corresponds to the first touch
-        if (evt.touches && evt.touches[0]) {
-            evt.clientX = evt.touches[0].clientX;
-            evt.clientY = evt.touches[0].clientY;
+        // add clientX & clientY to all events - corresponds to average x, y
+        var touches = evt.touches;
+        if (touches && touches[0]) {
+            var x = 0;
+            var y = 0;
+            var num = touches.length;
+            var touch;
+            for (var i=0; i<num; ++i) {
+                touch = touches[i];
+                x += touch.clientX;
+                y += touch.clientY;
+            }
+            evt.clientX = x / num;
+            evt.clientY = y / num;
         }
         if (this.includeXY) {
             evt.xy = this.getMousePosition(evt);

Modified: trunk/openlayers/lib/OpenLayers/Handler/Pinch.js
===================================================================
--- trunk/openlayers/lib/OpenLayers/Handler/Pinch.js	2011-02-26 00:33:10 UTC (rev 11543)
+++ trunk/openlayers/lib/OpenLayers/Handler/Pinch.js	2011-02-26 02:22:45 UTC (rev 11544)
@@ -120,13 +120,15 @@
      * {Boolean} Let the event propagate.
      */
     touchmove: function(evt) {
+        var propagate = true;
         if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
             this.pinching = true;
             var current = this.getPinchData(evt);
             this.callback("move", [evt, current]);
             this.last = current;
+            propagate = false;
         }
-        return true;
+        return propagate;
     },
 
     /**
@@ -140,14 +142,16 @@
      * {Boolean} Let the event propagate.
      */
     touchend: function(evt) {
+        var propagate = true;
         if (this.started) {
             this.started = false;
             this.pinching = false;
             this.callback("done", [evt, this.start, this.last]);
             this.start = null;
             this.last = null;
+            propagate = false;
         }
-        return true;
+        return propagate;
     },
 
     /**

Modified: trunk/openlayers/lib/OpenLayers.js
===================================================================
--- trunk/openlayers/lib/OpenLayers.js	2011-02-26 00:33:10 UTC (rev 11543)
+++ trunk/openlayers/lib/OpenLayers.js	2011-02-26 02:22:45 UTC (rev 11544)
@@ -182,6 +182,7 @@
                 "OpenLayers/Control/ZoomToMaxExtent.js",
                 "OpenLayers/Control/DragPan.js",
                 "OpenLayers/Control/Navigation.js",
+                "OpenLayers/Control/PinchZoom.js",
                 "OpenLayers/Control/TouchNavigation.js",
                 "OpenLayers/Control/MouseDefaults.js",
                 "OpenLayers/Control/MousePosition.js",

Added: trunk/openlayers/tests/Control/PinchZoom.html
===================================================================
--- trunk/openlayers/tests/Control/PinchZoom.html	                        (rev 0)
+++ trunk/openlayers/tests/Control/PinchZoom.html	2011-02-26 02:22:45 UTC (rev 11544)
@@ -0,0 +1,87 @@
+<html>
+<head>
+  <script src="../OLLoader.js"></script>
+  <script type="text/javascript">
+
+    function test_constructor(t) {
+        t.plan(2);
+        var control = new OpenLayers.Control.PinchZoom();
+        t.ok(control instanceof OpenLayers.Control.PinchZoom, "got an instance");
+        t.ok(control.handler instanceof OpenLayers.Handler.Pinch, "control has pinch handler");
+        control.destroy();
+    }
+
+    function test_destroy(t) {
+        t.plan(1);
+        var control = new OpenLayers.Control.PinchZoom();
+        control.destroy();
+        t.ok(!control.handler, "handler destroyed");
+    }
+    
+    function test_activate(t) {
+        t.plan(3);
+        var control = new OpenLayers.Control.PinchZoom();
+        t.ok(!control.active, "control not activated after construction");
+        
+        var map = new OpenLayers.Map({
+            div: "map",
+            controls: [control]
+        });
+        t.ok(control.active, "control activated after being added to the map");
+        
+        control.deactivate();
+        t.ok(!control.active, "control deactivated");
+        
+        map.destroy();
+    }
+
+    function test_pinchMove(t) {
+
+        var control = new OpenLayers.Control.PinchZoom();
+
+        var map = new OpenLayers.Map({
+            div: "map",
+            controls: [control]
+        });
+        
+        var log = [];
+        control.applyTransform = function(transform) {
+            log.push(transform);
+        }
+        
+        control.containerOrigin = {
+            x: 0, y: 0
+        };
+
+        control.pinchOrigin = {
+            x: 100, y: 50
+        };
+
+        var cases = [
+            {x: 100, y: 60, scale: 1, transform: "translate(0px, 10px) scale(1)"},
+            {x: 150, y: 60, scale: 1, transform: "translate(50px, 10px) scale(1)"},
+            {x: 150, y: 60, scale: 2, transform: "translate(-50px, -40px) scale(2)"},
+            {x: 50, y: 20, scale: 2.5, transform: "translate(-200px, -105px) scale(2.5)"},
+            {x: 150, y: 60, scale: 2, transform: "translate(-50px, -40px) scale(2)"},
+            {x: 50, y: 20, scale: 0.25, transform: "translate(25px, 8px) scale(0.25)"}
+        ];
+        
+        var len = cases.length;
+        t.plan(len*2);
+        
+        var c;
+        for (var i=0; i<len; ++i) {
+            c = cases[i];
+            control.pinchMove({xy: {x: c.x, y: c.y}}, {scale: c.scale});
+            t.eq(log.length, i+1, i + " called once");
+            t.eq(log[i], c.transform, i + " correct transform");
+        }
+        
+    }
+
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 256px; height: 256px;"></div>
+</body>
+</html>

Modified: trunk/openlayers/tests/list-tests.html
===================================================================
--- trunk/openlayers/tests/list-tests.html	2011-02-26 00:33:10 UTC (rev 11543)
+++ trunk/openlayers/tests/list-tests.html	2011-02-26 02:22:45 UTC (rev 11544)
@@ -32,6 +32,7 @@
     <li>Control/PanZoom.html</li>
     <li>Control/PanZoomBar.html</li>
     <li>Control/Permalink.html</li>
+    <li>Control/PinchZoom.html</li>
     <li>Control/Scale.html</li>
     <li>Control/ScaleLine.html</li>
     <li>Control/SelectFeature.html</li>



More information about the Commits mailing list