Bring back interim tiles, but don't block user interaction
This commit is contained in:
@@ -70,6 +70,12 @@ class VectorImageTile extends Tile {
|
|||||||
*/
|
*/
|
||||||
this.sourceTiles_ = sourceTiles;
|
this.sourceTiles_ = sourceTiles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {import("./tilegrid/TileGrid.js").default}
|
||||||
|
*/
|
||||||
|
this.sourceTileGrid_ = sourceTileGrid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
@@ -102,14 +108,19 @@ class VectorImageTile extends Tile {
|
|||||||
*/
|
*/
|
||||||
this.loadListenerKeys_ = [];
|
this.loadListenerKeys_ = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.isInterimTile = !sourceTileGrid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<import("./events.js").EventsKey>}
|
* @type {Array<import("./events.js").EventsKey>}
|
||||||
*/
|
*/
|
||||||
this.sourceTileListenerKeys_ = [];
|
this.sourceTileListenerKeys_ = [];
|
||||||
|
|
||||||
if (urlTileCoord) {
|
if (urlTileCoord && sourceTileGrid) {
|
||||||
const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord);
|
const extent = this.extent = tileGrid.getTileCoordExtent(urlTileCoord);
|
||||||
const resolution = tileGrid.getResolution(urlTileCoord[0]);
|
const resolution = this.resolution_ = tileGrid.getResolution(urlTileCoord[0]);
|
||||||
const sourceZ = sourceTileGrid.getZForResolution(resolution);
|
const sourceZ = sourceTileGrid.getZForResolution(resolution);
|
||||||
sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) {
|
sourceTileGrid.forEachTileCoord(extent, sourceZ, function(sourceTileCoord) {
|
||||||
let sharedExtent = getIntersection(extent,
|
let sharedExtent = getIntersection(extent,
|
||||||
@@ -143,7 +154,13 @@ class VectorImageTile extends Tile {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
disposeInternal() {
|
disposeInternal() {
|
||||||
this.setState(TileState.ABORT);
|
if (!this.isInterimTile) {
|
||||||
|
this.setState(TileState.ABORT);
|
||||||
|
}
|
||||||
|
if (this.interimTile) {
|
||||||
|
this.interimTile.dispose();
|
||||||
|
this.interimTile = null;
|
||||||
|
}
|
||||||
for (let i = 0, ii = this.tileKeys.length; i < ii; ++i) {
|
for (let i = 0, ii = this.tileKeys.length; i < ii; ++i) {
|
||||||
const sourceTileKey = this.tileKeys[i];
|
const sourceTileKey = this.tileKeys[i];
|
||||||
const sourceTile = this.getTile(sourceTileKey);
|
const sourceTile = this.getTile(sourceTileKey);
|
||||||
@@ -188,8 +205,51 @@ class VectorImageTile extends Tile {
|
|||||||
* @return {HTMLCanvasElement} Canvas.
|
* @return {HTMLCanvasElement} Canvas.
|
||||||
*/
|
*/
|
||||||
getImage(layer) {
|
getImage(layer) {
|
||||||
return this.getReplayState(layer).renderedTileRevision == -1 ?
|
return this.hasContext(layer) ? this.getContext(layer).canvas : null;
|
||||||
null : this.getContext(layer).canvas;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @override
|
||||||
|
* @return {VectorImageTile} Interim tile.
|
||||||
|
*/
|
||||||
|
getInterimTile() {
|
||||||
|
const sourceTileGrid = this.sourceTileGrid_;
|
||||||
|
const state = this.getState();
|
||||||
|
if (state < TileState.LOADED && !this.interimTile) {
|
||||||
|
let z = this.tileCoord[0];
|
||||||
|
const minZoom = sourceTileGrid.getMinZoom();
|
||||||
|
while (--z > minZoom) {
|
||||||
|
let covered = true;
|
||||||
|
const tileKeys = [];
|
||||||
|
sourceTileGrid.forEachTileCoord(this.extent, z, function(tileCoord) {
|
||||||
|
const key = tileCoord.toString();
|
||||||
|
if (key in this.sourceTiles_ && this.sourceTiles_[key].getState() === TileState.LOADED) {
|
||||||
|
tileKeys.push(key);
|
||||||
|
} else {
|
||||||
|
covered = false;
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
if (covered && tileKeys.length) {
|
||||||
|
for (let i = 0, ii = tileKeys.length; i < ii; ++i) {
|
||||||
|
this.sourceTiles_[tileKeys[i]].consumers++;
|
||||||
|
}
|
||||||
|
const tile = new VectorImageTile(this.tileCoord, TileState.IDLE, undefined, null, null,
|
||||||
|
this.wrappedTileCoord, null, null, null, this.sourceTiles_,
|
||||||
|
undefined, null, null, null);
|
||||||
|
tile.extent = this.extent;
|
||||||
|
tile.tileKeys = tileKeys;
|
||||||
|
tile.context_ = this.context_;
|
||||||
|
setTimeout(function() {
|
||||||
|
tile.sourceTilesLoaded = true;
|
||||||
|
tile.changed();
|
||||||
|
}, 16);
|
||||||
|
this.interimTile = tile;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const interimTile = /** @type {VectorImageTile} */ (this.interimTile);
|
||||||
|
return state === TileState.LOADED ? this : (interimTile || this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {Object<string, import("../../events").EventsKey)}
|
* @type {Object<string, import("../../events").EventsKey>}
|
||||||
*/
|
*/
|
||||||
this.tileChangeKeys_ = {};
|
this.tileChangeKeys_ = {};
|
||||||
|
|
||||||
@@ -158,10 +158,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* Listen to tile changes and mark tile as loaded when source tiles are loaded.
|
||||||
|
* @param {import("../../VectorImageTile").default} tile Tile to listen on.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {number} projection Projection.
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
getTile(z, x, y, pixelRatio, projection) {
|
listenTileChange_(tile, pixelRatio, projection) {
|
||||||
const tile = /** @type {import("../../VectorImageTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection));
|
|
||||||
const uid = getUid(tile);
|
const uid = getUid(tile);
|
||||||
if (!(uid in this.tileChangeKeys_) && tile.getState() === TileState.IDLE) {
|
if (!(uid in this.tileChangeKeys_) && tile.getState() === TileState.IDLE) {
|
||||||
this.tileChangeKeys_[uid] = listen(tile, EventType.CHANGE, function() {
|
this.tileChangeKeys_[uid] = listen(tile, EventType.CHANGE, function() {
|
||||||
@@ -179,9 +182,26 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
getTile(z, x, y, pixelRatio, projection) {
|
||||||
|
const tile = /** @type {import("../../VectorImageTile.js").default} */ (super.getTile(z, x, y, pixelRatio, projection));
|
||||||
|
this.listenTileChange_(tile, pixelRatio, projection);
|
||||||
|
if (tile.isInterimTile) {
|
||||||
|
// Register change listener also on the original tile
|
||||||
|
const source = /** @type {import("../../source/VectorTile").default} */ (this.getLayer().getSource());
|
||||||
|
const originalTile = /** @type {import("../../VectorImageTile").default} */ (source.getTile(z, x, y, pixelRatio, projection));
|
||||||
|
this.listenTileChange_(originalTile, pixelRatio, projection);
|
||||||
|
}
|
||||||
if (tile.getState() === TileState.LOADED) {
|
if (tile.getState() === TileState.LOADED) {
|
||||||
|
// Update existing instructions if necessary (e.g. when the style has changed)
|
||||||
this.updateExecutorGroup_(tile, pixelRatio, projection);
|
this.updateExecutorGroup_(tile, pixelRatio, projection);
|
||||||
if (tile.hasContext(this.getLayer())) {
|
const layer = this.getLayer();
|
||||||
|
if (tile.getReplayState(layer).renderedTileRevision !== -1) {
|
||||||
|
// Update existing tile image if necessary (e.g. when the style has changed)
|
||||||
this.renderTileImage_(tile, pixelRatio, projection);
|
this.renderTileImage_(tile, pixelRatio, projection);
|
||||||
} else {
|
} else {
|
||||||
// Render new tile images after existing tiles have been drawn to the target canvas.
|
// Render new tile images after existing tiles have been drawn to the target canvas.
|
||||||
@@ -532,12 +552,14 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
|||||||
* @param {import('../../PluggableMap.js').FrameState} frameState Frame state.
|
* @param {import('../../PluggableMap.js').FrameState} frameState Frame state.
|
||||||
*/
|
*/
|
||||||
renderMissingTileImages_(hifi, frameState) {
|
renderMissingTileImages_(hifi, frameState) {
|
||||||
if (hifi) {
|
// Even when we have time to render hifi, do not spend more than 100 ms in this render frame,
|
||||||
// Do not spend more than 100 ms in this render frame, to avoid delays when the user starts
|
// to avoid delays when the user starts interacting again with the map.
|
||||||
// interacting again with the map.
|
while (this.tilesWithoutImage_.length && Date.now() - frameState.time < 100) {
|
||||||
while (this.tilesWithoutImage_.length && Date.now() - frameState.time < 100) {
|
frameState.animate = true;
|
||||||
const tile = this.tilesWithoutImage_.pop();
|
const tile = this.tilesWithoutImage_.pop();
|
||||||
frameState.animate = true;
|
// When we don't have time to render hifi, only render interim tiles until we have used up
|
||||||
|
// half of the frame budget of 16 ms
|
||||||
|
if (hifi || (tile.isInterimTile && Date.now() - frameState.time < 8)) {
|
||||||
this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection);
|
this.renderTileImage_(tile, frameState.pixelRatio, frameState.viewState.projection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user