OpenLayers OpenLayers

Ticket #933: transition-resize-r6439.patch

File transition-resize-r6439.patch, 17.0 kB (added by pspencer, 10 months ago)

Updated patch with a description of the visual glitch in ffox2, no other change from previous patch.

  • lib/OpenLayers/Tile/Image.js

    old new  
    146146            this.events.triggerEvent("loadstart"); 
    147147        } 
    148148         
     149        return this.renderTile(); 
     150    }, 
     151     
     152    /** 
     153     * Method: renderTile 
     154     * 
     155     * internal function to actually initialize the image tile, 
     156     * position it correctly, and set its url 
     157     */ 
     158    renderTile: function() { 
    149159        if (this.imgDiv == null) { 
    150160            this.initImgDiv(); 
    151161        } 
     
    162172            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, 
    163173                    null, null, imageSize, this.url); 
    164174        } else { 
    165             this.imgDiv.src = this.url; 
    166175            OpenLayers.Util.modifyDOMElement(this.imgDiv, 
    167176                    null, null, imageSize) ; 
     177            this.imgDiv.src = this.url; 
    168178        } 
    169179        return true; 
    170180    }, 
     
    176186     */ 
    177187    clear: function() { 
    178188        if(this.imgDiv) { 
    179             this.imgDiv.style.display = "none"
     189            this.hide()
    180190            if (OpenLayers.Tile.Image.useBlankTile) {  
    181191                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 
    182192            }     
     
    223233        OpenLayers.Event.observe( this.imgDiv, "load", 
    224234            OpenLayers.Function.bind(this.checkImgURL, this) ); 
    225235        */ 
     236        this.frame.style.zIndex = this.isBackBuffer ? 0 : 1; 
    226237        this.frame.appendChild(this.imgDiv);  
    227238        this.layer.div.appendChild(this.frame);  
    228239 
     
    300311        if (this.layer) { 
    301312            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src; 
    302313            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) { 
    303                 this.imgDiv.style.display = "none"
     314                this.hide()
    304315            } 
    305316        } 
    306317    }, 
     318     
     319    /** 
     320     * Method: startTransition 
     321     * 
     322     * This method is invoked on tiles that are backBuffers for tiles in the 
     323     * grid.  The grid tile is about to be cleared and a new tile source 
     324     * loaded.  This is where the transition effect needs to be started 
     325     * to provide visual continuity. 
     326     */ 
     327    startTransition: function() { 
     328        // backBufferTile has to be valid and ready to use 
     329        if (!this.backBufferTile || !this.backBufferTile.imgDiv) { 
     330            return; 
     331        } 
    307332 
     333        // show the backBufferTile and hide this tile 
     334        this.hide(); 
     335 
     336        // calculate the ratio of change between the current resolution of the 
     337        // backBufferTile and the layer.  If several animations happen in a 
     338        // row, then the backBufferTile will scale itself appropriately for 
     339        // each request. 
     340        var ratio = 1; 
     341        if (this.backBufferTile.resolution) { 
     342            ratio = this.backBufferTile.resolution / this.layer.getResolution() ; 
     343        } 
     344         
     345        // if the ratio is not 1 (i.e. we are zooming), then we might be  
     346        // resizing the backBuffer tile ... 
     347        if (ratio != 1) { 
     348            if (this.layer.transitionEffect == 'resize') { 
     349                // In this case, we can just immediately resize the  
     350                // backBufferTile. 
     351                var upperLeft =  
     352                    new OpenLayers.LonLat(this.backBufferTile.bounds.left,  
     353                                          this.backBufferTile.bounds.top); 
     354                var size =  
     355                    new OpenLayers.Size(this.backBufferTile.size.w * ratio,  
     356                                        this.backBufferTile.size.h * ratio); 
     357 
     358                var px = this.layer.map.getLayerPxFromLonLat(upperLeft); 
     359                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,  
     360                                                 null, px, size);    
     361                var imageSize = this.backBufferTile.size.clone(); 
     362                imageSize = new OpenLayers.Size(imageSize.w * ratio,  
     363                                                imageSize.h * ratio); 
     364 
     365                OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv, 
     366                                                 null, null, imageSize) ; 
     367 
     368                this.backBufferTile.show(); 
     369            } 
     370        } else { 
     371            // default effect is just to leave the existing tile 
     372            // until the new one loads if this is a singleTile and 
     373            // there was no change in resolution.  Otherwise we 
     374            // don't bother to show the backBufferTile at all 
     375            if (this.layer.singleTile && ratio == 1) { 
     376                this.backBufferTile.show(); 
     377            } else { 
     378                this.backBufferTile.hide(); 
     379            } 
     380        } 
     381    }, 
     382     
     383    /**  
     384     * Method: show 
     385     * 
     386     * show the tile by showing its frame. 
     387     */ 
     388    show: function() { 
     389        this.frame.style.display = ''; 
     390    }, 
     391     
     392    /**  
     393     * Method: hide 
     394     * 
     395     * hide the tile by hiding its frame. 
     396     */ 
     397    hide: function() { 
     398        this.frame.style.display = 'none'; 
     399    }, 
     400     
    308401    CLASS_NAME: "OpenLayers.Tile.Image" 
    309402  } 
    310403); 
  • lib/OpenLayers/Tile.js

    old new  
    8080     */ 
    8181    isLoading: false, 
    8282     
     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 
     103     * effects when the tile is moved or changes resolution. 
     104     */ 
     105    backBufferTile: null, 
     106         
    83107    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. 
    84108     *             there is no need for the base tile class to have a url. 
    85109     *  
     
    111135     * Nullify references to prevent circular references and memory leaks. 
    112136     */ 
    113137    destroy:function() { 
     138        if (this.layer.transitionEffect) { 
     139            this.layer.events.unregister("loadend", this, this.resetBackBuffer); 
     140            this.events.unregister('loadend', this, this.resetBackBuffer);             
     141        } else { 
     142            this.events.unregister('loadend', this, this.showTile); 
     143        } 
    114144        this.layer  = null; 
    115145        this.bounds = null; 
    116146        this.size = null; 
     
    118148         
    119149        this.events.destroy(); 
    120150        this.events = null; 
     151         
     152        /* clean up the backBufferTile if it exists */ 
     153        if (this.backBufferTile) { 
     154            this.backBufferTile.destroy(); 
     155            this.backBufferTile = null; 
     156        } 
    121157    }, 
    122158     
    123159    /** 
     
    157193     */ 
    158194    draw: function() { 
    159195         
    160         //clear tile's contents and mark as not drawn 
    161         this.clear(); 
    162          
    163196        var maxExtent = this.layer.maxExtent; 
    164197        var withinMaxExtent = (maxExtent && 
    165198                               this.bounds.intersectsBounds(maxExtent, false)); 
    166199  
    167200        // The only case where we *wouldn't* want to draw the tile is if the  
    168201        // tile is outside its layer's maxExtent. 
    169         return (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     202        var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     203         
     204        if (this.layer.transitionEffect != null) { 
     205            if (drawTile) { 
     206                //we use a clone of this tile to create a double buffer for visual 
     207                //continuity.  The backBufferTile is used to create transition 
     208                //effects while the tile in the grid is repositioned and redrawn 
     209                if (!this.backBufferTile) { 
     210                    this.backBufferTile = this.clone(); 
     211                    this.backBufferTile.hide(); 
     212                    // this is important.  It allows the backBuffer to place itself 
     213                    // appropriately in the DOM.  The Image subclass needs to put 
     214                    // the backBufferTile behind the main tile so the tiles can 
     215                    // load over top and display as soon as they are loaded. 
     216                    this.backBufferTile.isBackBuffer = true; 
     217                     
     218                    // potentially end any transition effects when the tile loads 
     219                    this.events.register('loadend', this, this.resetBackBuffer); 
     220                     
     221                    // clear transition back buffer tile only after all tiles in 
     222                    // this layer have loaded to avoid visual glitches 
     223                    this.layer.events.register("loadend", this, this.resetBackBuffer); 
     224                } 
     225                // run any transition effects 
     226                this.startTransition(); 
     227            } else { 
     228                // if we aren't going to draw the tile, then the backBuffer should 
     229                // be hidden too! 
     230                if (this.backBufferTile) { 
     231                    this.backBufferTile.clear(); 
     232                } 
     233            } 
     234        } else { 
     235            if (this.isFirstDraw) { 
     236                this.events.register('loadend', this, this.showTile); 
     237                this.isFirstDraw = false; 
     238            }    
     239        }     
     240        this.shouldDraw = drawTile; 
     241         
     242        //clear tile's contents and mark as not drawn 
     243        this.clear(); 
     244         
     245        return drawTile; 
    170246    }, 
    171247     
    172248    /**  
     
    237313                                       topLeft.lat);   
    238314        return bounds; 
    239315    },         
    240  
     316     
     317    /**  
     318     * Method: startTransition 
     319     * 
     320     * prepare the tile for a transition effect.  To be 
     321     * implemented by subclasses. 
     322     */ 
     323    startTransition: function() {}, 
     324     
     325    /**  
     326     * Method: resetBackBuffer 
     327     * 
     328     * triggered by two different events, layer loadend, and tile loadend. 
     329     * In any of these cases, we check to see if we can hide the  
     330     * backBufferTile yet and update its parameters to match the  
     331     * foreground tile.  Basic logic: 
     332     * 
     333     * - If the backBufferTile hasn't been drawn yet, reset it 
     334     * - If layer is still loading, show foreground tile but don't hide 
     335     *   the backBufferTile yet 
     336     * - If layer is done loading, reset backBuffer tile and show  
     337     *   foreground tile 
     338     */ 
     339    resetBackBuffer: function(args) { 
     340        if (this.backBufferTile &&  
     341            (this.isFirstDraw || !this.layer.numLoadingTiles)) { 
     342            this.backBufferTile.hide(); 
     343            this.isFirstDraw = false; 
     344            // check to see if the backBufferTile is within the max extents 
     345            // before rendering it  
     346            var maxExtent = this.layer.maxExtent; 
     347            var withinMaxExtent = (maxExtent && 
     348                                   this.bounds.intersectsBounds(maxExtent, false)); 
     349            if (withinMaxExtent) { 
     350                this.backBufferTile.position = this.position.clone(); 
     351                this.backBufferTile.bounds = this.bounds.clone(); 
     352                this.backBufferTile.size = this.size.clone(); 
     353                this.backBufferTile.resolution = this.layer.getResolution(); 
     354                this.backBufferTile.renderTile(); 
     355            } 
     356        } 
     357        this.showTile(); 
     358    }, 
     359         
     360    /**  
     361     * Method: showTile 
     362     * 
     363     * show the tile only if it should be drawn. 
     364     */ 
     365    showTile: function() {  
     366        if (this.shouldDraw) { 
     367            this.show(); 
     368        } 
     369    }, 
     370     
     371    /**  
     372     * Method: show 
     373     * 
     374     * show the tile.  To be implemented by subclasses. 
     375     */ 
     376    show: function() { }, 
     377     
     378    /**  
     379     * Method: hide 
     380     * 
     381     * hide the tile.  To be implemented by subclasses. 
     382     */ 
     383    hide: function() { }, 
     384     
    241385    CLASS_NAME: "OpenLayers.Tile" 
    242386}); 
  • lib/OpenLayers/Layer.js

    old new  
    257257     */ 
    258258    wrapDateLine: false, 
    259259     
     260    /** 
     261     * APIProperty: transitionEffect 
     262     * {String} The transition effect to use when the map is panned or 
     263     * zoomed.  There is currently two supported values: 
     264     * null - no transition effect (the default). 
     265     * keep - Keep tiles around. This uses double-buffering: this 
     266     *    type of transition effect is only useful for singleTile layers, 
     267     *    where the tile will be kept in place on drag. 
     268     * resize - existing tiles are resized on zoom to provide a visual 
     269     *   effect of the zoom having taken place immediately.  As the 
     270     *   new tiles become available, they are drawn over top of the 
     271     *    resized tiles. 
     272     */ 
     273    transitionEffect: null, 
    260274     
    261275    /** 
    262276     * Constructor: OpenLayers.Layer 
  • lib/OpenLayers/Layer/Text.js

    old new  
    173173            // externalGraphic, because icon has no setOffset API Method.   
    174174            if (feature.style.graphicXOffset  
    175175                && feature.style.graphicYOffset) { 
    176                 iconOffset = new OpenLayers.Pixel
     176                iconOffset = new OpenLayers.Size
    177177                    feature.style.graphicXOffset,  
    178178                    feature.style.graphicYOffset); 
    179179            } 
  • 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    <p><em>Note: There is a small visual glitch that appears to affect singleTile layers with the resize effect in FireFox 2.0 with caching enabled (developers often disable their cache).  The map has a noticable flash when panning.  This appears to be a timing issue with showing and hiding the singleTile.  It</em></p> 
     41    <div id="mapDiv"></div> 
     42    <p> The first layer is an untiled WMS layer with no transition effect. </p> 
     43    <p> The second layer is an untiled WMS layer with a 'resize' effect. </p> 
     44    <p> The third layer is a tiled WMS layer with no transition effect. </p> 
     45    <p> The fourth layer is a tiled WMS layer with a 'resize' effect. </p> 
     46  </body> 
     47</html>