From bf9472ac57391d804c6085b271829322acad248f Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 18 Mar 2022 21:58:26 +0100 Subject: [PATCH] Add loadstart and loadend map events --- src/ol/MapEventType.js | 16 +++++- src/ol/PluggableMap.js | 49 +++++++++++------ test/browser/spec/ol/Map.test.js | 91 ++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 16 deletions(-) diff --git a/src/ol/MapEventType.js b/src/ol/MapEventType.js index 08ac0645c2..be63b9c68d 100644 --- a/src/ol/MapEventType.js +++ b/src/ol/MapEventType.js @@ -26,8 +26,22 @@ export default { * @api */ MOVEEND: 'moveend', + + /** + * Triggered when loading of additional map data (tiles, images, features) starts. + * @event module:ol/render/Event~RenderEvent#loadstart + * @api + */ + LOADSTART: 'loadstart', + + /** + * Triggered when loading of additional map data has completed. + * @event module:ol/render/Event~RenderEvent#loadend + * @api + */ + LOADEND: 'loadend', }; /*** - * @typedef {'postrender'|'movestart'|'moveend'} Types + * @typedef {'postrender'|'movestart'|'moveend'|'loadstart'|'loadend'} Types */ diff --git a/src/ol/PluggableMap.js b/src/ol/PluggableMap.js index 2cade9efc8..809843eae5 100644 --- a/src/ol/PluggableMap.js +++ b/src/ol/PluggableMap.js @@ -211,10 +211,16 @@ class PluggableMap extends BaseObject { /** * @private - * @type {boolean} + * @type {boolean|undefined} */ this.renderComplete_; + /** + * @private + * @type {boolean} + */ + this.loaded_ = true; + /** @private */ this.boundHandleBrowserEvent_ = this.handleBrowserEvent.bind(this); @@ -1187,17 +1193,26 @@ class PluggableMap extends BaseObject { } } - if ( - frameState && - this.renderer_ && - this.hasListener(RenderEventType.RENDERCOMPLETE) && - !frameState.animate && - this.renderComplete_ - ) { - this.renderer_.dispatchRenderEvent( - RenderEventType.RENDERCOMPLETE, - frameState - ); + if (frameState && this.renderer_ && !frameState.animate) { + if (this.renderComplete_ === true) { + if (this.hasListener(RenderEventType.RENDERCOMPLETE)) { + this.renderer_.dispatchRenderEvent( + RenderEventType.RENDERCOMPLETE, + frameState + ); + } + if (this.loaded_ === false) { + this.loaded_ = true; + this.dispatchEvent( + new MapEvent(MapEventType.LOADEND, this, frameState) + ); + } + } else if (this.loaded_ === true) { + this.loaded_ = false; + this.dispatchEvent( + new MapEvent(MapEventType.LOADSTART, this, frameState) + ); + } } const postRenderFunctions = this.postRenderFunctions_; @@ -1573,9 +1588,13 @@ class PluggableMap extends BaseObject { this.dispatchEvent(new MapEvent(MapEventType.POSTRENDER, this, frameState)); this.renderComplete_ = - !this.tileQueue_.getTilesLoading() && - !this.tileQueue_.getCount() && - !this.getLoadingOrNotReady(); + this.hasListener(MapEventType.LOADSTART) || + this.hasListener(MapEventType.LOADEND) || + this.hasListener(RenderEventType.RENDERCOMPLETE) + ? !this.tileQueue_.getTilesLoading() && + !this.tileQueue_.getCount() && + !this.getLoadingOrNotReady() + : undefined; if (!this.postRenderTimeoutHandle_) { this.postRenderTimeoutHandle_ = setTimeout(() => { diff --git a/test/browser/spec/ol/Map.test.js b/test/browser/spec/ol/Map.test.js index 480dbe6b2d..79ed62456d 100644 --- a/test/browser/spec/ol/Map.test.js +++ b/test/browser/spec/ol/Map.test.js @@ -505,6 +505,97 @@ describe('ol/Map', function () { }); }); + describe('loadstart/loadend event sequence', function () { + let map; + beforeEach(function () { + const target = document.createElement('div'); + target.style.width = '100px'; + target.style.height = '100px'; + document.body.appendChild(target); + map = new Map({ + target: target, + layers: [ + new TileLayer({ + opacity: 0.5, + source: new XYZ({ + url: 'spec/ol/data/osm-{z}-{x}-{y}.png', + }), + }), + new ImageLayer({ + source: new ImageStatic({ + url: 'spec/ol/data/osm-0-0-0.png', + imageExtent: getProjection('EPSG:3857').getExtent(), + projection: 'EPSG:3857', + }), + }), + new VectorLayer({ + source: new VectorSource({ + url: 'spec/ol/data/point.json', + format: new GeoJSON(), + }), + }), + new VectorLayer({ + source: new VectorSource({ + url: 'spec/ol/data/point.json', + format: new GeoJSON(), + strategy: tileStrategy(createXYZ()), + }), + }), + new VectorLayer({ + source: new VectorSource({ + features: [new Feature(new Point([0, 0]))], + }), + }), + new VectorLayer({ + source: new VectorSource({ + loader: function (extent, resolution, projection) { + this.addFeature(new Feature(new Point([0, 0]))); + }, + }), + }), + new WebGLPointsLayer({ + source: new VectorSource({ + features: [new Feature(new Point([0, 0]))], + }), + style: { + symbol: { + color: 'red', + symbolType: 'circle', + }, + }, + }), + ], + }); + }); + + afterEach(function () { + document.body.removeChild(map.getTargetElement()); + map.setTarget(null); + map.dispose(); + map.getLayers().forEach((layer) => layer.dispose()); + }); + + it('is a reliable start-end sequence', function (done) { + const layers = map.getLayers().getArray(); + expect(layers[6].getRenderer().ready).to.be(false); + let loading = 0; + map.on('loadstart', () => { + map.getView().setZoom(0.1); + loading++; + }); + map.on('loadend', () => { + expect(loading).to.be(1); + done(); + }); + map.setView( + new View({ + center: [0, 0], + zoom: 0, + }) + ); + }); + }); + describe('#getFeaturesAtPixel', function () { let target, map, layer; beforeEach(function () {