Merge pull request #7703 from ahocevar/draw-state
Improved drawing experience on touch devices
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -15,6 +15,7 @@ export default {
|
||||
CHANGE: 'change',
|
||||
|
||||
CLEAR: 'clear',
|
||||
CONTEXTMENU: 'contextmenu',
|
||||
CLICK: 'click',
|
||||
DBLCLICK: 'dblclick',
|
||||
DRAGENTER: 'dragenter',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user