Standard behavior, while zooming in and out in Openlayers, is that the current tile gets resampled and displayed stretched or compressed until the new tile is available. That's how it looks like while zooming in:

screen capture of stretched tiles while new one get loaded

Is it possible to change this behavior on layer level so that the old tile is hidden till the new one is loaded? I would like to apply that to ol.layer.Tile and ol.layer.VectorTile layers.

In my usage case I zoom in quite often when selecting features so tiles from low zoom levels get streched quite heavily. I would prefer to make them invisible and show the background color while loading new tiles.

Note: In Openlayers 2 there was a transitionEffect parameter in OpenLayers.Layer.WMS as shown in this example. 'default' value sounds like what I need, also I don't see any difference to 'resize' value...

As a workaround I could think of listening to tileloadstart and tileloadend events and meanwhile set opacity of the whole layer to 0. But I would prefer to see tiles appearing as the get loaded.

  • Have you tried with transition in source? openlayers.org/en/latest/apidoc/ol.source.TileImage.html
    – Chase Choi
    Commented Nov 14, 2017 at 1:32
  • I tried transition on source but don't get the desired effect. The old tile get's stretched the same way with transition set to 0 or with transition set to a higher value as the default (250).
    – geraldo
    Commented Nov 14, 2017 at 9:23
  • btw, set white until the map images are fully loaded could make worse i think. users would think nothing working.
    – Chase Choi
    Commented Nov 14, 2017 at 9:50
  • Have you looked at the various methods on the ol.View? Would have to play around with an installation somewhere, but thinking logically the zoom behaviour is handled by the View, not the source or layer. Zooming into the map effectively stretches the available layer into the relevant View, so you might be able to catch the zoom event on the view and replace the layer dynamically as you go.
    – Phil G
    Commented Nov 14, 2017 at 14:31
  • You mean to hook into change:resolution event and prevent standard behavior? I'm really not sure how to do so and replace the layer dynamically as you go...
    – geraldo
    Commented Nov 19, 2017 at 20:16

2 Answers 2


ol.source.[TYPE].refresh() is the closest native OL4 answer to the problem if hooked into the change:resolution event of the view and if the source is unset from the layer based on all zoom interpreted actions.

var map;
baseSource = new ol.source.OSM();
baseLayer = new ol.layer.Tile({
  name: "OSM Basemap",
  source: baseSource

var view = new ol.View({
  center: [0, 0],
  zoom: 2

map = new ol.Map({
  target: "map",
  layers: [ baseLayer],
  view: view,
  controls: [new ol.control.Zoom()]

$("#map .ol-zoom-in").on('click',function() {
$("#map .ol-zoom-out").on('click',function() {

view.on("change:resolution", function(e) {
  if (Number.isInteger(e.target.getZoom())) {

function() {view.setZoom(this.value);},

This will first unset the layer source when a zoom user event is triggered - may cause false positives for now but demonstration works. Once a change of the resolution is triggered - usually through a zoom event completing - the canvas refreshes and draws with the tiles from the new zoom level.

Click here for the fiddle

While the example uses a tile layer and OSM source I can't see any reason why this wouldn't work with other layer and source types. All layers require a source to be specified (providing the "unset" and "setSource" functions) and all Sources provide the "refresh" function.

  • See edit - doesn't take into account pinch and other more "esoteric" zoom events for now, but works on doubleclick, mousewheel and button click as well as the text input to demonstrate how execution from other captured mechanisms may work.
    – Phil G
    Commented Nov 20, 2017 at 15:25
  • Tidied up my comments and updated the fiddle by saving this time...
    – Phil G
    Commented Nov 21, 2017 at 9:14
  • Thanks, that looks good. Could you please update the link the last version of your fiddle so, it still links to the first version...
    – geraldo
    Commented Nov 21, 2017 at 10:09
  • Also works fine fine with VectorTile layers: jsfiddle.net/geraldo/rozrvnxn
    – geraldo
    Commented Nov 21, 2017 at 10:11

I think there is no way to hide single tiles yet. But you can hide the complete layer while loading using this code:

// hides layer on zoom and shows it again if all tiles are loaded
function setTileHidingWhileLoadingFor(tileLayer){
    // need source to listen to tile load events
    let source = tileLayer.getSource();
    // keep track of tiles
    let tilesToHandleCount = 0;

    // handle layer source events
    source.on("tileloadstart", function(){
        tilesToHandleCount += 1;
        // make layer invisible

    // inner function that is used on tile load success & fail
    function decreaseTilesToHandleAndMakeLayerVisible(){
        tilesToHandleCount -= 1;
        if(tilesToHandleCount == 0){
            // make layer visible again

    source.on("tileloadend", function(){

    source.on("tileloaderror", function(){

At least it is working with a simple OSM tile layer.

  • Tested your code on a vector source but layer is shown and hidden continuously till everything is loaded. Behavior shows that tile load requests don't get started all at the same time and so tilesToHandle counter gets down to 0 a different times before finally all new tiles get loaded.
    – geraldo
    Commented Nov 15, 2017 at 9:56
  • I also experienced this effect in some cases but for just a really short time. If the effect is too annoying maybe it is worth trying to use a timeout function to get rid of it.
    – Lars
    Commented Nov 15, 2017 at 11:48
  • Isn't there a way to check if source is fully loaded? I tried source.getState() but it has value ready once the source itself is loaded, and not the requested tiles... I saw examples calculating the total amount of tiles to load, but they rely on ol-debug.js
    – geraldo
    Commented Nov 15, 2017 at 12:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.