Merge pull request #7703 from ahocevar/draw-state

Improved drawing experience on touch devices
This commit is contained in:
Andreas Hocevar
2018-01-17 20:25:07 +01:00
committed by GitHub
6 changed files with 126 additions and 7 deletions
+7
View File
@@ -2,6 +2,13 @@
### Next release ### Next release
#### Changed behavior of the `Draw` interaction
For better drawing experience, two changes were made to the behavior of the Draw interaction:
1. On long press, the current vertex can be dragged to its desired position.
2. On touch move (e.g. when panning the map on a mobile device), no draw cursor is shown, and the geometry being drawn is not updated. But because of 1., the draw cursor will appear on long press. Mouse moves are not affected by this change.
#### Changes in proj4 integration #### Changes in proj4 integration
Because relying on a globally available proj4 is not practical with ES modules, we have made a change to the way we integrate proj4: Because relying on a globally available proj4 is not practical with ES modules, we have made a change to the way we integrate proj4:
+9
View File
@@ -2356,6 +2356,7 @@ olx.interaction.DragZoomOptions.prototype.out;
* @typedef {{clickTolerance: (number|undefined), * @typedef {{clickTolerance: (number|undefined),
* features: (ol.Collection.<ol.Feature>|undefined), * features: (ol.Collection.<ol.Feature>|undefined),
* source: (ol.source.Vector|undefined), * source: (ol.source.Vector|undefined),
* dragVertexDelay: (number|undefined),
* snapTolerance: (number|undefined), * snapTolerance: (number|undefined),
* type: (ol.geom.GeometryType|string), * type: (ol.geom.GeometryType|string),
* stopClick: (boolean|undefined), * stopClick: (boolean|undefined),
@@ -2401,6 +2402,14 @@ olx.interaction.DrawOptions.prototype.features;
olx.interaction.DrawOptions.prototype.source; olx.interaction.DrawOptions.prototype.source;
/**
* Delay in milliseconds after pointerdown before the current vertex can be
* dragged to its exact position. Default is 500 ms.
* @type {number|undefined}
*/
olx.interaction.DrawOptions.prototype.dragVertexDelay;
/** /**
* Pixel distance for snapping to the drawing finish. Default is `12`. * Pixel distance for snapping to the drawing finish. Default is `12`.
* @type {number|undefined} * @type {number|undefined}
+4
View File
@@ -267,6 +267,8 @@ const PluggableMap = function(options) {
*/ */
this.keyHandlerKeys_ = null; this.keyHandlerKeys_ = null;
_ol_events_.listen(this.viewport_, EventType.CONTEXTMENU,
this.handleBrowserEvent, this);
_ol_events_.listen(this.viewport_, EventType.WHEEL, _ol_events_.listen(this.viewport_, EventType.WHEEL,
this.handleBrowserEvent, this); this.handleBrowserEvent, this);
_ol_events_.listen(this.viewport_, EventType.MOUSEWHEEL, _ol_events_.listen(this.viewport_, EventType.MOUSEWHEEL,
@@ -491,6 +493,8 @@ PluggableMap.prototype.addOverlayInternal_ = function(overlay) {
*/ */
PluggableMap.prototype.disposeInternal = function() { PluggableMap.prototype.disposeInternal = function() {
this.mapBrowserEventHandler_.dispose(); this.mapBrowserEventHandler_.dispose();
_ol_events_.unlisten(this.viewport_, EventType.CONTEXTMENU,
this.handleBrowserEvent, this);
_ol_events_.unlisten(this.viewport_, EventType.WHEEL, _ol_events_.unlisten(this.viewport_, EventType.WHEEL,
this.handleBrowserEvent, this); this.handleBrowserEvent, this);
_ol_events_.unlisten(this.viewport_, EventType.MOUSEWHEEL, _ol_events_.unlisten(this.viewport_, EventType.MOUSEWHEEL,
+1
View File
@@ -15,6 +15,7 @@ export default {
CHANGE: 'change', CHANGE: 'change',
CLEAR: 'clear', CLEAR: 'clear',
CONTEXTMENU: 'contextmenu',
CLICK: 'click', CLICK: 'click',
DBLCLICK: 'dblclick', DBLCLICK: 'dblclick',
DRAGENTER: 'dragenter', DRAGENTER: 'dragenter',
+64 -6
View File
@@ -2,8 +2,10 @@
* @module ol/interaction/Draw * @module ol/interaction/Draw
*/ */
import {inherits} from '../index.js'; import {inherits} from '../index.js';
import EventType from '../events/EventType.js';
import Feature from '../Feature.js'; import Feature from '../Feature.js';
import MapBrowserEventType from '../MapBrowserEventType.js'; import MapBrowserEventType from '../MapBrowserEventType.js';
import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js';
import BaseObject from '../Object.js'; import BaseObject from '../Object.js';
import _ol_coordinate_ from '../coordinate.js'; import _ol_coordinate_ from '../coordinate.js';
import _ol_events_ from '../events.js'; import _ol_events_ from '../events.js';
@@ -17,6 +19,7 @@ import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js'; import MultiLineString from '../geom/MultiLineString.js';
import MultiPoint from '../geom/MultiPoint.js'; import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js'; import MultiPolygon from '../geom/MultiPolygon.js';
import MouseSource from '../pointer/MouseSource.js';
import Point from '../geom/Point.js'; import Point from '../geom/Point.js';
import Polygon, {fromCircle, makeRegular} from '../geom/Polygon.js'; import Polygon, {fromCircle, makeRegular} from '../geom/Polygon.js';
import DrawEventType from '../interaction/DrawEventType.js'; import DrawEventType from '../interaction/DrawEventType.js';
@@ -56,6 +59,18 @@ const Draw = function(options) {
*/ */
this.downPx_ = null; this.downPx_ = null;
/**
* @type {number}
* @private
*/
this.downTimeout_;
/**
* @type {number}
* @private
*/
this.lastDragTime_;
/** /**
* @type {boolean} * @type {boolean}
* @private * @private
@@ -191,6 +206,12 @@ const Draw = function(options) {
*/ */
this.geometryFunction_ = geometryFunction; this.geometryFunction_ = geometryFunction;
/**
* @type {number}
* @private
*/
this.dragVertexDelay_ = options.dragVertexDelay !== undefined ? options.dragVertexDelay : 500;
/** /**
* Finish coordinate for the feature (first point for polygons, last point for * Finish coordinate for the feature (first point for polygons, last point for
* linestrings). * linestrings).
@@ -255,7 +276,8 @@ const Draw = function(options) {
wrapX: options.wrapX ? options.wrapX : false wrapX: options.wrapX ? options.wrapX : false
}), }),
style: options.style ? options.style : style: options.style ? options.style :
Draw.getDefaultStyleFunction() Draw.getDefaultStyleFunction(),
updateWhileInteracting: true
}); });
/** /**
@@ -322,8 +344,27 @@ Draw.prototype.setMap = function(map) {
* @api * @api
*/ */
Draw.handleEvent = function(event) { Draw.handleEvent = function(event) {
if (event.originalEvent.type === EventType.CONTEXTMENU) {
// Avoid context menu for long taps when drawing on mobile
event.preventDefault();
}
this.freehand_ = this.mode_ !== Draw.Mode_.POINT && this.freehandCondition_(event); this.freehand_ = this.mode_ !== Draw.Mode_.POINT && this.freehandCondition_(event);
let move = event.type === MapBrowserEventType.POINTERMOVE;
let pass = true; let pass = true;
if (this.lastDragTime_ && event.type === MapBrowserEventType.POINTERDRAG) {
const now = Date.now();
if (now - this.lastDragTime_ >= this.dragVertexDelay_) {
this.downPx_ = event.pixel;
this.shouldHandle_ = !this.freehand_;
move = true;
} else {
this.lastDragTime_ = undefined;
}
if (this.shouldHandle_ && this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
}
if (this.freehand_ && if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDRAG && event.type === MapBrowserEventType.POINTERDRAG &&
this.sketchFeature_ !== null) { this.sketchFeature_ !== null) {
@@ -332,11 +373,18 @@ Draw.handleEvent = function(event) {
} else if (this.freehand_ && } else if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDOWN) { event.type === MapBrowserEventType.POINTERDOWN) {
pass = false; pass = false;
} else if (event.type === MapBrowserEventType.POINTERMOVE) { } else if (move) {
pass = this.handlePointerMove_(event); pass = event.type === MapBrowserEventType.POINTERMOVE;
if (pass && this.freehand_) {
pass = this.handlePointerMove_(event);
} else if (event.pointerEvent.pointerType == MouseSource.POINTER_TYPE ||
(event.type === MapBrowserEventType.POINTERDRAG && !this.downTimeout_)) {
this.handlePointerMove_(event);
}
} else if (event.type === MapBrowserEventType.DBLCLICK) { } else if (event.type === MapBrowserEventType.DBLCLICK) {
pass = false; pass = false;
} }
return PointerInteraction.handleEvent.call(this, event) && pass; return PointerInteraction.handleEvent.call(this, event) && pass;
}; };
@@ -357,6 +405,11 @@ Draw.handleDownEvent_ = function(event) {
} }
return true; return true;
} else if (this.condition_(event)) { } else if (this.condition_(event)) {
this.lastDragTime_ = Date.now();
this.downTimeout_ = setTimeout(function() {
this.handlePointerMove_(new MapBrowserPointerEvent(
MapBrowserEventType.POINTERMOVE, event.map, event.pointerEvent, event.frameState));
}.bind(this), this.dragVertexDelay_);
this.downPx_ = event.pixel; this.downPx_ = event.pixel;
return true; return true;
} else { } else {
@@ -374,6 +427,11 @@ Draw.handleDownEvent_ = function(event) {
Draw.handleUpEvent_ = function(event) { Draw.handleUpEvent_ = function(event) {
let pass = true; let pass = true;
if (this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
this.handlePointerMove_(event); this.handlePointerMove_(event);
const circleMode = this.mode_ === Draw.Mode_.CIRCLE; const circleMode = this.mode_ === Draw.Mode_.CIRCLE;
@@ -423,6 +481,9 @@ Draw.prototype.handlePointerMove_ = function(event) {
this.shouldHandle_ = this.freehand_ ? this.shouldHandle_ = this.freehand_ ?
squaredDistance > this.squaredClickTolerance_ : squaredDistance > this.squaredClickTolerance_ :
squaredDistance <= this.squaredClickTolerance_; squaredDistance <= this.squaredClickTolerance_;
if (!this.shouldHandle_) {
return true;
}
} }
if (this.finishCoordinate_) { if (this.finishCoordinate_) {
@@ -505,9 +566,6 @@ Draw.prototype.startDrawing_ = function(event) {
this.sketchLineCoords_ = this.sketchCoords_[0]; this.sketchLineCoords_ = this.sketchCoords_[0];
} else { } else {
this.sketchCoords_ = [start.slice(), start.slice()]; this.sketchCoords_ = [start.slice(), start.slice()];
if (this.mode_ === Draw.Mode_.CIRCLE) {
this.sketchLineCoords_ = this.sketchCoords_;
}
} }
if (this.sketchLineCoords_) { if (this.sketchLineCoords_) {
this.sketchLine_ = new Feature( this.sketchLine_ = new Feature(
+41 -1
View File
@@ -103,6 +103,15 @@ describe('ol.interaction.Draw', function() {
expect(draw.freehandCondition_(event)).to.be(true); expect(draw.freehandCondition_(event)).to.be(true);
}); });
it('accepts a dragVertexDelay option', function() {
const draw = new Draw({
source: source,
type: 'LineString',
dragVertexDelay: 42
});
expect(draw.dragVertexDelay_).to.be(42);
});
}); });
describe('specifying a geometryName', function() { describe('specifying a geometryName', function() {
@@ -339,7 +348,6 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointermove', 20, 30); simulateEvent('pointermove', 20, 30);
// freehand // freehand
simulateEvent('pointerdown', 20, 30, true);
simulateEvent('pointermove', 20, 30, true); simulateEvent('pointermove', 20, 30, true);
simulateEvent('pointerdrag', 20, 30, true); simulateEvent('pointerdrag', 20, 30, true);
simulateEvent('pointermove', 30, 40, true); simulateEvent('pointermove', 30, 40, true);
@@ -396,6 +404,38 @@ describe('ol.interaction.Draw', function() {
expect(geometry.getCoordinates()).to.eql([[10, -20], [30, -20]]); expect(geometry.getCoordinates()).to.eql([[10, -20], [30, -20]]);
}); });
it('allows dragging of the vertex after dragVertexDelay', function(done) {
// first point
simulateEvent('pointermove', 10, 20);
simulateEvent('pointerdown', 10, 20);
simulateEvent('pointerup', 10, 20);
// second point, drag vertex
simulateEvent('pointermove', 15, 20);
simulateEvent('pointerdown', 15, 20);
setTimeout(function() {
simulateEvent('pointermove', 20, 10);
simulateEvent('pointerdrag', 20, 10);
simulateEvent('pointerup', 20, 10);
// third point
simulateEvent('pointermove', 30, 20);
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
// finish on third point
simulateEvent('pointerdown', 30, 20);
simulateEvent('pointerup', 30, 20);
const features = source.getFeatures();
expect(features).to.have.length(1);
const geometry = features[0].getGeometry();
expect(geometry).to.be.a(LineString);
expect(geometry.getCoordinates()).to.eql([[10, -20], [20, -10], [30, -20]]);
done();
}, 600);
});
it('triggers draw events', function() { it('triggers draw events', function() {
const ds = sinon.spy(); const ds = sinon.spy();
const de = sinon.spy(); const de = sinon.spy();