diff --git a/examples/fullScreen.html b/examples/fullScreen.html
index 7a92debd56..f5df04a21f 100644
--- a/examples/fullScreen.html
+++ b/examples/fullScreen.html
@@ -25,9 +25,8 @@
}
-
-
+
@@ -38,7 +37,7 @@
- Demonstrate a map that fill the entire browser window.
+ Demonstrate a map that fills the entire browser window.
+
diff --git a/examples/fullScreen.js b/examples/fullScreen.js
index df90940d9d..dc8ed19d1a 100644
--- a/examples/fullScreen.js
+++ b/examples/fullScreen.js
@@ -1,15 +1,30 @@
-var map;
-function init(){
- map = new OpenLayers.Map('map');
+var urls = [
+ "http://a.tile.openstreetmap.org/${z}/${x}/${y}.png",
+ "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png",
+ "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"
+];
- var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
- "http://vmap0.tiles.osgeo.org/wms/vmap0",
- {layers: 'basic'} );
- var ol_wms_nobuffer = new OpenLayers.Layer.WMS( "OpenLayers WMS (no tile buffer)",
- "http://vmap0.tiles.osgeo.org/wms/vmap0",
- {layers: 'basic'}, {buffer: 0});
+var map = new OpenLayers.Map({
+ div: "map",
+ layers: [
+ new OpenLayers.Layer.XYZ("OSM (with buffer)", urls, {
+ transitionEffect: "resize", buffer: 2, sphericalMercator: true
+ }),
+ new OpenLayers.Layer.XYZ("OSM (without buffer)", urls, {
+ transitionEffect: "resize", buffer: 0, sphericalMercator: true
+ })
+ ],
+ controls: [
+ new OpenLayers.Control.Navigation({
+ dragPanOptions: {
+ enableKinetic: true
+ }
+ }),
+ new OpenLayers.Control.PanZoom(),
+ new OpenLayers.Control.Attribution()
+ ],
+ center: [0, 0],
+ zoom: 3
+});
- map.addLayers([ol_wms, ol_wms_nobuffer]);
- map.addControl(new OpenLayers.Control.LayerSwitcher());
- map.setCenter(new OpenLayers.LonLat(0, 0), 6);
-}
+map.addControl(new OpenLayers.Control.LayerSwitcher());
diff --git a/examples/style.mobile.css b/examples/style.mobile.css
index 31c27ffd92..58dee4586b 100644
--- a/examples/style.mobile.css
+++ b/examples/style.mobile.css
@@ -23,3 +23,9 @@ div.olControlZoomPanel .olControlZoomOutItemInactive {
top: 72px;
background-position: 0 -72px;
}
+.olTileImage {
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}
diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js
index fca45f3eba..ff815125ee 100644
--- a/lib/OpenLayers/Layer/Grid.js
+++ b/lib/OpenLayers/Layer/Grid.js
@@ -145,6 +145,14 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
backBufferLonLat: null,
+ /**
+ * Property; backBufferTimerId
+ * {Number} The id of the back buffer timer. This timer is used to
+ * delay the removal of the back buffer, thereby preventing
+ * flash effects caused by tile animation.
+ */
+ backBufferTimerId: null,
+
/**
* Register a listener for a particular event with the following syntax:
* (code)
@@ -197,6 +205,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
window.clearTimeout(this.timerId);
this.timerId = null;
}
+ if(this.backBufferTimerId !== null) {
+ window.clearTimeout(this.backBufferTimerId);
+ this.backBufferTimerId = null;
+ }
},
/**
@@ -204,9 +216,8 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* Deconstruct the layer and clear the grid.
*/
destroy: function() {
+ this.removeBackBuffer();
this.clearGrid();
- // clearGrid should remove any back buffer from the layer,
- // so no need to call removeBackBuffer here
this.grid = null;
this.tileSize = null;
@@ -455,6 +466,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* resolution - {Number} The resolution to transition to.
*/
applyBackBuffer: function(resolution) {
+ if(this.backBufferTimerId !== null) {
+ this.removeBackBuffer();
+ }
var backBuffer = this.backBuffer;
if(!backBuffer) {
backBuffer = this.createBackBuffer();
@@ -530,10 +544,14 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* Remove back buffer from DOM.
*/
removeBackBuffer: function() {
- if(this.backBuffer && this.backBuffer.parentNode) {
+ if(this.backBuffer) {
this.div.removeChild(this.backBuffer);
this.backBuffer = null;
this.backBufferResolution = null;
+ if(this.backBufferTimerId !== null) {
+ window.clearTimeout(this.backBufferTimerId);
+ this.backBufferTimerId = null;
+ }
}
},
@@ -937,8 +955,15 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
//if that was the last tile, then trigger a 'loadend' on the layer
if (this.numLoadingTiles == 0) {
this.events.triggerEvent("loadend");
- this.removeBackBuffer();
- }
+ if(this.backBuffer) {
+ // the removal of the back buffer is delayed to prevent flash
+ // effects due to the animation of tile displaying
+ this.backBufferTimerId = window.setTimeout(
+ OpenLayers.Function.bind(this.removeBackBuffer, this),
+ 2500
+ );
+ }
+ }
};
tile.events.register("loadend", this, tile.onLoadEnd);
tile.events.register("unload", this, tile.onLoadEnd);
diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js
index 1177479f5d..d863d7dece 100644
--- a/lib/OpenLayers/Tile/Image.js
+++ b/lib/OpenLayers/Tile/Image.js
@@ -80,7 +80,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
* transition effects are not supported if POST requests are used.
*/
maxGetUrlLength: null,
-
+
/** TBD 3.0 - reorder the parameters to the init function to remove
* URL. the getUrl() function on the layer gets called on
* each draw(), so no need to specify it here.
@@ -243,13 +243,14 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
style.width = "100%";
style.height = "100%";
}
- style.display = "none";
- style.position = "absolute";
+ style.visibility = "hidden";
+ style.opacity = 0;
if (this.layer.opacity < 1) {
- OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null,
- null, null, null, null,
- this.layer.opacity);
+ style.filter = 'alpha(opacity=' +
+ (this.layer.opacity * 100) +
+ ')';
}
+ style.position = "absolute";
if (this.layerAlphaHack) {
// move the image out of sight
style.paddingTop = style.height;
@@ -311,7 +312,8 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
*/
setImgSrc: function(url) {
var img = this.imgDiv;
- img.style.display = "none";
+ img.style.visibility = 'hidden';
+ img.style.opacity = 0;
if (url) {
img.src = url;
}
@@ -360,7 +362,10 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
onImageLoad: function() {
var img = this.imgDiv;
OpenLayers.Event.stopObservingElement(img);
- img.style.display = "";
+
+ img.style.visibility = 'inherit';
+ img.style.opacity = this.layer.opacity;
+
this.isLoading = false;
this.events.triggerEvent("loadend");
diff --git a/notes/2.12.md b/notes/2.12.md
index 3626cc7b59..ee5ae8843c 100644
--- a/notes/2.12.md
+++ b/notes/2.12.md
@@ -1,3 +1,25 @@
+# Major enhancements
+
+## Tile animation
+
+The displaying of tiles can now be animated, using CSS3 transitions. Transitions operate on the `opacity` property. Here's the CSS rule defined in OpenLayers' default theme:
+
+ .olTileImage {
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+ }
+
+People can override this rule to use other transition settings. To remove tile animation entirely use:
+
+ .olTileImage {
+ -webkit-transition: none;
+ -moz-transition: none;
+ -o-transition: all 0 none;
+ transition: none;
+ }
+
# Behavior Changes from Past Releases
## Function return values
diff --git a/tests/Layer/ArcGIS93Rest.html b/tests/Layer/ArcGIS93Rest.html
index 4f1a4c618d..4a57b70c12 100644
--- a/tests/Layer/ArcGIS93Rest.html
+++ b/tests/Layer/ArcGIS93Rest.html
@@ -190,31 +190,6 @@
}
- function test_Layer_AGS93_setOpacity (t) {
- var params = {layers: "show:0,2"};
- t.plan( 5 );
-
- var map = new OpenLayers.Map('map');
- map.projection = "xx";
- tParams = { layers: 'show:0,2',
- format: 'png'};
- tOptions = { 'opacity': '0.5' };
- var tLayer = new OpenLayers.Layer.ArcGIS93Rest(name, url, tParams, tOptions);
- map.addLayer(tLayer);
- map.zoomToMaxExtent();
- t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
- tLayer.setOpacity("0.6");
- t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
- var pixel = new OpenLayers.Pixel(5,6);
- var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
- tile.draw();
- t.eq(parseFloat(tile.imgDiv.style.opacity), 0.6, "Tile opacity is set correctly");
-
- map.destroy();
- }
-
function test_Layer_AGS93_noGutters (t) {
t.plan(2);
var map = new OpenLayers.Map('map');
diff --git a/tests/Layer/Grid.html b/tests/Layer/Grid.html
index 85724378c3..21b5105d89 100644
--- a/tests/Layer/Grid.html
+++ b/tests/Layer/Grid.html
@@ -659,6 +659,38 @@
t.eq( layer.grid, null, "layer.grid is null after destroy" );
t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
}
+
+ function test_setOpacity(t) {
+
+ t.plan(5);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ opacity: '0.6'
+ });
+ map.addLayer(layer);
+ // setCenter adds tiles to the layer's grid
+ map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+
+ var tile = layer.grid[0][0], tileImg = tile.imgDiv;
+
+ tile.onImageLoad(); // simulate an image load event
+ t.eq(layer.opacity, '0.6', 'layer opacity value is correct');
+ t.eq(parseFloat(tileImg.style.opacity), 0.6, 'tile opacity is correct');
+
+ layer.setOpacity('0.2');
+ t.eq(layer.opacity, '0.2', 'layer opacity value is correct');
+ t.eq(parseFloat(tileImg.style.opacity), 0.2, 'tile opacity is correct');
+
+ tile = layer.addTile(new OpenLayers.Bounds(1, 2, 3, 4),
+ new OpenLayers.Pixel(5, 6));
+ tile.draw(); // add tile to the grid
+ tile.onImageLoad(); // simulate an image load event
+ t.eq(parseFloat(tile.imgDiv.style.opacity), 0.2, "tile opacity is correc");
+
+ map.destroy();
+ }
function test_getServerResolution(t) {
@@ -937,7 +969,7 @@
}
function test_applyBackBuffer(t) {
- t.plan(13);
+ t.plan(16);
var map = new OpenLayers.Map('map2');
var layer = new OpenLayers.Layer.WMS('', '', {}, {
@@ -1000,6 +1032,24 @@
t.eq(layer.backBuffer.style.top, '295%',
'back buffer has correct top');
+ // test #4
+ // and a back buffer in the layer and do as if back buffer removal
+ // has been scheduled, and test that applyBackBuffer removes the
+ // back buffer and clears the timer
+ layer.createBackBuffer = function() {
+ return;
+ };
+ backBuffer = document.createElement('div');
+ layer.div.insertBefore(backBuffer, layer.div.firstChild);
+ layer.backBuffer = backBuffer;
+ layer.backBufferTimerId = 'fake';
+ layer.applyBackBuffer(2);
+ t.ok(backBuffer.parentNode !== layer.div,
+ 'back buffer is not child node of layer div');
+ t.eq(layer.backBuffer, null,
+ 'back buffer not set in layer');
+ t.eq(layer.backBufferTimerId, null,
+ 'back buffer timer cleared');
map.destroy();
}
@@ -1046,7 +1096,7 @@
}
function test_removeBackBuffer(t) {
- t.plan(3);
+ t.plan(4);
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true});
@@ -1058,12 +1108,17 @@
layer.div.appendChild(backBuffer);
layer.backBufferResolution = 32;
+ // add a fake back buffer removal timer
+ layer.backBufferTimerId = 'fake';
+
layer.removeBackBuffer();
t.eq(layer.backBuffer, null, 'backBuffer set to null in layer');
t.eq(layer.backBufferResolution, null,
'backBufferResolution set to null in layer');
t.ok(backBuffer.parentNode !== layer.div,
'back buffer removed from layer');
+ t.eq(layer.backBufferTimerId, null,
+ 'backBufferTimerId set to null in layer');
map.destroy();
}
@@ -1203,6 +1258,53 @@
map.destroy();
}
+
+ function test_delayed_back_buffer_removal(t) {
+
+ //
+ // Test that the delaying of the back buffer removal behaves
+ // as expected.
+ //
+
+ t.plan(5);
+
+ // set up
+
+ var map = new OpenLayers.Map('map', {
+ resolutions: [32, 16, 8, 4, 2, 1]
+ });
+ var layer = new OpenLayers.Layer.WMS('', '', {}, {
+ isBaseLayer: true,
+ transitionEffect: 'resize'
+ });
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+
+ map.zoomTo(1);
+
+ t.delay_call(1, function() {
+
+ t.ok(layer.backBuffer.parentNode === layer.div,
+ '[a] back buffer is a child of layer div');
+ t.ok(layer.backBufferTimerId !== null,
+ '[a] back buffer scheduled for removal');
+
+ var backBuffer = layer.backBuffer;
+
+ map.zoomTo(2);
+
+ t.ok(layer.backBuffer !== backBuffer,
+ '[b] a new back buffer was created');
+ t.ok(layer.backBuffer.parentNode === layer.div,
+ '[b] back buffer is a child of layer div');
+ t.ok(layer.backBufferTimerId === null,
+ '[b] back buffer no longer scheduled for removal');
+
+ // tear down
+
+ map.destroy();
+ });
+ }
diff --git a/tests/Layer/Image.html b/tests/Layer/Image.html
index a11caa5260..05ab5c378f 100644
--- a/tests/Layer/Image.html
+++ b/tests/Layer/Image.html
@@ -128,7 +128,7 @@
function test_loadEvents(t) {
t.plan(3);
- var map = new OpenLayers.Map('map');
+ var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.Image(
'Test', '../../img/blank.gif',
new OpenLayers.Bounds(-180, -88.759, 180, 88.759),
diff --git a/tests/Layer/MapServer.html b/tests/Layer/MapServer.html
index 11173cb14b..004731db49 100644
--- a/tests/Layer/MapServer.html
+++ b/tests/Layer/MapServer.html
@@ -190,31 +190,6 @@
}
- function test_Layer_MapServer_setOpacity (t) {
- t.plan( 5 );
-
- var map = new OpenLayers.Map('map');
- map.projection = "xx";
- tUrl = "http://labs.metacarta.com/cgi-bin/mapserv";
- tParams = { layers: 'basic',
- format: 'image/png'};
- tOptions = { 'opacity': '0.5' };
- var tLayer = new OpenLayers.Layer.MapServer(name, tUrl, tParams, tOptions);
- map.addLayer(tLayer);
- map.zoomToMaxExtent();
- t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
- tLayer.setOpacity("0.6");
- t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
- var pixel = new OpenLayers.Pixel(5,6);
- var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
- tile.draw();
- t.eq(parseFloat(tile.imgDiv.style.opacity), 0.6, "Tile opacity is set correctly");
- map.destroy();
-
- }
-
function test_Layer_MapServer_singleTile (t) {
t.plan( 5 );
var map = new OpenLayers.Map('map');
diff --git a/tests/Layer/WMS.html b/tests/Layer/WMS.html
index a07b36c1d9..61c7f126e1 100644
--- a/tests/Layer/WMS.html
+++ b/tests/Layer/WMS.html
@@ -271,31 +271,25 @@
}
- function test_Layer_WMS_setOpacity (t) {
- t.plan( 5 );
+ function test_setOpacity(t) {
+ t.plan(1);
- var map = new OpenLayers.Map('map');
- map.projection = "xx";
- tUrl = "http://octo.metacarta.com/cgi-bin/mapserv";
- tParams = { layers: 'basic',
- format: 'image/png'};
- tOptions = { 'opacity': '0.5' };
- var tLayer = new OpenLayers.Layer.WMS(name, tUrl, tParams, tOptions);
- map.addLayer(tLayer);
+ var layer = new OpenLayers.Layer.WMS(
+ null, "/bogus/wms", {layers: "mylayer"}
+ );
+ var map = new OpenLayers.Map("map");
+ map.addLayer(layer);
+
map.zoomToMaxExtent();
- t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
- tLayer.setOpacity("0.6");
- t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
- var pixel = new OpenLayers.Pixel(5,6);
- var tile = tLayer.addTile(new OpenLayers.Bounds(1,2,3,4), pixel);
- tile.draw();
- t.eq(parseFloat(tile.imgDiv.style.opacity), 0.6, "Tile opacity is set correctly");
-
- map.destroy();
+
+ layer.setOpacity(0.5);
+ t.delay_call(1, function() {
+ t.eq(parseFloat(layer.div.firstChild.style.opacity), 0.5, "opacity set");
+ map.destroy();
+ });
}
+
function test_Layer_WMS_noGutters (t) {
t.plan(2);
var map = new OpenLayers.Map('map');
diff --git a/tests/Tile/Image.html b/tests/Tile/Image.html
index f350b5c059..9b00113eea 100644
--- a/tests/Tile/Image.html
+++ b/tests/Tile/Image.html
@@ -262,14 +262,14 @@
tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
tile.draw();
tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true);
- t.delay_call( 1, function() { t.eq(tile.imgDiv.style.display, "none", "Tile image is invisible.") } );
+ t.delay_call( 1, function() { t.eq(tile.imgDiv.style.visibility, "hidden", "Tile image is invisible.") } );
var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, {'alpha':true});
map.addLayer(layer);
tile = new OpenLayers.Tile.Image(layer, position, new OpenLayers.Bounds(-90,-85,-90,85), url, size);
tile.draw();
tile.moveTo(new OpenLayers.Bounds(-185,-90,-180,-80), new OpenLayers.Pixel(-180,-85), true)
- t.delay_call( 1, function() { t.eq(tile.imgDiv.style.display, "none", "Alpha tile image is invisible.") } );
+ t.delay_call( 1, function() { t.eq(tile.imgDiv.style.visibility, "hidden", "Alpha tile image is invisible.") } );
}
@@ -343,6 +343,32 @@
map.destroy();
}
+ function test_onImageLoad(t) {
+ t.plan(3);
+
+ var map = new OpenLayers.Map('map');
+ var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+ "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, {opacity: 0.5});
+ map.addLayer(layer);
+ map.setCenter(new OpenLayers.LonLat(0,0), 5);
+
+ var tile = layer.grid[0][0];
+
+ var log;
+ tile.events.on({loadend: function() { log++; }});
+
+ log = 0;
+ tile.onImageLoad();
+ t.eq(tile.imgDiv.style.visibility, 'inherit',
+ 'onImageLoad makes the image visible');
+ t.eq(parseFloat(tile.imgDiv.style.opacity), 0.5,
+ 'onImageLoad sets the expected opacity for the image');
+ t.eq(log, 1,
+ 'onImageLoad does trigger loadend');
+
+ map.destroy();
+ }
+
// test for https://github.com/openlayers/openlayers/pull/36
// (more an integration test than a unit test)
function test_olImageLoadError(t) {
diff --git a/tests/deprecated/Layer/MapServer/Untiled.html b/tests/deprecated/Layer/MapServer/Untiled.html
index fd9e5adfdd..1b1dc94f36 100644
--- a/tests/deprecated/Layer/MapServer/Untiled.html
+++ b/tests/deprecated/Layer/MapServer/Untiled.html
@@ -118,27 +118,6 @@
}
- function test_Layer_MapServer_Untiled_setOpacity (t) {
- t.plan( 4 );
-
- var map = new OpenLayers.Map('map');
- map.projection = "xx";
- tUrl = "http://labs.metacarta.com/cgi-bin/mapserv";
- tParams = { layers: 'basic',
- format: 'image/png'};
- tOptions = { 'opacity': '0.5' };
- var tLayer = new OpenLayers.Layer.MapServer.Untiled(name, tUrl, tParams, tOptions);
- map.addLayer(tLayer);
- map.zoomToMaxExtent();
- t.eq(tLayer.opacity, "0.5", "Opacity is set correctly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.5, "Opacity on tile is correct");
- tLayer.setOpacity("0.6");
- t.eq(tLayer.opacity, "0.6", "setOpacity works properly");
- t.eq(parseFloat(tLayer.div.firstChild.style.opacity), 0.6, "Opacity on tile is changed correctly");
- map.destroy();
-
- }
-
// DEPRECATED -- REMOVE IN 3.0
function test_Layer_Untiled_MapServer(t) {
t.plan(1);
diff --git a/theme/default/style.css b/theme/default/style.css
index ea8e520890..c695689ff7 100644
--- a/theme/default/style.css
+++ b/theme/default/style.css
@@ -428,3 +428,14 @@ span.olGoogleAttribution.hybrid a, span.olGoogleAttribution.satellite a {
.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
background-position: -26px -24px;
}
+
+/**
+ * Animations
+ */
+
+.olTileImage {
+ -webkit-transition: opacity 0.2s linear;
+ -moz-transition: opacity 0.2s linear;
+ -o-transition: opacity 0.2s linear;
+ transition: opacity 0.2s linear;
+}