diff --git a/examples/mobile-wmts-vienna.css b/examples/mobile-wmts-vienna.css index a5e3f5e167..741cd6d37b 100644 --- a/examples/mobile-wmts-vienna.css +++ b/examples/mobile-wmts-vienna.css @@ -9,6 +9,12 @@ html, body, #map { #title, #tags, #shortdesc { display: none; } +.olTileImage { + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} div.olControlAttribution { position: absolute; font-size: 10px; diff --git a/examples/mobile-wmts-vienna.html b/examples/mobile-wmts-vienna.html index 280f66ce74..d6d127c7c6 100644 --- a/examples/mobile-wmts-vienna.html +++ b/examples/mobile-wmts-vienna.html @@ -7,7 +7,6 @@ -

City of Vienna WMTS for Desktop and Mobile Devices

@@ -22,6 +21,7 @@ functionality and uses the Geolocate control.

+ diff --git a/examples/mobile-wmts-vienna.js b/examples/mobile-wmts-vienna.js index 692b86afe2..4257f7429b 100644 --- a/examples/mobile-wmts-vienna.js +++ b/examples/mobile-wmts-vienna.js @@ -152,7 +152,6 @@ var map; var defaults = { requestEncoding: "REST", matrixSet: "google3857", - buffer: 4, attribution: 'Datenquelle: Stadt Wien - data.wien.gv.at' }; var doc = request.responseText, @@ -182,7 +181,6 @@ var map; requestEncoding: "REST", matrixSet: "google3857", tileFullExtent: extent, - buffer: 4, attribution: 'Datenquelle: Stadt Wien - data.wien.gv.at' }; fmzk = new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults({ diff --git a/lib/OpenLayers/Animation.js b/lib/OpenLayers/Animation.js index 66edc2ec5d..1a1f906321 100644 --- a/lib/OpenLayers/Animation.js +++ b/lib/OpenLayers/Animation.js @@ -15,6 +15,16 @@ */ OpenLayers.Animation = (function(window) { + /** + * Property: isNative + * {Boolean} true if a native requestAnimationFrame function is available + */ + var isNative = !!(window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame); + /** * Function: requestFrame * Schedule a function to be called at the next available animation frame. @@ -89,6 +99,7 @@ OpenLayers.Animation = (function(window) { } return { + isNative: isNative, requestFrame: requestFrame, start: start, stop: stop diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index 4722d64322..86bf9488a5 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -99,11 +99,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { /** * APIProperty: tileLoadingDelay - * {Integer} - Number of milliseconds before we shift and load - * tiles. Default is 100. + * {Integer} Number of milliseconds before we shift and load + * tiles when panning. Ignored if is + * true. Default is 85. */ - tileLoadingDelay: 100, - + tileLoadingDelay: 85, + /** * Property: serverResolutions * {Array(Number}} This property is documented in subclasses as @@ -112,11 +113,31 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { serverResolutions: null, /** - * Property: timerId - * {Number} - The id of the tileLoadingDelay timer. + * Property: moveTimerId + * {Number} The id of the timer. */ - timerId: null, + moveTimerId: null, + + /** + * Property: deferMoveGriddedTiles + * {Function} A function that defers execution of by + * . If is true, this + * is null and unused. + */ + deferMoveGriddedTiles: null, + /** + * Property: tileQueueId + * {Number} The id of the animation. + */ + tileQueueId: null, + + /** + * Property: tileQueue + * {Array()} Tiles queued for drawing. + */ + tileQueue: null, + /** * Property: backBuffer * {DOMElement} The back buffer. @@ -152,7 +173,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * flash effects caused by tile animation. */ backBufferTimerId: null, - + /** * Register a listener for a particular event with the following syntax: * (code) @@ -187,10 +208,14 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments); this.grid = []; - - this._moveGriddedTiles = OpenLayers.Function.bind( - this.moveGriddedTiles, this - ); + this.tileQueue = []; + + if (!OpenLayers.Animation.isNative) { + this.deferMoveGriddedTiles = OpenLayers.Function.bind(function() { + this.moveGriddedTiles(true); + this.moveTimerId = null; + }, this); + } }, /** @@ -201,10 +226,11 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * map - {} The map. */ removeMap: function(map) { - if(this.timerId != null) { - window.clearTimeout(this.timerId); - this.timerId = null; + if (this.moveTimerId !== null) { + window.clearTimeout(this.moveTimerId); + this.moveTimerId = null; } + this.clearTileQueue(); if(this.backBufferTimerId !== null) { window.clearTimeout(this.backBufferTimerId); this.backBufferTimerId = null; @@ -230,20 +256,20 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * destroy() on each of them to kill circular references */ clearGrid:function() { + this.clearTileQueue(); if (this.grid) { for(var iRow=0, len=this.grid.length; iRow} + */ + destroyTile: function(tile) { + this.removeTileMonitoringHooks(tile); + tile.destroy(); + }, /** * Method: getServerResolution @@ -565,24 +646,10 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ moveByPx: function(dx, dy) { if (!this.singleTile) { - this.scheduleMoveGriddedTiles(); + this.moveGriddedTiles(); } }, - /** - * Method: scheduleMoveGriddedTiles - * Schedule the move of tiles. - */ - scheduleMoveGriddedTiles: function() { - if (this.timerId != null) { - window.clearTimeout(this.timerId); - } - this.timerId = window.setTimeout( - this._moveGriddedTiles, - this.tileLoadingDelay - ); - }, - /** * APIMethod: setTileSize * Check if we are in singleTile mode and if so, set the size as a ratio @@ -633,6 +700,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * bounds - {} */ initSingleTile: function(bounds) { + this.clearTileQueue(); //determine new tile bounds var center = bounds.getCenterLonLat(); @@ -744,6 +812,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * bounds - {} */ initGriddedTiles:function(bounds) { + this.clearTileQueue(); // work out mininum number of rows and columns; this is the number of // tiles required to cover the viewport plus at least one for panning @@ -776,7 +845,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left); var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top); - + var tileData = [], center = this.map.getCenter(); do { var row = this.grid[rowidx++]; if (!row) { @@ -810,6 +879,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { } else { tile.moveTo(tileBounds, px, false); } + var tileCenter = tileBounds.getCenterLonLat(); + tileData.push({ + tile: tile, + distance: Math.pow(tileCenter.lon - center.lon, 2) + + Math.pow(tileCenter.lat - center.lat, 2) + }); tileoffsetlon += tilelon; tileoffsetx += this.tileSize.w; @@ -828,7 +903,12 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { this.gridResolution = this.getServerResolution(); //now actually draw the tiles - this.spiralTileLoad(); + tileData.sort(function(a, b) { + return a.distance - b.distance; + }); + for (var i=0, ii=tileData.length; i= 0) && - (testCell < this.grid[0].length) && (testCell >= 0)) { - tile = this.grid[testRow][testCell]; - } - - if ((tile != null) && (!tile.queued)) { - //add tile to beginning of queue, mark it as queued. - tileQueue.unshift(tile); - tile.queued = true; - - //restart the directions counter and take on the new coords - directionsTried = 0; - iRow = testRow; - iCell = testCell; - } else { - //need to try to load a tile in a different direction - direction = (direction + 1) % 4; - directionsTried++; - } - } - - // now we go through and draw the tiles in forward order - for(var i=0, len=tileQueue.length; i} The added OpenLayers.Tile */ addTile: function(bounds, position) { - return new this.tileClass(this, position, bounds, null, - this.tileSize, this.tileOptions); + var tile = new this.tileClass( + this, position, bounds, null, this.tileSize, this.tileOptions + ); + tile.events.register("beforedraw", this, this.queueTileDraw); + return tile; }, /** @@ -955,7 +965,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { this.numLoadingTiles--; this.events.triggerEvent("tileloaded"); //if that was the last tile, then trigger a 'loadend' on the layer - if (this.numLoadingTiles == 0) { + if (this.tileQueue.length === 0 && this.numLoadingTiles === 0) { this.events.triggerEvent("loadend"); if(this.backBuffer) { // the removal of the back buffer is delayed to prevent flash @@ -965,7 +975,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { 2500 ); } - } + } }; tile.events.register("loadend", this, tile.onLoadEnd); tile.events.register("unload", this, tile.onLoadEnd); @@ -991,39 +1001,47 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { /** * Method: moveGriddedTiles + * + * Parameter: + * deferred - {Boolean} true if this is a deferred call that should not + * be delayed. */ - moveGriddedTiles: function() { - var shifted = true; + moveGriddedTiles: function(deferred) { + if (!deferred && !OpenLayers.Animation.isNative) { + if (this.moveTimerId != null) { + window.clearTimeout(this.moveTimerId); + } + this.moveTimerId = window.setTimeout( + this.deferMoveGriddedTiles, this.tileLoadingDelay + ); + return; + } var buffer = this.buffer || 1; var scale = this.getResolutionScale(); - var tlLayer = this.grid[0][0].position.clone(); - tlLayer.x *= scale; - tlLayer.y *= scale; - tlLayer = tlLayer.add(parseInt(this.div.style.left, 10), - parseInt(this.div.style.top, 10)); - var offsetX = parseInt(this.map.layerContainerDiv.style.left); - var offsetY = parseInt(this.map.layerContainerDiv.style.top); - var tlViewPort = tlLayer.add(offsetX, offsetY); - var tileSize = { - w: this.tileSize.w * scale, - h: this.tileSize.h * scale - }; - if (tlViewPort.x > -tileSize.w * (buffer - 1)) { - this.shiftColumn(true); - } else if (tlViewPort.x < -tileSize.w * buffer) { - this.shiftColumn(false); - } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { - this.shiftRow(true); - } else if (tlViewPort.y < -tileSize.h * buffer) { - this.shiftRow(false); - } else { - shifted = false; - } - if (shifted) { - // we may have other row or columns to shift, schedule it - // with a setTimeout, to give the user a chance to sneak - // in moveTo's - this.timerId = window.setTimeout(this._moveGriddedTiles, 0); + while(true) { + var tlViewPort = { + x: (this.grid[0][0].position.x * scale) + + parseInt(this.div.style.left, 10) + + parseInt(this.map.layerContainerDiv.style.left), + y: (this.grid[0][0].position.y * scale) + + parseInt(this.div.style.top, 10) + + parseInt(this.map.layerContainerDiv.style.top) + }; + var tileSize = { + w: this.tileSize.w * scale, + h: this.tileSize.h * scale + }; + if (tlViewPort.x > -tileSize.w * (buffer - 1)) { + this.shiftColumn(true); + } else if (tlViewPort.x < -tileSize.w * buffer) { + this.shiftColumn(false); + } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { + this.shiftRow(true); + } else if (tlViewPort.y < -tileSize.h * buffer) { + this.shiftRow(false); + } else { + break; + } } }, @@ -1113,8 +1131,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var row = this.grid.pop(); for (var i=0, l=row.length; i(true) to actually draw the tile. * - *loadstart* Triggered when tile loading starts. * - *loadend* Triggered when tile loading ends. * - *reload* Triggered when an already loading tile is reloaded. @@ -107,7 +111,7 @@ OpenLayers.Tile = OpenLayers.Class({ * {Boolean} Is the tile loading? */ isLoading: false, - + /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. * there is no need for the base tile class to have a url. * @@ -180,15 +184,26 @@ OpenLayers.Tile = OpenLayers.Class({ * it should actually be re-drawn. This is an example implementation * that can be overridden by subclasses. The minimum thing to do here * is to call and return the result from . + * + * Parameters: + * deferred - {Boolean} When drawing was aborted by returning false from a + * *beforedraw* listener, the queue manager needs to pass true, so the + * tile will not be cleared and immediately be drawn. Otherwise, the + * tile will be cleared and a *beforedraw* event will be fired. * * Returns: * {Boolean} Whether or not the tile should actually be drawn. */ - draw: function() { - //clear tile's contents and mark as not drawn - this.clear(); - - return this.shouldDraw(); + draw: function(deferred) { + if (!deferred) { + //clear tile's contents and mark as not drawn + this.clear(); + } + var draw = this.shouldDraw(); + if (draw && !deferred) { + draw = this.events.triggerEvent("beforedraw") !== false; + } + return draw; }, /** @@ -259,10 +274,10 @@ OpenLayers.Tile = OpenLayers.Class({ /** * Method: clear * Clear the tile of any bounds/position-related data so that it can - * be reused in a new location. To be implemented by subclasses. + * be reused in a new location. */ clear: function(draw) { - // to be implemented by subclasses + // to be extended by subclasses }, CLASS_NAME: "OpenLayers.Tile" diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js index dfbf1bda43..5332c1581d 100644 --- a/lib/OpenLayers/Tile/Image.js +++ b/lib/OpenLayers/Tile/Image.js @@ -6,6 +6,7 @@ /** * @requires OpenLayers/Tile.js + * @requires OpenLayers/Animation.js */ /** @@ -220,6 +221,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { * it can be reused in a new location. */ clear: function() { + OpenLayers.Tile.prototype.clear.apply(this, arguments); var img = this.imgDiv; if (img) { OpenLayers.Event.stopObservingElement(img); diff --git a/tests/Animation.html b/tests/Animation.html index 7215284ae7..75cd4434f8 100644 --- a/tests/Animation.html +++ b/tests/Animation.html @@ -15,7 +15,8 @@