Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barryhunter/437b661faa92174eb45a9f578fec4512 to your computer and use it in GitHub Desktop.
Save barryhunter/437b661faa92174eb45a9f578fec4512 to your computer and use it in GitHub Desktop.
reloading a Leaflet TileLayer, while reducing flicker
var layerUrl='https://t0.geograph.org.uk/tile/tile-density.php?z={z}&x={x}&y={y}&match=&l=1&6=1';
overlayMaps["Coverage - Dots"] = new L.TileLayer(layerUrl, {user_id: 0, minZoom: 5, maxZoom: 19, attribution: layerAttrib, bounds: bounds});
function j() { //L.template can use callback function.
return (map.getZoom()>16 && overlayMaps["Coverage - Dots"] && map.hasLayer(overlayMaps["Coverage - Dots"]))?1:0;
}
var layerUrl='https://t0.geograph.org.uk/tile/tile-viewpoint2.php?z={z}&x={x}&y={y}&match=&l=1&6=1&j={j}';
overlayMaps["Coverage - ViewPoints"] = new L.TileLayer(layerUrl, {j:j, user_id: 0, minZoom: 11, maxZoom: 18, attribution: layerAttrib, bounds: bounds, opacity:0.9});
//this is just being nice, forcing the Viewpoint layer to redraw
//... going out of way to make the transition seamless as possible. (so layer doesnt disappear before reappearing)
function syncViewLine() {
if (map.getZoom()>16) && overlayMaps["Coverage - ViewPoints"] && map.hasLayer(overlayMaps["Coverage - ViewPoints"])) {
//we COULD just call redraw(), but that removes all tiles, rather than just updating the url of each tile.
if (typeof jQuery === "undefined" || jQuery === null || typeof jQuery.fn === "undefined" || typeof jQuery.fn.load === "undefined" || !$) {
overlayMaps["Coverage - ViewPoints"].redraw();
return;
}
// this is a BODGE, using jquery to do it, as TileLayer has no function. Dont think ther adverse affects by running aournd l$
var hasDots = map.hasLayer(overlayMaps["Coverage - Dots"])?1:0;
//another bodge, is to temporally disable animation, as GridLayer, still animates opacity,
// there is an 'onload' event, on the actual <img>
var wasANimated = map._fadeAnimated;
map._fadeAnimated = false;
$('div.leaflet-tile-container img').each(function() {
if (this.src.indexOf('/tile/tile-viewpoint') > -1) { //make sure only affecting the right layer!
var newsrc = this.src.replace(/j=\d/,'j='+hasDots);
var that = this;
//use a image in background, so that only replace the actual tile, once image is loaded in cache!
var img = new Image();
img.onload = function() {
L.Util.requestAnimFrame(function() {
that.src = newsrc;
});
}
img.src = newsrc;
}
});
if (wasANimated) {
setTimeout(function() {
map._fadeAnimated = wasANimated;
}, 5000);
}
}
}
overlayMaps["Coverage - Dots"].on('remove',syncViewLine).on('add',syncViewLine);
@barryhunter
Copy link
Author

To explain the code, its two separate TileLayer's. The second one (viewpoints) is different depending on if he first (dots) is also on the map. This is pretty easy to implement using a callback function in the URL template/options. so a URL param is set 'dynamically'

But if change visibility of the first layer, need to 'trigger' the second layer to reevaluate the template, and update the URL of tiles.

There is a redraw() method which makes it easy, but its undesirable, as causes a very visible flicker to the user, they see all the content disappearing, only to be re-added slightly layer. It's made worse because new tiles loaded can 'animate' (think its a 200ms opacity animation)- when they appear. So even if in browser cache, there is still a visible delay, and interval where the content disappears.

This code, instead uses jquery to surgically just update the URL of the <img> tags. First loading the image off screen ( so the swap only happens when arrives from server)
... it also temporarily disables map animation, to hobble opacity animation in GridLayer._tileReady

See: Leaflet/Leaflet#6659 - for suggestion to make this a more formal native method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment