From 12099bab5611a080f3a1ee7b05aa63f90ba4f0cb Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 4 Oct 2012 11:12:48 +0200 Subject: [PATCH 1/9] Implement @elemoine's requestRenderFrame architecture --- src/ol/map.js | 110 ++++++++------------------ src/ol/renderer/dom/dommaprenderer.js | 4 +- src/ol/renderer/maprenderer.js | 2 +- 3 files changed, 35 insertions(+), 81 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6ad2e77d52..d674f05d3a 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -8,6 +8,7 @@ goog.provide('ol.MapProperty'); goog.provide('ol.RendererHint'); goog.require('goog.array'); +goog.require('goog.async.AnimationDelay'); goog.require('goog.debug.Logger'); goog.require('goog.dispose'); goog.require('goog.dom'); @@ -22,8 +23,6 @@ goog.require('goog.events.MouseWheelEvent'); goog.require('goog.events.MouseWheelHandler'); goog.require('goog.events.MouseWheelHandler.EventType'); goog.require('goog.functions'); -goog.require('goog.fx.anim'); -goog.require('goog.fx.anim.Animated'); goog.require('goog.object'); goog.require('ol.BrowserFeature'); goog.require('ol.Collection'); @@ -115,7 +114,6 @@ ol.MapProperty = { /** * @constructor * @extends {ol.Object} - * @implements {goog.fx.anim.Animated} * @param {ol.MapOptions} mapOptions Map options. */ ol.Map = function(mapOptions) { @@ -146,15 +144,11 @@ ol.Map = function(mapOptions) { /** * @private - * @type {boolean} + * @type {goog.async.AnimationDelay} */ - this.animatedRenderer_ = false; - - /** - * @private - * @type {number} - */ - this.animatingCount_ = 0; + this.animationDelay_ = + new goog.async.AnimationDelay(this.renderFrame_, undefined, this); + this.registerDisposable(this.animationDelay_); /** * @private @@ -652,14 +646,6 @@ ol.Map.prototype.handleBrowserWindowResize = function() { }; -/** - * @return {boolean} Is animating. - */ -ol.Map.prototype.isAnimating = function() { - return this.animatingCount_ > 0; -}; - - /** * @return {boolean} Is defined. */ @@ -670,17 +656,6 @@ ol.Map.prototype.isDef = function() { }; -/** - * @inheritDoc - */ -ol.Map.prototype.onAnimationFrame = function() { - if (goog.DEBUG) { - this.logger.info('onAnimationFrame'); - } - this.renderFrame_(); -}; - - /** * @private */ @@ -704,33 +679,43 @@ ol.Map.prototype.recalculateTransforms_ = function() { * Render. */ ol.Map.prototype.render = function() { - if (this.animatingCount_ < 1) { - if (this.freezeRenderingCount_ === 0) { - this.renderFrame_(); - } else { - this.dirty_ = true; - } + if (this.animationDelay_.isActive()) { + // pass + } else if (this.freezeRenderingCount_ === 0) { + this.animationDelay_.fire(); + } else { + this.dirty_ = true; } }; /** + * Request that render be called some time in the future. + */ +ol.Map.prototype.requestRenderFrame = function() { + if (this.freezeRenderingCount_ === 0) { + if (!this.animationDelay_.isActive()) { + this.animationDelay_.start(); + } + } else { + this.dirty_ = true; + } +}; + + +/** + * @param {number} time Time. * @private */ -ol.Map.prototype.renderFrame_ = function() { +ol.Map.prototype.renderFrame_ = function(time) { + if (this.freezeRenderingCount_ != 0) { + return; + } if (goog.DEBUG) { this.logger.info('renderFrame_'); } - var animatedRenderer = this.renderer_.render(); + this.renderer_.render(); this.dirty_ = false; - if (animatedRenderer != this.animatedRenderer_) { - if (animatedRenderer) { - this.startAnimating(); - } else { - this.stopAnimating(); - } - this.animatedRenderer_ = animatedRenderer; - } if (goog.DEBUG) { this.logger.info('postrender'); } @@ -852,42 +837,13 @@ goog.exportProperty( ol.Map.prototype.setUserProjection); -/** - * Start animating. - */ -ol.Map.prototype.startAnimating = function() { - if (++this.animatingCount_ == 1) { - if (goog.DEBUG) { - this.logger.info('startAnimating'); - } - goog.fx.anim.registerAnimation(this); - } -}; - - -/** - * Stop animating. - */ -ol.Map.prototype.stopAnimating = function() { - goog.asserts.assert(this.animatingCount_ > 0); - if (--this.animatingCount_ === 0) { - if (goog.DEBUG) { - this.logger.info('stopAnimating'); - } - goog.fx.anim.unregisterAnimation(this); - } -}; - - /** * Unfreeze rendering. */ ol.Map.prototype.unfreezeRendering = function() { goog.asserts.assert(this.freezeRenderingCount_ > 0); - if (--this.freezeRenderingCount_ === 0 && - this.animatingCount_ < 1 && - this.dirty_) { - this.renderFrame_(); + if (--this.freezeRenderingCount_ === 0 && this.dirty_) { + this.animationDelay_.fire(); } }; diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index 82f8d5cd1c..be2ff3f869 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -184,13 +184,11 @@ ol.renderer.dom.Map.prototype.handleSizeChanged = function() { /** * Render the map. Sets up the layers pane on first render and adjusts its * position as needed on subsequent calls. - * - * @return {boolean} Animating. */ ol.renderer.dom.Map.prototype.render = function() { var map = this.getMap(); if (!map.isDef()) { - return false; + return; } var mapCenter = map.getCenter(); diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 55b0fd5e08..882b971f32 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -309,7 +309,7 @@ ol.renderer.Map.prototype.removeLayerRenderer = function(layer) { /** - * @return {boolean} Animating. + * Render. */ ol.renderer.Map.prototype.render = goog.functions.FALSE; From 783dfe0618b3b844dd6cec7e30b70c77f40f6b4c Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 4 Oct 2012 11:13:40 +0200 Subject: [PATCH 2/9] Port WebGL renderer to requestRenderFrame --- src/ol/renderer/webgl/webgllayerrenderer.js | 1 + src/ol/renderer/webgl/webglmaprenderer.js | 14 ++++- .../renderer/webgl/webgltilelayerrenderer.js | 53 ++++++------------- 3 files changed, 30 insertions(+), 38 deletions(-) diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 90e457fa2d..638ae00424 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -112,5 +112,6 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction; /** * Render. + * @return {boolean} Request render frame. */ ol.renderer.webgl.Layer.prototype.render = goog.abstractMethod; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 13ea482b40..16cf4650b8 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -604,9 +604,17 @@ ol.renderer.webgl.Map.prototype.removeLayerRenderer = function(layer) { ol.renderer.webgl.Map.prototype.render = function() { if (!this.getMap().isDef()) { - return false; + return; } + var requestRenderFrame = false; + + this.forEachReadyVisibleLayer(function(layer, layerRenderer) { + if (layerRenderer.render()) { + requestRenderFrame = true; + } + }); + var size = /** @type {ol.Size} */ this.getMap().getSize(); if (!this.canvasSize_.equals(size)) { this.canvas_.width = size.width; @@ -684,7 +692,9 @@ ol.renderer.webgl.Map.prototype.render = function() { gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); }, this); - return animate; + if (requestRenderFrame) { + this.getMap().requestRenderFrame(); + } }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 8722ce4064..c849b31362 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -141,12 +141,6 @@ ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) { */ this.framebufferDimension_ = undefined; - /** - * @private - * @type {Object.} - */ - this.tileChangeListenerKeys_ = {}; - /** * @private * @type {goog.vec.Mat4.AnyType} @@ -276,19 +270,6 @@ ol.renderer.webgl.TileLayer.prototype.getTexture = function() { }; -/** - * @param {goog.events.Event} event Event. - * @protected - */ -ol.renderer.webgl.TileLayer.prototype.handleTileChange = function(event) { - var tile = /** @type {ol.Tile} */ (event.target); - var tileKey = goog.getUid(tile); - goog.asserts.assert(tileKey in this.tileChangeListenerKeys_); - delete this.tileChangeListenerKeys_[tileKey]; - this.dispatchChangeEvent(); -}; - - /** * @inheritDoc */ @@ -306,7 +287,7 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() { */ ol.renderer.webgl.TileLayer.prototype.render = function() { - var animate = false; + var requestRenderFrame = false; var mapRenderer = this.getMapRenderer(); var map = this.getMap(); @@ -413,26 +394,26 @@ ol.renderer.webgl.TileLayer.prototype.render = function() { var tile = tileSource.getTile(tileCoord); if (goog.isNull(tile)) { - // FIXME consider returning here as this is outside the source's extent - } else if (tile.getState() == ol.TileState.LOADED) { - if (mapRenderer.isImageTextureLoaded(tile.getImage())) { + return; + } + + var tileState = tile.getState(); + if (tileState == ol.TileState.IDLE) { + tile.load(); + } else if (tileState == ol.TileState.LOADED) { + var image = tile.getImage(); + if (mapRenderer.isImageTextureLoaded(image)) { tilesToDrawByZ[z][tileCoord.toString()] = tile; return; } else { - imagesToLoad.push(tile.getImage()); - allTilesLoaded = false; + imagesToLoad.push(image); } - } else { - var tileKey = goog.getUid(tile); - if (!(tileKey in this.tileChangeListenerKeys_)) { - tile.load(); - // FIXME will need to handle aborts as well - this.tileChangeListenerKeys_[tileKey] = goog.events.listen(tile, - goog.events.EventType.CHANGE, this.handleTileChange, false, this); - } - allTilesLoaded = false; + } else if (tileState == ol.TileState.ERROR) { + return; } + allTilesLoaded = false; + // FIXME this could be more efficient about filling partial holes tileGrid.forEachTileCoordParentTileRange( tileCoord, @@ -497,7 +478,6 @@ ol.renderer.webgl.TileLayer.prototype.render = function() { this.logger.info('uploaded textures'); } }, mapRenderer, imagesToLoad)); - animate = true; } if (allTilesLoaded) { @@ -506,6 +486,7 @@ ol.renderer.webgl.TileLayer.prototype.render = function() { } else { this.renderedTileRange_ = null; this.renderedFramebufferExtent_ = null; + requestRenderFrame = true; } } @@ -531,6 +512,6 @@ ol.renderer.webgl.TileLayer.prototype.render = function() { -0.5, 0); - return animate; + return requestRenderFrame; }; From 756792b8dbde071c85821d6a004ed5f267651b40 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 4 Oct 2012 12:03:52 +0200 Subject: [PATCH 3/9] Pass time to render functions and rename them to renderFrame_ --- src/ol/map.js | 4 ++-- src/ol/renderer/dom/domlayerrenderer.js | 3 ++- src/ol/renderer/dom/dommaprenderer.js | 5 +++-- src/ol/renderer/dom/domtilelayerrenderer.js | 2 +- src/ol/renderer/maprenderer.js | 3 ++- src/ol/renderer/webgl/webgllayerrenderer.js | 3 ++- src/ol/renderer/webgl/webglmaprenderer.js | 6 +++--- src/ol/renderer/webgl/webgltilelayerrenderer.js | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index d674f05d3a..3e0296695d 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -690,7 +690,7 @@ ol.Map.prototype.render = function() { /** - * Request that render be called some time in the future. + * Request that renderFrame_ be called some time in the future. */ ol.Map.prototype.requestRenderFrame = function() { if (this.freezeRenderingCount_ === 0) { @@ -714,7 +714,7 @@ ol.Map.prototype.renderFrame_ = function(time) { if (goog.DEBUG) { this.logger.info('renderFrame_'); } - this.renderer_.render(); + this.renderer_.renderFrame(time); this.dirty_ = false; if (goog.DEBUG) { this.logger.info('postrender'); diff --git a/src/ol/renderer/dom/domlayerrenderer.js b/src/ol/renderer/dom/domlayerrenderer.js index cddf4ce527..ed7ae1203f 100644 --- a/src/ol/renderer/dom/domlayerrenderer.js +++ b/src/ol/renderer/dom/domlayerrenderer.js @@ -71,8 +71,9 @@ ol.renderer.dom.Layer.prototype.handleLayerVisibleChange = function() { /** * Render. + * @param {number} time Time. */ -ol.renderer.dom.Layer.prototype.render = goog.abstractMethod; +ol.renderer.dom.Layer.prototype.renderFrame = goog.abstractMethod; /** diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index be2ff3f869..e14abf983d 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -184,8 +184,9 @@ ol.renderer.dom.Map.prototype.handleSizeChanged = function() { /** * Render the map. Sets up the layers pane on first render and adjusts its * position as needed on subsequent calls. + * @inheritDoc */ -ol.renderer.dom.Map.prototype.render = function() { +ol.renderer.dom.Map.prototype.renderFrame = function(time) { var map = this.getMap(); if (!map.isDef()) { return; @@ -228,7 +229,7 @@ ol.renderer.dom.Map.prototype.render = function() { var animate = false; this.forEachReadyVisibleLayer(function(layer, layerRenderer) { - if (layerRenderer.render()) { + if (layerRenderer.renderFrame(time)) { animate = true; } }); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 7b3abd7d0d..8ed136685f 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -108,7 +108,7 @@ ol.renderer.dom.TileLayer.prototype.handleTileChange_ = function(event) { /** * @inheritDoc */ -ol.renderer.dom.TileLayer.prototype.render = function() { +ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { var map = this.getMap(); if (!map.isDef()) { diff --git a/src/ol/renderer/maprenderer.js b/src/ol/renderer/maprenderer.js index 882b971f32..b1caf6a48d 100644 --- a/src/ol/renderer/maprenderer.js +++ b/src/ol/renderer/maprenderer.js @@ -310,8 +310,9 @@ ol.renderer.Map.prototype.removeLayerRenderer = function(layer) { /** * Render. + * @param {number} time Time. */ -ol.renderer.Map.prototype.render = goog.functions.FALSE; +ol.renderer.Map.prototype.renderFrame = goog.functions.FALSE; /** diff --git a/src/ol/renderer/webgl/webgllayerrenderer.js b/src/ol/renderer/webgl/webgllayerrenderer.js index 638ae00424..465d6ac55a 100644 --- a/src/ol/renderer/webgl/webgllayerrenderer.js +++ b/src/ol/renderer/webgl/webgllayerrenderer.js @@ -112,6 +112,7 @@ ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = goog.nullFunction; /** * Render. + * @param {number} time Time. * @return {boolean} Request render frame. */ -ol.renderer.webgl.Layer.prototype.render = goog.abstractMethod; +ol.renderer.webgl.Layer.prototype.renderFrame = goog.abstractMethod; diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js index 16cf4650b8..5a7d893bfb 100644 --- a/src/ol/renderer/webgl/webglmaprenderer.js +++ b/src/ol/renderer/webgl/webglmaprenderer.js @@ -601,7 +601,7 @@ ol.renderer.webgl.Map.prototype.removeLayerRenderer = function(layer) { /** * @inheritDoc */ -ol.renderer.webgl.Map.prototype.render = function() { +ol.renderer.webgl.Map.prototype.renderFrame = function(time) { if (!this.getMap().isDef()) { return; @@ -610,7 +610,7 @@ ol.renderer.webgl.Map.prototype.render = function() { var requestRenderFrame = false; this.forEachReadyVisibleLayer(function(layer, layerRenderer) { - if (layerRenderer.render()) { + if (layerRenderer.renderFrame(time)) { requestRenderFrame = true; } }); @@ -624,7 +624,7 @@ ol.renderer.webgl.Map.prototype.render = function() { var animate = false; this.forEachReadyVisibleLayer(function(layer, layerRenderer) { - if (layerRenderer.render()) { + if (layerRenderer.renderFrame(time)) { animate = true; } }); diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index c849b31362..d5d2bdf328 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -285,7 +285,7 @@ ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() { /** * @inheritDoc */ -ol.renderer.webgl.TileLayer.prototype.render = function() { +ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) { var requestRenderFrame = false; From 8c44dcf83a49fb21367d6ba3da0a45d23069e50a Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 4 Oct 2012 12:17:39 +0200 Subject: [PATCH 4/9] Add some WebGL renderer FIXMEs --- src/ol/renderer/webgl/webgltilelayerrenderer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index d5d2bdf328..2b7e9d5b9c 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -1,5 +1,7 @@ // FIXME large resolutions lead to too large framebuffers :-( // FIXME animated shaders! check in redraw +// FIXME throttle texture uploads +// FIXME prioritize texture uploads goog.provide('ol.renderer.webgl.TileLayer'); goog.provide('ol.renderer.webgl.tilelayerrenderer'); From 934c75bc8c5f1652673e11856c75c49a3e363bd7 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 4 Oct 2012 20:33:16 +0200 Subject: [PATCH 5/9] Use requestRenderFrame API in interactions --- src/ol/interaction/dragpaninteraction.js | 2 ++ src/ol/interaction/dragrotateandzoominteraction.js | 2 ++ src/ol/interaction/dragrotateinteraction.js | 2 ++ src/ol/interaction/mousewheelzoominteraction.js | 1 + 4 files changed, 7 insertions(+) diff --git a/src/ol/interaction/dragpaninteraction.js b/src/ol/interaction/dragpaninteraction.js index 8a49b6846a..d3a2c2fa7d 100644 --- a/src/ol/interaction/dragpaninteraction.js +++ b/src/ol/interaction/dragpaninteraction.js @@ -40,6 +40,7 @@ ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) { } var newCenter = new ol.Coordinate( this.startCenter.x + delta.x, this.startCenter.y + delta.y); + map.requestRenderFrame(); map.setCenter(newCenter); }; @@ -50,6 +51,7 @@ ol.interaction.DragPan.prototype.handleDrag = function(mapBrowserEvent) { ol.interaction.DragPan.prototype.handleDragStart = function(mapBrowserEvent) { var browserEvent = mapBrowserEvent.browserEvent; if (this.condition_(browserEvent)) { + mapBrowserEvent.map.requestRenderFrame(); return true; } else { return false; diff --git a/src/ol/interaction/dragrotateandzoominteraction.js b/src/ol/interaction/dragrotateandzoominteraction.js index 730b4def6a..a45f943a90 100644 --- a/src/ol/interaction/dragrotateandzoominteraction.js +++ b/src/ol/interaction/dragrotateandzoominteraction.js @@ -51,6 +51,7 @@ ol.interaction.DragRotateAndZoom.prototype.handleDrag = size.height / 2 - browserEvent.offsetY); var theta = Math.atan2(delta.y, delta.x); // FIXME this should use map.withFrozenRendering but an assertion fails :-( + map.requestRenderFrame(); map.rotate(this.startRotation_, -theta); var resolution = this.startRatio_ * delta.magnitude(); map.zoomToResolution(resolution); @@ -73,6 +74,7 @@ ol.interaction.DragRotateAndZoom.prototype.handleDragStart = var theta = Math.atan2(delta.y, delta.x); this.startRotation_ = (map.getRotation() || 0) + theta; this.startRatio_ = resolution / delta.magnitude(); + map.requestRenderFrame(); return true; } else { return false; diff --git a/src/ol/interaction/dragrotateinteraction.js b/src/ol/interaction/dragrotateinteraction.js index b40ab74690..d574a72a64 100644 --- a/src/ol/interaction/dragrotateinteraction.js +++ b/src/ol/interaction/dragrotateinteraction.js @@ -42,6 +42,7 @@ ol.interaction.DragRotate.prototype.handleDrag = function(mapBrowserEvent) { var theta = Math.atan2( size.height / 2 - offset.y, offset.x - size.width / 2); + map.requestRenderFrame(); map.rotate(this.startRotation_, -theta); }; @@ -55,6 +56,7 @@ ol.interaction.DragRotate.prototype.handleDragStart = var map = mapBrowserEvent.map; if (browserEvent.isMouseActionButton() && this.condition_(browserEvent) && map.canRotate()) { + map.requestRenderFrame(); var size = map.getSize(); var offset = mapBrowserEvent.getPixel(); var theta = Math.atan2( diff --git a/src/ol/interaction/mousewheelzoominteraction.js b/src/ol/interaction/mousewheelzoominteraction.js index c9b9ec47a7..0e439742c3 100644 --- a/src/ol/interaction/mousewheelzoominteraction.js +++ b/src/ol/interaction/mousewheelzoominteraction.js @@ -36,6 +36,7 @@ ol.interaction.MouseWheelZoom.prototype.handleMapBrowserEvent = goog.asserts.assert(mouseWheelEvent instanceof goog.events.MouseWheelEvent); var anchor = mapBrowserEvent.getCoordinate(); var delta = mouseWheelEvent.deltaY < 0 ? this.delta_ : -this.delta_; + map.requestRenderFrame(); map.zoom(delta, anchor); mapBrowserEvent.preventDefault(); mouseWheelEvent.preventDefault(); From 51636ea0d13fac3da8c3f1eb33b1dddb1dde014f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 24 Oct 2012 22:02:28 +0200 Subject: [PATCH 6/9] Only appending tiles to the dom after load A port of f46cdb4 and 4242ef40 from animation-frame branch. --- src/ol/renderer/dom/domtilelayerrenderer.js | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 8ed136685f..68fac4cec6 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -33,11 +33,6 @@ ol.renderer.dom.TileLayer = function(mapRenderer, tileLayer, target) { */ this.renderedMapResolution_ = undefined; - /** - * @type {Object.} - * @private - */ - this.tileChangeListenerKeys_ = {}; }; goog.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer); @@ -96,12 +91,8 @@ ol.renderer.dom.TileLayer.prototype.removeExtraTiles_ = * @private */ ol.renderer.dom.TileLayer.prototype.handleTileChange_ = function(event) { - var tile = /** @type {ol.Tile} */ (event.target); - goog.asserts.assert(tile.getState() == ol.TileState.LOADED); - var tileKey = goog.getUid(tile); - goog.asserts.assert(tileKey in this.tileChangeListenerKeys_); - delete this.tileChangeListenerKeys_[tileKey]; - this.render(); + goog.asserts.assert(event.target.getState() == ol.TileState.LOADED); + this.getMap().render(); }; @@ -149,10 +140,9 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { tilesToDrawByZ[z][key] = tile; return; } else { - var tileKey = goog.getUid(tile); - if (!(tileKey in this.tileChangeListenerKeys_)) { - this.tileChangeListenerKeys_[tileKey] = goog.events.listen(tile, - goog.events.EventType.CHANGE, this.handleTileChange_, false, this); + if (state != ol.TileState.LOADING) { + goog.events.listen(tile, goog.events.EventType.CHANGE, + this.handleTileChange_, false, this); tile.load(); } } From b386c3811141c0b179f40f86adf7ae303ab39dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Wed, 24 Oct 2012 22:38:19 +0200 Subject: [PATCH 7/9] Demonstrating user-controlled animation Inspired from 840ffb6 --- test/spec/ol/map.test.js | 104 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/test/spec/ol/map.test.js b/test/spec/ol/map.test.js index a936388a7d..1ce6d9f694 100644 --- a/test/spec/ol/map.test.js +++ b/test/spec/ol/map.test.js @@ -1,5 +1,11 @@ +goog.require('goog.async.AnimationDelay'); goog.require('goog.dom'); +goog.require('ol.Collection'); +goog.require('ol.Coordinate'); goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.layer.TileLayer'); +goog.require('ol.source.XYZ'); describe('ol.Map', function() { @@ -135,4 +141,102 @@ describe('ol.Map', function() { }); }); }); + + describe('user animation', function() { + + var layer, map; + beforeEach(function() { + // always use setTimeout based shim for requestAnimationFrame + spyOn(goog.async.AnimationDelay.prototype, 'getRaf_') + .andCallFake(function() {return null;}); + + layer = new ol.layer.TileLayer({ + source: new ol.source.XYZ({ + url: 'foo', + maxZoom: 2 + }) + }); + + map = new ol.Map({ + center: new ol.Coordinate(0, 0), + layers: new ol.Collection([layer]), + renderer: ol.RendererHint.DOM, + target: 'map', + zoom: 1 + }); + }); + + afterEach(function() { + map.dispose(); + layer.dispose(); + }); + + it('can set up an animation loop', function() { + + function quadInOut(t, b, c, d) { + if ((t /= d / 2) < 1) { + return c / 2 * t * t + b; + } + return -c / 2 * ((--t) * (t - 2) - 1) + b; + } + + var duration = 500; + var destination = new ol.Coordinate(1000, 1000); + + var origin = map.getCenter(); + var start = new Date().getTime(); + var x0 = origin.x; + var y0 = origin.y; + var dx = destination.x - origin.x; + var dy = destination.y - origin.y; + + var o = { + callback: function() { + var dt = new Date().getTime() - start; + var more = dt <= duration, + x, y; + if (more) { + x = quadInOut(dt, x0, dx, duration); + y = quadInOut(dt, y0, dy, duration); + } else { + x = destination.x; + y = destination.y; + } + map.setCenter(new ol.Coordinate(x, y)); + if (more) { + animationDelay.start(); + } + } + }; + + spyOn(o, 'callback').andCallThrough(); + + var animationDelay = new goog.async.AnimationDelay(o.callback); + + animationDelay.start(); + + // confirm that the center is somewhere between origin and destination + // after a short delay + waits(100); + runs(function() { + expect(o.callback).toHaveBeenCalled(); + var loc = map.getCenter(); + expect(loc.x).not.toEqual(origin.x); + expect(loc.y).not.toEqual(origin.y); + expect(loc.x).not.toEqual(destination.x); + expect(loc.y).not.toEqual(destination.y); + }); + + // confirm that the map has reached the destination after the duration + waits(duration); + runs(function() { + var loc = map.getCenter(); + expect(loc.x).toEqual(destination.x); + expect(loc.y).toEqual(destination.y); + }); + + }); + + }); + }); From 7a36793776d7c813806890f220813fcd86937876 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 3 Jan 2013 14:51:15 +0100 Subject: [PATCH 8/9] Port DOM renderer to request render frame architecture --- src/ol/renderer/dom/dommaprenderer.js | 33 +++++++-------------- src/ol/renderer/dom/domtilelayerrenderer.js | 25 +++++----------- 2 files changed, 18 insertions(+), 40 deletions(-) diff --git a/src/ol/renderer/dom/dommaprenderer.js b/src/ol/renderer/dom/dommaprenderer.js index e14abf983d..108f8a2cb9 100644 --- a/src/ol/renderer/dom/dommaprenderer.js +++ b/src/ol/renderer/dom/dommaprenderer.js @@ -135,11 +135,7 @@ ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) { */ ol.renderer.dom.Map.prototype.handleCenterChanged = function() { goog.base(this, 'handleCenterChanged'); - var map = this.getMap(); - if (!map.isDef()) { - return; - } - map.render(); + this.getMap().render(); }; @@ -148,11 +144,7 @@ ol.renderer.dom.Map.prototype.handleCenterChanged = function() { */ ol.renderer.dom.Map.prototype.handleResolutionChanged = function() { goog.base(this, 'handleResolutionChanged'); - var map = this.getMap(); - if (!map.isDef()) { - return; - } - map.render(); + this.getMap().render(); }; @@ -160,11 +152,8 @@ ol.renderer.dom.Map.prototype.handleResolutionChanged = function() { * @inheritDoc */ ol.renderer.dom.Map.prototype.handleRotationChanged = function() { - var map = this.getMap(); - if (!map.isDef()) { - return; - } - map.render(); + goog.base(this, 'handleRotationChanged'); + this.getMap().render(); }; @@ -173,11 +162,7 @@ ol.renderer.dom.Map.prototype.handleRotationChanged = function() { */ ol.renderer.dom.Map.prototype.handleSizeChanged = function() { goog.base(this, 'handleSizeChanged'); - var map = this.getMap(); - if (!map.isDef()) { - return; - } - map.render(); + this.getMap().render(); }; @@ -227,14 +212,16 @@ ol.renderer.dom.Map.prototype.renderFrame = function(time) { this.renderedRotation_ = mapRotation; this.renderedSize_ = mapSize; - var animate = false; + var requestRenderFrame = false; this.forEachReadyVisibleLayer(function(layer, layerRenderer) { if (layerRenderer.renderFrame(time)) { - animate = true; + requestRenderFrame = true; } }); - return animate; + if (requestRenderFrame) { + map.requestRenderFrame(); + } }; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 68fac4cec6..e753a6020c 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -86,16 +86,6 @@ ol.renderer.dom.TileLayer.prototype.removeExtraTiles_ = }; -/** - * @param {goog.events.Event} event Tile change event. - * @private - */ -ol.renderer.dom.TileLayer.prototype.handleTileChange_ = function(event) { - goog.asserts.assert(event.target.getState() == ol.TileState.LOADED); - this.getMap().render(); -}; - - /** * @inheritDoc */ @@ -125,6 +115,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { var tileRange = tileGrid.getTileRangeForExtentAndResolution(mapExtent, mapResolution); + var allTilesLoaded = true; // first pass through the tile range to determine all the tiles needed tileRange.forEachTileCoord(z, function(tileCoord) { @@ -136,17 +127,15 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { var key = tile.tileCoord.toString(); var state = tile.getState(); - if (state == ol.TileState.LOADED) { + if (state == ol.TileState.IDLE) { + tile.load(); + } else if (state == ol.TileState.LOADED) { tilesToDrawByZ[z][key] = tile; return; - } else { - if (state != ol.TileState.LOADING) { - goog.events.listen(tile, goog.events.EventType.CHANGE, - this.handleTileChange_, false, this); - tile.load(); - } } + allTilesLoaded = false; + /** * Look for already loaded tiles at alternate z that can serve as * placeholders until tiles at the current z have loaded. @@ -233,4 +222,6 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = function(time) { this.renderedMapResolution_ = mapResolution; this.removeExtraTiles_(tilesToDrawByZ); + + return !allTilesLoaded; }; From b2bb08eb81e849b371e546fda79e066d2619971a Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Thu, 3 Jan 2013 17:57:42 +0100 Subject: [PATCH 9/9] Clarify comment --- src/ol/interaction/dragrotateandzoominteraction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/dragrotateandzoominteraction.js b/src/ol/interaction/dragrotateandzoominteraction.js index a45f943a90..0fa9992f65 100644 --- a/src/ol/interaction/dragrotateandzoominteraction.js +++ b/src/ol/interaction/dragrotateandzoominteraction.js @@ -50,8 +50,9 @@ ol.interaction.DragRotateAndZoom.prototype.handleDrag = browserEvent.offsetX - size.width / 2, size.height / 2 - browserEvent.offsetY); var theta = Math.atan2(delta.y, delta.x); - // FIXME this should use map.withFrozenRendering but an assertion fails :-( map.requestRenderFrame(); + // FIXME the calls to map.rotate and map.zoomToResolution should use + // map.withFrozenRendering but an assertion fails :-( map.rotate(this.startRotation_, -theta); var resolution = this.startRatio_ * delta.magnitude(); map.zoomToResolution(resolution);