diff --git a/externs/olx.js b/externs/olx.js index 25c3589c3c..f258393c28 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2360,7 +2360,8 @@ olx.interaction.DragZoomOptions.prototype.style; * minPointsPerRing: (number|undefined), * style: (ol.style.Style|Array.|ol.style.StyleFunction|undefined), * geometryName: (string|undefined), - * condition: (ol.events.ConditionType|undefined)}} + * condition: (ol.events.ConditionType|undefined), + * freehandCondition: (ol.events.ConditionType|undefined)}} * @api */ olx.interaction.DrawOptions; @@ -2427,13 +2428,26 @@ olx.interaction.DrawOptions.prototype.geometryName; /** * A function that takes an {@link ol.MapBrowserEvent} and returns a boolean * to indicate whether that event should be handled. - * By default {@link ol.events.condition.noModifierKeys} adds a vertex. + * By default {@link ol.events.condition.noModifierKeys}, i.e. a click, adds a + * vertex or deactivates freehand drawing. * @type {ol.events.ConditionType|undefined} * @api */ olx.interaction.DrawOptions.prototype.condition; +/** + * Condition that activates freehand drawing for lines and polygons. This + * function takes an {@link ol.MapBrowserEvent} and returns a boolean to + * indicate whether that event should be handled. The default is + * {@link ol.events.condition.shiftKeyOnly}, meaning that the Shift key + * activates freehand drawing. + * @type {ol.events.ConditionType|undefined} + * @api + */ +olx.interaction.DrawOptions.prototype.freehandCondition; + + /** * @typedef {{condition: (ol.events.ConditionType|undefined), * duration: (number|undefined), diff --git a/src/ol/interaction/drawinteraction.js b/src/ol/interaction/drawinteraction.js index fd564a4d90..90078b19e7 100644 --- a/src/ol/interaction/drawinteraction.js +++ b/src/ol/interaction/drawinteraction.js @@ -98,6 +98,12 @@ ol.interaction.Draw = function(options) { */ this.downPx_ = null; + /** + * @type {boolean} + * @private + */ + this.freehand_ = false; + /** * Target source for drawn features. * @type {ol.source.Vector} @@ -212,6 +218,13 @@ ol.interaction.Draw = function(options) { this.condition_ = goog.isDef(options.condition) ? options.condition : ol.events.condition.noModifierKeys; + /** + * @private + * @type {ol.events.ConditionType} + */ + this.freehandCondition_ = goog.isDef(options.freehandCondition) ? + options.freehandCondition : ol.events.condition.shiftKeyOnly; + goog.events.listen(this, ol.Object.getChangeEventType(ol.interaction.InteractionProperty.ACTIVE), this.updateState_, false, this); @@ -247,8 +260,13 @@ ol.interaction.Draw.prototype.setMap = function(map) { * @api */ ol.interaction.Draw.handleEvent = function(mapBrowserEvent) { - var pass = true; - if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { + var pass = !this.freehand_; + if (this.freehand_ && + mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG) { + this.addToDrawing_(mapBrowserEvent); + pass = false; + } else if (mapBrowserEvent.type === + ol.MapBrowserEvent.EventType.POINTERMOVE) { pass = this.handlePointerMove_(mapBrowserEvent); } else if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.DBLCLICK) { pass = false; @@ -267,6 +285,15 @@ ol.interaction.Draw.handleDownEvent_ = function(event) { if (this.condition_(event)) { this.downPx_ = event.pixel; return true; + } else if ((this.mode_ === ol.interaction.DrawMode.LINE_STRING || + this.mode_ === ol.interaction.DrawMode.POLYGON) && + this.freehandCondition_(event)) { + this.downPx_ = event.pixel; + this.freehand_ = true; + if (goog.isNull(this.finishCoordinate_)) { + this.startDrawing_(event); + } + return true; } else { return false; } @@ -280,6 +307,7 @@ ol.interaction.Draw.handleDownEvent_ = function(event) { * @private */ ol.interaction.Draw.handleUpEvent_ = function(event) { + this.freehand_ = false; var downPx = this.downPx_; var clickPx = event.pixel; var dx = downPx[0] - clickPx[0]; @@ -290,8 +318,8 @@ ol.interaction.Draw.handleUpEvent_ = function(event) { this.handlePointerMove_(event); if (goog.isNull(this.finishCoordinate_)) { this.startDrawing_(event); - } else if (this.mode_ === ol.interaction.DrawMode.POINT || - this.mode_ === ol.interaction.DrawMode.CIRCLE && + } else if ((this.mode_ === ol.interaction.DrawMode.POINT || + this.mode_ === ol.interaction.DrawMode.CIRCLE) && !goog.isNull(this.finishCoordinate_) || this.atFinish_(event)) { this.finishDrawing(); @@ -355,7 +383,9 @@ ol.interaction.Draw.prototype.atFinish_ = function(event) { var pixel = event.pixel; var dx = pixel[0] - finishPixel[0]; var dy = pixel[1] - finishPixel[1]; - at = Math.sqrt(dx * dx + dy * dy) <= this.snapTolerance_; + var freehand = this.freehand_ && this.freehandCondition_(event); + var snapTolerance = freehand ? 1 : this.snapTolerance_; + at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance; if (at) { this.finishCoordinate_ = finishCoordinate; break; diff --git a/test/spec/ol/interaction/drawinteraction.test.js b/test/spec/ol/interaction/drawinteraction.test.js index 1d73c68bb4..dda8d2dcdc 100644 --- a/test/spec/ol/interaction/drawinteraction.test.js +++ b/test/spec/ol/interaction/drawinteraction.test.js @@ -225,6 +225,29 @@ describe('ol.interaction.Draw', function() { expect(geometry.getCoordinates()).to.eql([[10, -20], [30, -20]]); }); + it('supports freehand drawing for linestrings', function() { + // freehand sequence + simulateEvent('pointermove', 10, 20); + simulateEvent('pointerdown', 10, 20, true); + simulateEvent('pointermove', 20, 30, true); + simulateEvent('pointerdrag', 20, 30, true); + simulateEvent('pointermove', 20, 40, true); + simulateEvent('pointerdrag', 20, 40, true); + simulateEvent('pointerup', 20, 40, true); + + // finish on third point + simulateEvent('pointermove', 20, 40); + simulateEvent('pointerdown', 20, 40); + simulateEvent('pointerup', 20, 40); + + var features = source.getFeatures(); + expect(features).to.have.length(1); + var geometry = features[0].getGeometry(); + expect(geometry).to.be.a(ol.geom.LineString); + expect(geometry.getCoordinates()).to.eql( + [[10, -20], [20, -30], [20, -40]]); + }); + it('does not add a point with a significant drag', function() { // first point simulateEvent('pointermove', 10, 20); @@ -385,6 +408,30 @@ describe('ol.interaction.Draw', function() { ]); }); + it('supports freehand drawing for polygons', function() { + // freehand sequence + simulateEvent('pointermove', 10, 20); + simulateEvent('pointerdown', 10, 20, true); + simulateEvent('pointermove', 30, 20, true); + simulateEvent('pointerdrag', 30, 20, true); + simulateEvent('pointermove', 40, 10, true); + simulateEvent('pointerdrag', 40, 10, true); + simulateEvent('pointerup', 40, 10, true); + + // finish on last point + simulateEvent('pointerdown', 40, 10); + simulateEvent('pointerup', 40, 10); + + var features = source.getFeatures(); + expect(features).to.have.length(1); + var geometry = features[0].getGeometry(); + expect(geometry).to.be.a(ol.geom.Polygon); + + expect(geometry.getCoordinates()).to.eql([ + [[10, -20], [30, -20], [40, -10], [10, -20]] + ]); + }); + it('triggers draw events', function() { var ds = sinon.spy(); var de = sinon.spy();