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

View File

@@ -2,6 +2,13 @@
### 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
Because relying on a globally available proj4 is not practical with ES modules, we have made a change to the way we integrate proj4:

View File

@@ -2356,6 +2356,7 @@ olx.interaction.DragZoomOptions.prototype.out;
* @typedef {{clickTolerance: (number|undefined),
* features: (ol.Collection.<ol.Feature>|undefined),
* source: (ol.source.Vector|undefined),
* dragVertexDelay: (number|undefined),
* snapTolerance: (number|undefined),
* type: (ol.geom.GeometryType|string),
* stopClick: (boolean|undefined),
@@ -2401,6 +2402,14 @@ olx.interaction.DrawOptions.prototype.features;
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`.
* @type {number|undefined}

View File

@@ -267,6 +267,8 @@ const PluggableMap = function(options) {
*/
this.keyHandlerKeys_ = null;
_ol_events_.listen(this.viewport_, EventType.CONTEXTMENU,
this.handleBrowserEvent, this);
_ol_events_.listen(this.viewport_, EventType.WHEEL,
this.handleBrowserEvent, this);
_ol_events_.listen(this.viewport_, EventType.MOUSEWHEEL,
@@ -491,6 +493,8 @@ PluggableMap.prototype.addOverlayInternal_ = function(overlay) {
*/
PluggableMap.prototype.disposeInternal = function() {
this.mapBrowserEventHandler_.dispose();
_ol_events_.unlisten(this.viewport_, EventType.CONTEXTMENU,
this.handleBrowserEvent, this);
_ol_events_.unlisten(this.viewport_, EventType.WHEEL,
this.handleBrowserEvent, this);
_ol_events_.unlisten(this.viewport_, EventType.MOUSEWHEEL,

View File

@@ -15,6 +15,7 @@ export default {
CHANGE: 'change',
CLEAR: 'clear',
CONTEXTMENU: 'contextmenu',
CLICK: 'click',
DBLCLICK: 'dblclick',
DRAGENTER: 'dragenter',

View File

@@ -2,8 +2,10 @@
* @module ol/interaction/Draw
*/
import {inherits} from '../index.js';
import EventType from '../events/EventType.js';
import Feature from '../Feature.js';
import MapBrowserEventType from '../MapBrowserEventType.js';
import MapBrowserPointerEvent from '../MapBrowserPointerEvent.js';
import BaseObject from '../Object.js';
import _ol_coordinate_ from '../coordinate.js';
import _ol_events_ from '../events.js';
@@ -17,6 +19,7 @@ import LineString from '../geom/LineString.js';
import MultiLineString from '../geom/MultiLineString.js';
import MultiPoint from '../geom/MultiPoint.js';
import MultiPolygon from '../geom/MultiPolygon.js';
import MouseSource from '../pointer/MouseSource.js';
import Point from '../geom/Point.js';
import Polygon, {fromCircle, makeRegular} from '../geom/Polygon.js';
import DrawEventType from '../interaction/DrawEventType.js';
@@ -56,6 +59,18 @@ const Draw = function(options) {
*/
this.downPx_ = null;
/**
* @type {number}
* @private
*/
this.downTimeout_;
/**
* @type {number}
* @private
*/
this.lastDragTime_;
/**
* @type {boolean}
* @private
@@ -191,6 +206,12 @@ const Draw = function(options) {
*/
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
* linestrings).
@@ -255,7 +276,8 @@ const Draw = function(options) {
wrapX: options.wrapX ? options.wrapX : false
}),
style: options.style ? options.style :
Draw.getDefaultStyleFunction()
Draw.getDefaultStyleFunction(),
updateWhileInteracting: true
});
/**
@@ -322,8 +344,27 @@ Draw.prototype.setMap = function(map) {
* @api
*/
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);
let move = event.type === MapBrowserEventType.POINTERMOVE;
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_ &&
event.type === MapBrowserEventType.POINTERDRAG &&
this.sketchFeature_ !== null) {
@@ -332,11 +373,18 @@ Draw.handleEvent = function(event) {
} else if (this.freehand_ &&
event.type === MapBrowserEventType.POINTERDOWN) {
pass = false;
} else if (event.type === MapBrowserEventType.POINTERMOVE) {
pass = this.handlePointerMove_(event);
} else if (move) {
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) {
pass = false;
}
return PointerInteraction.handleEvent.call(this, event) && pass;
};
@@ -357,6 +405,11 @@ Draw.handleDownEvent_ = function(event) {
}
return true;
} 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;
return true;
} else {
@@ -374,6 +427,11 @@ Draw.handleDownEvent_ = function(event) {
Draw.handleUpEvent_ = function(event) {
let pass = true;
if (this.downTimeout_) {
clearTimeout(this.downTimeout_);
this.downTimeout_ = undefined;
}
this.handlePointerMove_(event);
const circleMode = this.mode_ === Draw.Mode_.CIRCLE;
@@ -423,6 +481,9 @@ Draw.prototype.handlePointerMove_ = function(event) {
this.shouldHandle_ = this.freehand_ ?
squaredDistance > this.squaredClickTolerance_ :
squaredDistance <= this.squaredClickTolerance_;
if (!this.shouldHandle_) {
return true;
}
}
if (this.finishCoordinate_) {
@@ -505,9 +566,6 @@ Draw.prototype.startDrawing_ = function(event) {
this.sketchLineCoords_ = this.sketchCoords_[0];
} else {
this.sketchCoords_ = [start.slice(), start.slice()];
if (this.mode_ === Draw.Mode_.CIRCLE) {
this.sketchLineCoords_ = this.sketchCoords_;
}
}
if (this.sketchLineCoords_) {
this.sketchLine_ = new Feature(

View File

@@ -103,6 +103,15 @@ describe('ol.interaction.Draw', function() {
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() {
@@ -339,7 +348,6 @@ describe('ol.interaction.Draw', function() {
simulateEvent('pointermove', 20, 30);
// freehand
simulateEvent('pointerdown', 20, 30, true);
simulateEvent('pointermove', 20, 30, true);
simulateEvent('pointerdrag', 20, 30, true);
simulateEvent('pointermove', 30, 40, true);
@@ -396,6 +404,38 @@ describe('ol.interaction.Draw', function() {
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() {
const ds = sinon.spy();
const de = sinon.spy();