[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