diff --git a/src/ol/layer/Layer.js b/src/ol/layer/Layer.js index 23a4324a63..424429cc26 100644 --- a/src/ol/layer/Layer.js +++ b/src/ol/layer/Layer.js @@ -189,13 +189,15 @@ class Layer extends BaseLayer { * In charge to manage the rendering of the layer. One layer type is * bounded with one layer renderer. * @param {?import("../PluggableMap.js").FrameState} frameState Frame state. + * @param {HTMLElement} target Target which the renderer may (but need not) use + * for rendering its content. * @return {HTMLElement} The rendered element. */ - render(frameState) { + render(frameState, target) { const layerRenderer = this.getRenderer(); const layerState = this.getLayerState(); if (layerRenderer.prepareFrame(frameState, layerState)) { - return layerRenderer.renderFrame(frameState, layerState); + return layerRenderer.renderFrame(frameState, layerState, target); } } diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index d0b5566d0b..d58d19e72f 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -87,7 +87,7 @@ class CompositeMapRenderer extends MapRenderer { const viewResolution = frameState.viewState.resolution; this.children_.length = 0; - let element = null; + let previousElement = null; for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) { const layerState = layerStatesArray[i]; if (!visibleAtResolution(layerState, viewResolution) || @@ -96,9 +96,12 @@ class CompositeMapRenderer extends MapRenderer { } const layer = layerState.layer; - element = layer.render(frameState); + const element = layer.render(frameState, previousElement); if (element) { - this.children_.push(element); + previousElement = element; + if (element !== this.children_[this.children_.length - 1]) { + this.children_.push(element); + } } } super.renderFrame(frameState); diff --git a/src/ol/renderer/Layer.js b/src/ol/renderer/Layer.js index f266a28c57..a42276a62b 100644 --- a/src/ol/renderer/Layer.js +++ b/src/ol/renderer/Layer.js @@ -41,9 +41,10 @@ class LayerRenderer extends Observable { * @abstract * @param {import("../PluggableMap.js").FrameState} frameState Frame state. * @param {import("../layer/Layer.js").State} layerState Layer state. + * @param {HTMLElement} target Target that may be used to render content to. * @return {HTMLElement} The rendered element. */ - renderFrame(frameState, layerState) { + renderFrame(frameState, layerState, target) { return abstract(); } diff --git a/src/ol/renderer/canvas/Layer.js b/src/ol/renderer/canvas/Layer.js index aa7531c0f0..260c62bfd6 100644 --- a/src/ol/renderer/canvas/Layer.js +++ b/src/ol/renderer/canvas/Layer.js @@ -7,7 +7,8 @@ import RenderEvent from '../../render/Event.js'; import RenderEventType from '../../render/EventType.js'; import {rotateAtOffset} from '../../render/canvas.js'; import LayerRenderer from '../Layer.js'; -import {create as createTransform, apply as applyTransform, compose as composeTransform} from '../../transform.js'; +import {create as createTransform, apply as applyTransform, compose as composeTransform, toString as transformToString} from '../../transform.js'; +import ContextEventType from '../../webgl/ContextEventType.js'; /** * @abstract @@ -55,12 +56,49 @@ class CanvasLayerRenderer extends LayerRenderer { * @protected * @type {CanvasRenderingContext2D} */ - this.context = createCanvasContext2D(); + this.context = null; - const canvas = this.context.canvas; - canvas.style.position = 'absolute'; - canvas.style.transformOrigin = 'top left'; - canvas.className = this.getLayer().getClassName(); + } + + /** + * Reuse the passed canvas's context, or create a new one. + * @protected + * @param {HTMLCanvasElement} canvas Canvas. + * @return {CanvasRenderingContext2D} context Context. + */ + useContext(canvas) { + let context; + if (canvas) { + context = canvas.getContext('2d'); + } + if (!context) { + context = createCanvasContext2D(); + canvas = context.canvas; + canvas.style.position = 'absolute'; + canvas.style.transformOrigin = 'top left'; + } + canvas.classList.add(this.getLayer().getClassName()); + this.context = context; + return context; + } + + /** + * @param {HTMLElement} target Potential render target. + * @param {import("../../transform").Transform} transform Transform. + * @return {HTMLCanvasElement} Canvas. + */ + getCanvas(target, transform) { + let canvas = null; + if (target) { + canvas = target.firstElementChild || target; + if (canvas && canvas instanceof HTMLCanvasElement && canvas.style.transform === transformToString(transform)) { + return canvas; + } + } else if (this.context) { + canvas = this.context.canvas; + this.context.clearRect(0, 0, canvas.width, canvas.height); + } + return canvas; } /** diff --git a/src/ol/renderer/canvas/TileLayer.js b/src/ol/renderer/canvas/TileLayer.js index 512fb58a4b..b27feb1cdf 100644 --- a/src/ol/renderer/canvas/TileLayer.js +++ b/src/ol/renderer/canvas/TileLayer.js @@ -137,8 +137,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { * @inheritDoc * @returns {HTMLElement} The rendered element. */ - renderFrame(frameState, layerState) { - const context = this.context; + renderFrame(frameState, layerState, target) { const viewState = frameState.viewState; const projection = viewState.projection; const viewResolution = viewState.resolution; @@ -224,7 +223,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { } - const canvas = context.canvas; const canvasScale = tileResolution / viewResolution; // set forward and inverse pixel transforms @@ -234,6 +232,10 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { rotation, -width / 2, -height / 2 ); + + const context = this.useContext(this.getCanvas(target, this.pixelTransform_)); + const canvas = context.canvas; + makeInverse(this.inversePixelTransform_, this.pixelTransform_); // set scale transform for calculating tile positions on the canvas @@ -247,8 +249,6 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { if (canvas.width != width || canvas.height != height) { canvas.width = width; canvas.height = height; - } else { - context.clearRect(0, 0, width, height); } if (layerState.extent) { @@ -270,6 +270,8 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { } }); + const clips = []; + let currentClip; for (let i = 0, ii = zs.length; i < ii; ++i) { const currentZ = zs[i]; const currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); @@ -299,6 +301,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { const w = nextX - x; const h = nextY - y; + currentClip = [x, y, w, h]; this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, z === currentZ); this.renderedTiles.push(tile); this.updateUsedTiles(frameState.usedTiles, tileSource, tile); @@ -355,7 +358,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer { const tileLayer = /** @type {import("../../layer/Tile.js").default} */ (this.getLayer()); const tileSource = tileLayer.getSource(); if (alpha === 1 && !tileSource.getOpaque(frameState.viewState.projection)) { - this.context.clearRect(x, y, w, h); + //this.context.clearRect(x, y, w, h); } const alphaChanged = alpha !== this.context.globalAlpha; if (alphaChanged) {