diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index 0438fdcd3f..9cd3cf4f21 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -2,6 +2,35 @@ ### v3.11.0 +#### `ol.interaction.DragBox` and `ol.interaction.DragZoom` changes + +Styling is no longer done with `ol.Style`, but with pure CSS. The `style` constructor option is no longer required, and no longer available. Instead, there is a `className` option for the CSS selector. The default for `ol.interaction.DragBox` is `ol-dragbox`, and `ol.interaction.DragZoom` uses `ol-dragzoom`. If you previously had +```js +new ol.interaction.DragZoom({ + style: new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'red', + width: 3 + }), + fill: new ol.style.Fill({ + color: [255, 255, 255, 0.4] + }) + }) +}); +``` +you'll now just need +```js +new ol.interaction.DragZoom(); +``` +but with additional css: +```css +.ol-dragzoom { + border-color: red; + border-width: 3px; + background-color: rgba(255,255,255,0.4); +} +``` + ### v3.10.0 #### `ol.layer.Layer` changes diff --git a/css/ol.css b/css/ol.css index 65577bd10f..43c07a9bea 100644 --- a/css/ol.css +++ b/css/ol.css @@ -1,3 +1,8 @@ +.ol-box { + box-sizing: border-box; + border-radius: 2px; + border: 2px solid blue; +} .ol-mouse-position { top: 8px; diff --git a/examples/box-selection.css b/examples/box-selection.css new file mode 100644 index 0000000000..90c08960a4 --- /dev/null +++ b/examples/box-selection.css @@ -0,0 +1,4 @@ +.ol-dragbox { + background-color: rgba(255,255,255,0.4); + border-color: rgba(100,150,0,1); +} diff --git a/examples/box-selection.js b/examples/box-selection.js index ded817496a..69ed2a936a 100644 --- a/examples/box-selection.js +++ b/examples/box-selection.js @@ -8,9 +8,6 @@ goog.require('ol.layer.Tile'); goog.require('ol.layer.Vector'); goog.require('ol.source.OSM'); goog.require('ol.source.Vector'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); var vectorSource = new ol.source.Vector({ @@ -44,15 +41,7 @@ var selectedFeatures = select.getFeatures(); // a DragBox interaction used to select features by drawing boxes var dragBox = new ol.interaction.DragBox({ - condition: ol.events.condition.platformModifierKeyOnly, - style: new ol.style.Style({ - fill: new ol.style.Fill({ - color: [255, 255, 255, 0.4] - }), - stroke: new ol.style.Stroke({ - color: [100, 150, 0, 1] - }) - }) + condition: ol.events.condition.platformModifierKeyOnly }); map.addInteraction(dragBox); diff --git a/externs/olx.js b/externs/olx.js index bcfbb01c18..b7abb24f37 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2281,13 +2281,21 @@ olx.interaction.DragAndDropOptions.prototype.projection; /** - * @typedef {{condition: (ol.events.ConditionType|undefined), - * style: ol.style.Style}} + * @typedef {{className: (string|undefined), + * condition: (ol.events.ConditionType|undefined)}} * @api */ olx.interaction.DragBoxOptions; +/** + * CSS class name for styling the box. The default is `ol-dragbox`. + * @type {string|undefined} + * @api + */ +olx.interaction.DragBoxOptions.prototype.className; + + /** * A function that takes an {@link ol.MapBrowserEvent} and returns a boolean * to indicate whether that event should be handled. @@ -2298,14 +2306,6 @@ olx.interaction.DragBoxOptions; olx.interaction.DragBoxOptions.prototype.condition; -/** - * Style for the box. - * @type {ol.style.Style} - * @api - */ -olx.interaction.DragBoxOptions.prototype.style; - - /** * @typedef {{kinetic: (ol.Kinetic|undefined)}} * @api @@ -2374,14 +2374,22 @@ olx.interaction.DragRotateOptions.prototype.duration; /** - * @typedef {{condition: (ol.events.ConditionType|undefined), - * duration: (number|undefined), - * style: (ol.style.Style|undefined)}} + * @typedef {{className: (string|undefined), + * condition: (ol.events.ConditionType|undefined), + * duration: (number|undefined)}} * @api */ olx.interaction.DragZoomOptions; +/** + * CSS class name for styling the box. The default is `ol-dragzoom`. + * @type {string|undefined} + * @api + */ +olx.interaction.DragZoomOptions.prototype.className; + + /** * A function that takes an {@link ol.MapBrowserEvent} and returns a boolean * to indicate whether that event should be handled. @@ -2400,14 +2408,6 @@ olx.interaction.DragZoomOptions.prototype.condition; olx.interaction.DragZoomOptions.prototype.duration; -/** - * Style for the box. - * @type {ol.style.Style|undefined} - * @api - */ -olx.interaction.DragZoomOptions.prototype.style; - - /** * @typedef {{clickTolerance: (number|undefined), * features: (ol.Collection.|undefined), @@ -5995,7 +5995,7 @@ olx.style.TextOptions.prototype.rotation; /** - * Text content. + * Text content. * @type {string|undefined} * @api */ @@ -6003,7 +6003,7 @@ olx.style.TextOptions.prototype.text; /** - * Text alignment. Possible values: 'left', 'right', 'center', 'end' or 'start'. + * Text alignment. Possible values: 'left', 'right', 'center', 'end' or 'start'. * Default is 'start'. * @type {string|undefined} * @api @@ -6012,7 +6012,7 @@ olx.style.TextOptions.prototype.textAlign; /** - * Text base line. Possible values: 'bottom', 'top', 'middle', 'alphabetic', + * Text base line. Possible values: 'bottom', 'top', 'middle', 'alphabetic', * 'hanging', 'ideographic'. Default is 'alphabetic'. * @type {string|undefined} * @api diff --git a/src/ol/interaction/dragboxinteraction.js b/src/ol/interaction/dragboxinteraction.js index 7aa5eb19be..ca044f1067 100644 --- a/src/ol/interaction/dragboxinteraction.js +++ b/src/ol/interaction/dragboxinteraction.js @@ -93,17 +93,11 @@ ol.interaction.DragBox = function(opt_options) { var options = opt_options ? opt_options : {}; - /** - * @private - * @type {ol.style.Style} - */ - var style = options.style ? options.style : null; - /** * @type {ol.render.Box} * @private */ - this.box_ = new ol.render.Box(style); + this.box_ = new ol.render.Box(options.className || 'ol-dragbox'); /** * @type {ol.Pixel} diff --git a/src/ol/interaction/dragzoominteraction.js b/src/ol/interaction/dragzoominteraction.js index 96bdc04ce5..939dfeb561 100644 --- a/src/ol/interaction/dragzoominteraction.js +++ b/src/ol/interaction/dragzoominteraction.js @@ -6,8 +6,6 @@ goog.require('ol.easing'); goog.require('ol.events.condition'); goog.require('ol.extent'); goog.require('ol.interaction.DragBox'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); @@ -17,6 +15,9 @@ goog.require('ol.style.Style'); * normally combined with an {@link ol.events.condition} that limits * it to when a key, shift by default, is held down. * + * To change the style of the box, use CSS and the `.ol-dragzoom` selector, or + * your custom one configured with `className`. + * * @constructor * @extends {ol.interaction.DragBox} * @param {olx.interaction.DragZoomOptions=} opt_options Options. @@ -34,20 +35,9 @@ ol.interaction.DragZoom = function(opt_options) { */ this.duration_ = options.duration !== undefined ? options.duration : 200; - /** - * @private - * @type {ol.style.Style} - */ - var style = options.style ? - options.style : new ol.style.Style({ - stroke: new ol.style.Stroke({ - color: [0, 0, 255, 1] - }) - }); - goog.base(this, { condition: condition, - style: style + className: options.className || 'ol-dragzoom' }); }; diff --git a/src/ol/render/box.js b/src/ol/render/box.js index 7a416a392a..a0541d806f 100644 --- a/src/ol/render/box.js +++ b/src/ol/render/box.js @@ -4,18 +4,30 @@ goog.provide('ol.render.Box'); goog.require('goog.Disposable'); goog.require('goog.asserts'); -goog.require('goog.events'); goog.require('ol.geom.Polygon'); -goog.require('ol.render.EventType'); /** * @constructor * @extends {goog.Disposable} - * @param {ol.style.Style} style Style. + * @param {string} className CSS class name. */ -ol.render.Box = function(style) { +ol.render.Box = function(className) { + + /** + * @type {ol.geom.Polygon} + * @private + */ + this.geometry_ = null; + + /** + * @type {HTMLDivElement} + * @private + */ + this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); + this.element_.style.position = 'absolute'; + this.element_.className = 'ol-box ' + className; /** * @private @@ -23,12 +35,6 @@ ol.render.Box = function(style) { */ this.map_ = null; - /** - * @private - * @type {goog.events.Key} - */ - this.postComposeListenerKey_ = null; - /** * @private * @type {ol.Pixel} @@ -41,27 +47,68 @@ ol.render.Box = function(style) { */ this.endPixel_ = null; - /** - * @private - * @type {ol.geom.Polygon} - */ - this.geometry_ = null; - - /** - * @private - * @type {ol.style.Style} - */ - this.style_ = style; - }; goog.inherits(ol.render.Box, goog.Disposable); /** - * @private - * @return {ol.geom.Polygon} Geometry. + * @inheritDoc */ -ol.render.Box.prototype.createGeometry_ = function() { +ol.render.Box.prototype.disposeInternal = function() { + this.setMap(null); + goog.base(this, 'disposeInternal'); +}; + + +/** + * @private + */ +ol.render.Box.prototype.render_ = function() { + var startPixel = this.startPixel_; + var endPixel = this.endPixel_; + goog.asserts.assert(startPixel, 'this.startPixel_ must be truthy'); + goog.asserts.assert(endPixel, 'this.endPixel_ must be truthy'); + var px = 'px'; + var style = this.element_.style; + style.left = Math.min(startPixel[0], endPixel[0]) + px; + style.top = Math.min(startPixel[1], endPixel[1]) + px; + style.width = Math.abs(endPixel[0] - startPixel[0]) + px; + style.height = Math.abs(endPixel[1] - startPixel[1]) + px; +}; + + +/** + * @param {ol.Map} map Map. + */ +ol.render.Box.prototype.setMap = function(map) { + if (this.map_) { + this.map_.getViewport().removeChild(this.element_); + var style = this.element_.style; + style.left = style.top = style.width = style.height = 'inherit'; + } + this.map_ = map; + if (this.map_) { + this.map_.getViewport().appendChild(this.element_); + } +}; + + +/** + * @param {ol.Pixel} startPixel Start pixel. + * @param {ol.Pixel} endPixel End pixel. + */ +ol.render.Box.prototype.setPixels = function(startPixel, endPixel) { + this.startPixel_ = startPixel; + this.endPixel_ = endPixel; + this.createOrUpdateGeometry(); + this.render_(); +}; + + +/** + * Creates or updates the cached geometry. + */ +ol.render.Box.prototype.createOrUpdateGeometry = function() { goog.asserts.assert(this.startPixel_, 'this.startPixel_ must be truthy'); goog.asserts.assert(this.endPixel_, @@ -78,33 +125,11 @@ ol.render.Box.prototype.createGeometry_ = function() { var coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); // close the polygon coordinates[4] = coordinates[0].slice(); - return new ol.geom.Polygon([coordinates]); -}; - - -/** - * @inheritDoc - */ -ol.render.Box.prototype.disposeInternal = function() { - this.setMap(null); -}; - - -/** - * @param {ol.render.Event} event Event. - * @private - */ -ol.render.Box.prototype.handleMapPostCompose_ = function(event) { - var geometry = this.geometry_; - goog.asserts.assert(geometry, 'geometry should be defined'); - var style = this.style_; - goog.asserts.assert(style, 'style must be truthy'); - // use drawAsync(Infinity) to draw above everything - event.vectorContext.drawAsync(Infinity, function(render) { - render.setFillStrokeStyle(style.getFill(), style.getStroke()); - render.setTextStyle(style.getText()); - render.drawPolygonGeometry(geometry, null); - }); + if (!this.geometry_) { + this.geometry_ = new ol.geom.Polygon([coordinates]); + } else { + this.geometry_.setCoordinates([coordinates]); + } }; @@ -114,45 +139,3 @@ ol.render.Box.prototype.handleMapPostCompose_ = function(event) { ol.render.Box.prototype.getGeometry = function() { return this.geometry_; }; - - -/** - * @private - */ -ol.render.Box.prototype.requestMapRenderFrame_ = function() { - if (this.map_ && this.startPixel_ && this.endPixel_) { - this.map_.render(); - } -}; - - -/** - * @param {ol.Map} map Map. - */ -ol.render.Box.prototype.setMap = function(map) { - if (this.postComposeListenerKey_) { - goog.events.unlistenByKey(this.postComposeListenerKey_); - this.postComposeListenerKey_ = null; - this.map_.render(); - this.map_ = null; - } - this.map_ = map; - if (this.map_) { - this.postComposeListenerKey_ = goog.events.listen( - map, ol.render.EventType.POSTCOMPOSE, this.handleMapPostCompose_, false, - this); - this.requestMapRenderFrame_(); - } -}; - - -/** - * @param {ol.Pixel} startPixel Start pixel. - * @param {ol.Pixel} endPixel End pixel. - */ -ol.render.Box.prototype.setPixels = function(startPixel, endPixel) { - this.startPixel_ = startPixel; - this.endPixel_ = endPixel; - this.geometry_ = this.createGeometry_(); - this.requestMapRenderFrame_(); -}; diff --git a/test/spec/ol/interaction/dragzoominteraction.test.js b/test/spec/ol/interaction/dragzoominteraction.test.js index 61b0758fe4..987efaff3b 100644 --- a/test/spec/ol/interaction/dragzoominteraction.test.js +++ b/test/spec/ol/interaction/dragzoominteraction.test.js @@ -43,6 +43,14 @@ describe('ol.interaction.DragZoom', function() { var instance = new ol.interaction.DragZoom(); expect(instance).to.be.an(ol.interaction.DragZoom); }); + it('sets "ol-dragzoom" as box className', function() { + var instance = new ol.interaction.DragZoom(); + expect(instance.box_.element_.className).to.be('ol-box ol-dragzoom'); + }); + it('sets a custom box className', function() { + var instance = new ol.interaction.DragZoom({className: 'test-dragzoom'}); + expect(instance.box_.element_.className).to.be('ol-box test-dragzoom'); + }); }); diff --git a/test/spec/ol/render/box.test.js b/test/spec/ol/render/box.test.js new file mode 100644 index 0000000000..958eee52de --- /dev/null +++ b/test/spec/ol/render/box.test.js @@ -0,0 +1,65 @@ +goog.provide('ol.test.render.Box'); + +describe('ol.render.Box', function() { + + var box, map, target; + + beforeEach(function() { + box = new ol.render.Box('test-box'); + + target = document.createElement('div'); + document.body.appendChild(target); + + map = new ol.Map({ + target: target, + view: new ol.View({ + center: [0, 0], + zoom: 0 + }) + }); + map.renderSync(); + box.setMap(map); + }); + + afterEach(function() { + goog.dispose(map); + document.body.removeChild(target); + }); + + describe('Constructor', function() { + it('creates an absolutely positioned DIV with a className', function() { + expect(box.element_).to.be.a(HTMLDivElement); + expect(box.element_.style.position).to.be('absolute'); + expect(box.element_.className).to.be('ol-box test-box'); + expect(box.element_.style.position).to.be('absolute'); + }); + }); + + describe('#setPixels()', function() { + it('applies correct styles for a box', function() { + box.setPixels([1, 2], [4, 8]); + expect(box.element_.style.left).to.be('1px'); + expect(box.element_.style.top).to.be('2px'); + expect(box.element_.style.width).to.be('3px'); + expect(box.element_.style.height).to.be('6px'); + }); + it('applies correct styles for a flipped box', function() { + box.setPixels([4, 8], [1, 2]); + expect(box.element_.style.left).to.be('1px'); + expect(box.element_.style.top).to.be('2px'); + expect(box.element_.style.width).to.be('3px'); + expect(box.element_.style.height).to.be('6px'); + }); + it('creates a polygon geometry', function() { + expect(box.getGeometry()).to.be(null); + box.setPixels([1, 2], [3, 4]); + expect(box.getGeometry()).to.be.a(ol.geom.Polygon); + }); + }); + +}); + +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.geom.Polygon'); +goog.require('ol.render.Box');