diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index a21452d9e7..99c79fdb2f 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -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", diff --git a/lib/OpenLayers/Control/PinchZoom.js b/lib/OpenLayers/Control/PinchZoom.js new file mode 100644 index 0000000000..038af939a5 --- /dev/null +++ b/lib/OpenLayers/Control/PinchZoom.js @@ -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.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" + +}); diff --git a/lib/OpenLayers/Control/TouchNavigation.js b/lib/OpenLayers/Control/TouchNavigation.js index d102c39205..3bc2ab5bff 100644 --- a/lib/OpenLayers/Control/TouchNavigation.js +++ b/lib/OpenLayers/Control/TouchNavigation.js @@ -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 control. + * double-tapping, tap with two fingers, and pinch zoom). Create a new + * control with the constructor. * * Inherits: * - @@ -31,6 +32,18 @@ OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { */ dragPanOptions: null, + /** + * Property: 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. @@ -70,6 +83,10 @@ OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { 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 @@ OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { 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 @@ OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { 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 @@ OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { }, this.dragPanOptions) ); this.dragPan.draw(); + this.pinchZoom = new OpenLayers.Control.PinchZoom( + OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions) + ); }, /** diff --git a/lib/OpenLayers/Events.js b/lib/OpenLayers/Events.js index 4e7f51f4d3..f043653f66 100644 --- a/lib/OpenLayers/Events.js +++ b/lib/OpenLayers/Events.js @@ -821,10 +821,20 @@ OpenLayers.Events = OpenLayers.Class({ // 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 + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 505d05e404..61c7b9e58f 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -32,6 +32,7 @@
  • Control/PanZoom.html
  • Control/PanZoomBar.html
  • Control/Permalink.html
  • +
  • Control/PinchZoom.html
  • Control/Scale.html
  • Control/ScaleLine.html
  • Control/SelectFeature.html