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.

@@ -48,6 +47,7 @@ fullScreen.js source to see how this is done.

+ 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; +}