Ticket #933: transition.patch
| File transition.patch, 16.4 kB (added by crschmidt, 1 year ago) |
|---|
-
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; … … 142 146 this.events.triggerEvent("loadstart"); 143 147 } 144 148 149 this.renderTile(); 150 151 return true; 152 }, 153 154 /** 155 * Method: renderTile 156 * 157 * internal function to actually initialize the image tile, 158 * position it correctly, and set its url 159 */ 160 renderTile: function() { 145 161 if (this.imgDiv == null) { 146 162 this.initImgDiv(); 147 163 } 148 164 149 165 this.imgDiv.viewRequestID = this.layer.map.viewRequestID; 150 151 this.url = this.layer.getURL(this.bounds);152 166 // position the frame 153 167 OpenLayers.Util.modifyDOMElement(this.frame, 154 168 null, this.position, this.size); 155 169 170 this.url = this.layer.getURL(this.bounds); 156 171 var imageSize = this.layer.getImageSize(); 157 172 if (this.layerAlphaHack) { 158 173 OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, 159 174 null, null, imageSize, this.url); 160 175 } else { 161 this.imgDiv.src = this.url;162 176 OpenLayers.Util.modifyDOMElement(this.imgDiv, 163 177 null, null, imageSize) ; 178 this.imgDiv.src = this.url; 164 179 } 165 return true;166 180 }, 167 181 168 182 /** … … 172 186 */ 173 187 clear: function() { 174 188 if(this.imgDiv) { 175 this. imgDiv.style.display = "none";189 this.hide(); 176 190 if (OpenLayers.Tile.Image.useBlankTile) { 177 191 this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 178 192 } … … 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 … … 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 -
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>
