Ticket #933: transition-resize.patch
| File transition-resize.patch, 20.8 kB (added by pspencer, 1 year ago) |
|---|
-
tests/list-tests.html
old new 1 1 <ul id="testlist"> 2 <li>test_Ajax.html</li> 2 3 <li>test_OpenLayers.html</li> 3 4 <li>test_Ajax.html</li> 4 5 <li>test_BaseTypes.html</li> -
lib/OpenLayers/Tile/Image.js
old new 77 77 */ 78 78 destroy: function() { 79 79 if (this.imgDiv != null) { 80 OpenLayers.Event.stopObservingElement(this.imgDiv.id); 80 if (this.layer.alpha) { 81 OpenLayers.Event.stopObservingElement(this.imgDiv.childNodes[0].id); 82 }else { 83 OpenLayers.Event.stopObservingElement(this.imgDiv.id); 84 } 81 85 if (this.imgDiv.parentNode == this.frame) { 82 86 this.frame.removeChild(this.imgDiv); 83 87 this.imgDiv.map = null; … … 117 121 118 122 119 123 return obj; 120 }, 124 }, 121 125 126 122 127 /** 123 128 * Method: draw 124 129 * Check that a tile should be drawn, and draw it. … … 126 131 * Returns: 127 132 * {Boolean} Always returns true. 128 133 */ 129 draw: function() {134 draw: function() { 130 135 if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { 131 136 this.bounds = this.getBoundsFromBaseLayer(this.position); 132 137 } … … 142 147 this.events.triggerEvent("loadstart"); 143 148 } 144 149 150 this.renderTile(); 151 152 return true; 153 }, 154 155 /** 156 * Method: renderTile 157 * 158 * internal function to actually initialize the image tile, 159 * position it correctly, and set its url 160 */ 161 renderTile: function() { 145 162 if (this.imgDiv == null) { 146 163 this.initImgDiv(); 147 164 } 148 165 149 166 this.imgDiv.viewRequestID = this.layer.map.viewRequestID; 150 151 this.url = this.layer.getURL(this.bounds);152 167 // position the frame 153 168 OpenLayers.Util.modifyDOMElement(this.frame, 154 169 null, this.position, this.size); 155 170 171 this.url = this.layer.getURL(this.bounds); 156 172 var imageSize = this.layer.getImageSize(); 157 173 if (this.layerAlphaHack) { 158 174 OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, 159 175 null, null, imageSize, this.url); 160 176 } else { 161 this.imgDiv.src = this.url;162 177 OpenLayers.Util.modifyDOMElement(this.imgDiv, 163 178 null, null, imageSize) ; 179 this.imgDiv.src = this.url; 164 180 } 165 return true;166 181 }, 167 182 168 183 /** … … 172 187 */ 173 188 clear: function() { 174 189 if(this.imgDiv) { 175 this.imgDiv.style.display = "none"; 190 this.hide(); 191 //this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 176 192 if (OpenLayers.Tile.Image.useBlankTile) { 177 193 this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 178 194 } … … 184 200 * Creates the imgDiv property on the tile. 185 201 */ 186 202 initImgDiv: function() { 187 188 203 var offset = this.layer.imageOffset; 189 204 var size = this.layer.getImageSize(); 190 191 205 if (this.layerAlphaHack) { 192 206 this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null, 193 207 offset, … … 219 233 OpenLayers.Event.observe( this.imgDiv, "load", 220 234 OpenLayers.Function.bind(this.checkImgURL, this) ); 221 235 */ 236 this.frame.style.zIndex = this.isBackBuffer ? 0 : 1; 222 237 this.frame.appendChild(this.imgDiv); 223 238 this.layer.div.appendChild(this.frame); 224 239 … … 231 246 232 247 // we need this reference to check back the viewRequestID 233 248 this.imgDiv.map = this.layer.map; 234 249 235 250 //bind a listener to the onload of the image div so that we 236 251 // can register when a tile has finished loading. 237 252 var onload = function() { 238 253 239 254 //normally isLoading should always be true here but there are some 240 255 // right funky conditions where loading and then reloading a tile 241 256 // with the same url *really*fast*. this check prevents sending … … 245 260 this.isLoading = false; 246 261 this.events.triggerEvent("loadend"); 247 262 } 263 248 264 }; 249 OpenLayers.Event.observe(this.imgDiv, 'load', 250 OpenLayers.Function.bind(onload, this)); 265 266 if (this.layer.alphaHack) { 267 OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load', 268 OpenLayers.Function.bind(onload, this)); 269 } else { 270 OpenLayers.Event.observe(this.imgDiv, 'load', 271 OpenLayers.Function.bind(onload, this)); 272 } 273 251 274 252 275 // Bind a listener to the onerror of the image div so that we 253 276 // can registere when a tile has finished loading with errors. … … 289 312 if (this.layer) { 290 313 var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src; 291 314 if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) { 292 this. imgDiv.style.display = "none";315 this.hide(); 293 316 } 294 317 } 295 318 }, 319 320 /** 321 * Method: startTransition 322 * 323 * This method is invoked on tiles that are backBuffers for tiles in the 324 * grid. The grid tile is about to be cleared and a new tile source 325 * loaded. This is where the transition effect needs to be started 326 * to provide visual continuity. 327 */ 328 startTransition: function() { 329 // backBufferTile has to be valid and ready to use 330 if (!this.backBufferTile || !this.backBufferTile.imgDiv) { 331 return; 332 } 296 333 334 // show the backBufferTile and hide this tile 335 this.hide(); 336 337 // calculate the ratio of change between the current resolution of the 338 // backBufferTile and the layer. If several animations happen in a row, 339 // then the backBufferTile will scale itself appropriately for each 340 // request. 341 var ratio = 1; 342 if (this.backBufferTile.resolution) { 343 ratio = this.backBufferTile.resolution / this.layer.getResolution() ; 344 } 345 346 // if the ratio is not 1 (i.e. we are zooming), then we might be 347 // resizing the backBuffer tile ... 348 if (ratio != 1) { 349 if (this.layer.transitionEffect == 'resize') { 350 // In this case, we can just immediately resize the 351 // backBufferTile. 352 var upperLeft = 353 new OpenLayers.LonLat(this.backBufferTile.bounds.left, 354 this.backBufferTile.bounds.top); 355 var size = 356 new OpenLayers.Size(this.backBufferTile.size.w * ratio, 357 this.backBufferTile.size.h * ratio); 358 359 var px = this.layer.map.getLayerPxFromLonLat(upperLeft); 360 OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame, 361 null, px, size); 362 var imageSize = this.backBufferTile.size.clone(); 363 imageSize = new OpenLayers.Size(imageSize.w * ratio, 364 imageSize.h * ratio); 365 366 OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv, 367 null, null, imageSize) ; 368 369 this.backBufferTile.show(); 370 } 371 } else { 372 // default effect is just to leave the existing tile 373 // until the new one loads if this is a singleTile and 374 // there was no change in resolution. Otherwise we 375 // don't bother to show the backBufferTile at all 376 if (this.layer.singleTile && ratio == 1) { 377 this.backBufferTile.show(); 378 } else { 379 this.backBufferTile.hide(); 380 } 381 } 382 }, 383 384 /** 385 * Method: show 386 * 387 * show the tile by showing its frame. 388 */ 389 show: function() { 390 this.frame.style.display = ''; 391 }, 392 393 /** 394 * Method: hide 395 * 396 * hide the tile by hiding its frame. 397 */ 398 hide: function() { 399 this.frame.style.display = 'none'; 400 }, 401 402 /** @final @type String */ 297 403 CLASS_NAME: "OpenLayers.Tile.Image" 298 404 } 299 405 ); -
lib/OpenLayers/Tile.js
old new 80 80 */ 81 81 isLoading: false, 82 82 83 /** 84 * Property: isBackBuffer 85 * {Boolean} Is this tile a back buffer tile? 86 */ 87 isBackBuffer: false, 88 89 /** 90 * Property: isFirstDraw 91 * {Boolean} Is this the first time the tile is being drawn? 92 * This is used to force resetBackBuffer to synchronize 93 * the backBufferTile with the foreground tile the first time 94 * the foreground tile loads so that if the user zooms 95 * before the layer has fully loaded, the backBufferTile for 96 * tiles that have been loaded can be used. 97 */ 98 isFirstDraw: true, 99 100 /** 101 * Property: backBufferTile 102 * {<OpenLayers.Tile>} A clone of the tile used to create transition effects 103 * when the tile is moved or changes resolution. 104 */ 105 backBufferTile: null, 106 83 107 /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. 84 108 * there is no need for the base tile class to have a url. 85 109 * … … 111 135 * Nullify references to prevent circular references and memory leaks. 112 136 */ 113 137 destroy:function() { 138 this.layer.events.unregister("loadend", this, this.resetBackBuffer); 139 this.events.unregister('loadend', this, this.resetBackBuffer); 114 140 this.layer = null; 115 141 this.bounds = null; 116 142 this.size = null; … … 118 144 119 145 this.events.destroy(); 120 146 this.events = null; 147 148 /* clean up the backBufferTile if it exists */ 149 if (this.backBufferTile) { 150 this.backBufferTile.destroy(); 151 this.backBufferTile = null; 152 } 121 153 }, 122 154 155 123 156 /** 124 157 * Method: clone 125 158 * … … 157 190 */ 158 191 draw: function() { 159 192 160 //clear tile's contents and mark as not drawn161 this.clear();162 163 193 var maxExtent = this.layer.maxExtent; 164 194 var withinMaxExtent = (maxExtent && 165 195 this.bounds.intersectsBounds(maxExtent, false)); 166 196 167 197 // The only case where we *wouldn't* want to draw the tile is if the 168 198 // tile is outside its layer's maxExtent. 169 return (withinMaxExtent || this.layer.displayOutsideMaxExtent); 199 var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent); 200 201 if (drawTile) { 202 //we use a clone of this tile to create a double buffer for visual 203 //continuity. The backBufferTile is used to create transition 204 //effects while the tile in the grid is repositioned and redrawn 205 if (!this.backBufferTile) { 206 this.backBufferTile = this.clone(); 207 this.backBufferTile.hide(); 208 // this is important. It allows the backBuffer to place itself 209 // appropriately in the DOM. The Image subclass needs to put 210 // the backBufferTile behind the main tile so the tiles can 211 // load over top and display as soon as they are loaded. 212 this.backBufferTile.isBackBuffer = true; 213 // potentially end any transition effects when the tile loads 214 this.events.register('loadend', this, this.resetBackBuffer); 215 // clear transition back buffer tile only after all tiles in this 216 // layer have loaded to avoid visual glitches 217 this.layer.events.register("loadend", this, this.resetBackBuffer); 218 } 219 // run any transition effects 220 this.startTransition(); 221 } else { 222 // if we aren't going to draw the tile, then the backBuffer should 223 // be hidden too! 224 if (this.backBufferTile) { 225 this.backBufferTile.clear(); 226 } 227 } 228 this.shouldDraw = drawTile; 229 230 //clear tile's contents and mark as not drawn 231 this.clear(); 232 233 return drawTile; 170 234 }, 171 235 172 236 /** … … 240 304 topLeft.lat); 241 305 return bounds; 242 306 }, 243 307 308 /** 309 * Method: startTransition 310 * 311 * prepare the tile for a transition effect. To be 312 * implemented by subclasses. 313 */ 314 startTransition: function() {}, 315 316 /** 317 * Method: resetBackBuffer 318 * 319 * triggered by two different events, layer loadend, and tile loadend. 320 * In any of these cases, we check to see if we can hide the 321 * backBufferTile yet and update its parameters to match the 322 * foreground tile. Basic logic: 323 * 324 * - If the backBufferTile hasn't been drawn yet, reset it 325 * - If layer is still loading, show foreground tile but don't hide 326 * the backBufferTile yet 327 * - If layer is done loading, reset backBuffer tile and show 328 * foreground tile 329 */ 330 resetBackBuffer: function(args) { 331 if (this.backBufferTile && 332 (this.isFirstDraw || !this.layer.numLoadingTiles)) { 333 this.backBufferTile.hide(); 334 this.backBufferTile.position = this.position.clone(); 335 this.backBufferTile.bounds = this.bounds.clone(); 336 this.backBufferTile.size = this.size.clone(); 337 this.backBufferTile.resolution = this.layer.getResolution(); 338 this.backBufferTile.renderTile(); 339 this.isFirstDraw = false; 340 } 341 if (this.shouldDraw) { 342 this.show(); 343 } 344 }, 345 346 /** 347 * Method: show 348 * 349 * show the tile. To be implemented by subclasses. 350 */ 351 show: function() { }, 352 353 /** 354 * Method: hide 355 * 356 * hide the tile. To be implemented by subclasses. 357 */ 358 hide: function() { }, 359 244 360 CLASS_NAME: "OpenLayers.Tile" 245 361 }); -
lib/OpenLayers/Layer.js
old new 231 231 */ 232 232 wrapDateLine: false, 233 233 234 /** 235 * APIProperty: transitionEffect 236 * {String} The transition effect to use when the map is panned or 237 * zoomed. There is currently two supported values: 238 * null - no transition effect (the default). 239 * resize - existing tiles are resized on zoom to provide a visual 240 * effect of the zoom having taken place immediately. As the 241 * new tiles become available, they are drawn over top of the 242 * resized tiles. 243 */ 244 transitionEffect: null, 234 245 235 246 /** 236 247 * Constructor: OpenLayers.Layer -
lib/OpenLayers/Layer/Grid.js
old new 59 59 */ 60 60 numLoadingTiles: 0, 61 61 62 /** 63 * APIProperty: gridOrigin 64 * {LongLat} origin of the grid tiles for all resolution. Usefull for WMSC 65 * layers which requested tiles must be stricly aligned to the server grid 66 * If not specified : bottom left corner of the map.maxExtent 67 */ 68 gridOrigin: null, 69 62 70 /** 63 71 * Constructor: OpenLayers.Layer.Grid 64 72 * Create a new grid layer … … 316 324 var minCols = Math.ceil(viewSize.w/this.tileSize.w) + 317 325 Math.max(1, 2 * this.buffer); 318 326 319 var extent = this.map.getMaxExtent(); 327 this.gridOrigin = this.gridOrigin || new OpenLayers.LonLat( 328 this.map.getMaxExtent().left, this.map.getMaxExtent().bottom); 329 320 330 var resolution = this.map.getResolution(); 321 331 var tilelon = resolution * this.tileSize.w; 322 332 var tilelat = resolution * this.tileSize.h; 323 333 324 var offsetlon = bounds.left - extent.left;334 var offsetlon = bounds.left - this.gridOrigin.lon; 325 335 var tilecol = Math.floor(offsetlon/tilelon) - this.buffer; 326 336 var tilecolremain = offsetlon/tilelon - tilecol; 327 337 var tileoffsetx = -tilecolremain * this.tileSize.w; 328 var tileoffsetlon = extent.left+ tilecol * tilelon;338 var tileoffsetlon = this.gridOrigin.lon + tilecol * tilelon; 329 339 330 var offsetlat = bounds.top - ( extent.bottom+ tilelat);340 var offsetlat = bounds.top - (this.gridOrigin.lat + tilelat); 331 341 var tilerow = Math.ceil(offsetlat/tilelat) + this.buffer; 332 342 var tilerowremain = tilerow - offsetlat/tilelat; 333 343 var tileoffsety = -tilerowremain * this.tileSize.h; 334 var tileoffsetlat = extent.bottom+ tilerow * tilelat;344 var tileoffsetlat = this.gridOrigin.lat + tilerow * tilelat; 335 345 336 346 tileoffsetx = Math.round(tileoffsetx); // heaven help us 337 347 tileoffsety = Math.round(tileoffsety); -
examples/transition.html
old new 1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head> 3 <style type="text/css"> 4 #mapDiv { 5 width: 400px; 6 height: 400px; 7 border: 1px solid black; 8 } 9 </style> 10 <script src="../lib/OpenLayers.js"></script> 11 <script type="text/javascript"> 12 var map; 13 function init(){ 14 map = new OpenLayers.Map('mapDiv', {maxResolution: 'auto'}); 15 16 var single_default_effect = new OpenLayers.Layer.WMS( "WMS untiled default", 17 "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, 18 { singleTile: true, isBaseLayer: true} ); 19 var single_resize_effect = new OpenLayers.Layer.WMS( "WMS untiled resize", 20 "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, 21 { singleTile: true, isBaseLayer: true, transitionEffect: 'resize'} ); 22 var tiled_default_effect = new OpenLayers.Layer.WMS( "WMS tiled default ", 23 "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, 24 { isBaseLayer: true} ); 25 var tiled_resize_effect = new OpenLayers.Layer.WMS( "WMS tiled resize", 26 "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'}, 27 { isBaseLayer: true, transitionEffect: 'resize'} ); 28 29 map.addLayers([single_default_effect, single_resize_effect, 30 tiled_default_effect, tiled_resize_effect]); 31 map.addControl(new OpenLayers.Control.LayerSwitcher()); 32 map.setCenter(new OpenLayers.LonLat(6.5, 40.5), 4); 33 } 34 </script> 35 </head> 36 <body onload="init()"> 37 <h1 id="title">Transition Example</h1> 38 <p id="shortdesc"> 39 Demonstrates the use of transition effects in tiled and untiled layers. There are two transitions that are currently implemented, 'default', 'resize'. The default transition effect is used when no transition is specified and is implemented as no transition effect except for panning singleTile layers. The 'resize' effect resamples the current tile and displays it stretched or compressed until the new tile is available.</p> 40 <div id="mapDiv"></div> 41 <p> The first layer is an untiled WMS layer with no transition effect. </p> 42 <p> The second layer is an untiled WMS layer with a 'resize' effect. </p> 43 <p> The third layer is a tiled WMS layer with no transition effect. </p> 44 <p> The fourth layer is a tiled WMS layer with a 'resize' effect. </p> 45 </body> 46 </html>
