From c9df8f4d4366ae0100cb497dacafd2cae91ec53c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Tue, 10 Nov 2009 11:14:28 +0000 Subject: [PATCH] Added documentDrag option to the DragPan control and Drag handler. When set to true, this allow pan-dragging while outside the map. Thanks vmx for the initial patches. r=vmx, bartvde (closes #39) git-svn-id: http://svn.openlayers.org/trunk/openlayers@9791 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/document-drag.html | 39 +++++++++++++ lib/OpenLayers/Control/DragPan.js | 12 +++- lib/OpenLayers/Handler/Drag.js | 95 ++++++++++++++++++++++++++----- tests/Control/DragPan.html | 22 +++++++ 4 files changed, 153 insertions(+), 15 deletions(-) create mode 100644 examples/document-drag.html diff --git a/examples/document-drag.html b/examples/document-drag.html new file mode 100644 index 0000000000..bc9f07533f --- /dev/null +++ b/examples/document-drag.html @@ -0,0 +1,39 @@ + + + OpenLayers Document Drag Example + + + + + + +

OpenLayers Document Drag Example

+ +
+ +
Keep on dragging even when the mouse cursor moves outside of the map
+ +
+ +
+ This example shows how to make a map draggable outside of the map itself. +
+ + \ No newline at end of file diff --git a/lib/OpenLayers/Control/DragPan.js b/lib/OpenLayers/Control/DragPan.js index 8caf6b823a..90c47402ff 100644 --- a/lib/OpenLayers/Control/DragPan.js +++ b/lib/OpenLayers/Control/DragPan.js @@ -34,7 +34,14 @@ OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { * panning the map again. Set this to increase dragging performance. * Defaults to 25 milliseconds. */ - interval: 25, + interval: 25, + + /** + * APIProperty: documentDrag + * {Boolean} If set to true, mouse dragging will continue even if the + * mouse cursor leaves the map viewport. Default is false. + */ + documentDrag: false, /** * Method: draw @@ -46,7 +53,8 @@ OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { "move": this.panMap, "done": this.panMapDone }, { - interval: this.interval + interval: this.interval, + documentDrag: this.documentDrag } ); }, diff --git a/lib/OpenLayers/Handler/Drag.js b/lib/OpenLayers/Handler/Drag.js index 6f0cdb3d84..b746fb047f 100644 --- a/lib/OpenLayers/Handler/Drag.js +++ b/lib/OpenLayers/Handler/Drag.js @@ -83,6 +83,20 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { * This is "private", and should be left alone. */ timeoutId: null, + + /** + * APIProperty: documentDrag + * {Boolean} If set to true, the handler will also handle mouse moves when + * the cursor has moved out of the map viewport. Default is false. + */ + documentDrag: false, + + /** + * Property: documentEvents + * {} Event instance for observing document events. Will + * be set on mouseout if documentDrag is set to true. + */ + documentEvents: null, /** * Constructor: OpenLayers.Handler.Drag @@ -210,6 +224,16 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { */ mousemove: function (evt) { if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) { + if(this.documentDrag === true && this.documentEvents) { + if(evt.element === document) { + this.adjustXY(evt); + // do setEvent manually because the documentEvents are not + // registered with the map + this.setEvent(evt); + } else { + this.destroyDocumentEvents(); + } + } if (this.interval > 0) { this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval); } @@ -245,6 +269,10 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { */ mouseup: function (evt) { if (this.started) { + if(this.documentDrag === true && this.documentEvents) { + this.adjustXY(evt); + this.destroyDocumentEvents(); + } var dragged = (this.start != this.last); this.started = false; this.dragging = false; @@ -273,19 +301,31 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { */ mouseout: function (evt) { if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.div)) { - var dragged = (this.start != this.last); - this.started = false; - this.dragging = false; - OpenLayers.Element.removeClass( - this.map.viewPortDiv, "olDragDown" - ); - this.out(evt); - this.callback("out", []); - if(dragged) { - this.callback("done", [evt.xy]); - } - if(document.onselectstart) { - document.onselectstart = this.oldOnselectstart; + if(this.documentDrag === true) { + this.documentEvents = new OpenLayers.Events(this, document, + null, null, {includeXY: true}); + this.documentEvents.on({ + mousemove: this.mousemove, + mouseup: this.mouseup + }); + OpenLayers.Element.addClass( + document.body, "olDragDown" + ); + } else { + var dragged = (this.start != this.last); + this.started = false; + this.dragging = false; + OpenLayers.Element.removeClass( + this.map.viewPortDiv, "olDragDown" + ); + this.out(evt); + this.callback("out", []); + if(dragged) { + this.callback("done", [evt.xy]); + } + if(document.onselectstart) { + document.onselectstart = this.oldOnselectstart; + } } } return true; @@ -345,6 +385,35 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { } return deactivated; }, + + /** + * Method: adjustXY + * Converts event coordinates that are relative to the document body to + * ones that are relative to the map viewport. The latter is the default in + * OpenLayers. + * + * Parameters: + * evt - {Object} + */ + adjustXY: function(evt) { + var pos = OpenLayers.Util.pagePosition(this.map.div); + evt.xy.x -= pos[0]; + evt.xy.y -= pos[1]; + }, + + /** + * Method: destroyDocumentEvents + * Destroys the events instance that gets added to the document body when + * documentDrag is true and the mouse cursor leaves the map viewport while + * dragging. + */ + destroyDocumentEvents: function() { + OpenLayers.Element.removeClass( + document.body, "olDragDown" + ); + this.documentEvents.destroy(); + this.documentEvents = null; + }, CLASS_NAME: "OpenLayers.Handler.Drag" }); diff --git a/tests/Control/DragPan.html b/tests/Control/DragPan.html index ab48b3af67..def6bd6f8b 100644 --- a/tests/Control/DragPan.html +++ b/tests/Control/DragPan.html @@ -36,6 +36,28 @@ t.eq(map.getCenter().lat, res * 5, "Lat is " + (res * 5) + " after drag"); t.eq(map.getCenter().lon, res * -5, "Lon is " + (res * -5) + " after drag"); } + function test_Control_DragPan_drag_documentDrag (t) { + t.plan(4); + control = new OpenLayers.Control.DragPan({documentDrag: true}); + map = new OpenLayers.Map("map", {controls:[control]}); + layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", + "http://labs.metacarta.com/wms/vmap0", + {layers: 'basic'} ); + map.addLayer(layer); + map.zoomToMaxExtent(); + map.zoomIn(); + control.activate(); + + res = map.baseLayer.resolutions[map.getZoom()]; + t.eq(map.center.lat, 0, "Lat is 0 before drag"); + t.eq(map.center.lon, 0, "Lon is 0 before drag"); + map.events.triggerEvent('mousedown', {'type':'mousedown', 'xy':new OpenLayers.Pixel(0,0), 'which':1}); + map.events.triggerEvent('mousemove', {'type':'mousemove', 'xy':new OpenLayers.Pixel(5,5), 'which':1}); + map.events.triggerEvent('mouseup', {'type':'mouseup', 'xy':new OpenLayers.Pixel(5,5), 'which':1}); + + t.eq(map.getCenter().lat, res * 5, "Lat is " + (res * 5) + " after drag"); + t.eq(map.getCenter().lon, res * -5, "Lon is " + (res * -5) + " after drag"); + } function test_Control_DragPan_click(t) { t.plan(1); var control = new OpenLayers.Control.DragPan();