OpenLayers OpenLayers

Ticket #933: transition-resize-r6450.patch

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

updated with minor wording change from r6451

  • 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     * Internal function to actually initialize the image tile, 
     155     *     position it correctly, and set its url. 
     156     */ 
     157    renderTile: function() { 
    149158        if (this.imgDiv == null) { 
    150159            this.initImgDiv(); 
    151160        } 
     
    162171            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, 
    163172                    null, null, imageSize, this.url); 
    164173        } else { 
    165             this.imgDiv.src = this.url; 
    166174            OpenLayers.Util.modifyDOMElement(this.imgDiv, 
    167175                    null, null, imageSize) ; 
     176            this.imgDiv.src = this.url; 
    168177        } 
    169178        return true; 
    170179    }, 
     
    176185     */ 
    177186    clear: function() { 
    178187        if(this.imgDiv) { 
    179             this.imgDiv.style.display = "none"
     188            this.hide()
    180189            if (OpenLayers.Tile.Image.useBlankTile) {  
    181190                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif"; 
    182191            }     
     
    223232        OpenLayers.Event.observe( this.imgDiv, "load", 
    224233            OpenLayers.Function.bind(this.checkImgURL, this) ); 
    225234        */ 
     235        this.frame.style.zIndex = this.isBackBuffer ? 0 : 1; 
    226236        this.frame.appendChild(this.imgDiv);  
    227237        this.layer.div.appendChild(this.frame);  
    228238 
     
    300310        if (this.layer) { 
    301311            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src; 
    302312            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) { 
    303                 this.imgDiv.style.display = "none"
     313                this.hide()
    304314            } 
    305315        } 
    306316    }, 
     317     
     318    /** 
     319     * Method: startTransition 
     320     * This method is invoked on tiles that are backBuffers for tiles in the 
     321     *     grid.  The grid tile is about to be cleared and a new tile source 
     322     *     loaded.  This is where the transition effect needs to be started 
     323     *     to provide visual continuity. 
     324     */ 
     325    startTransition: function() { 
     326        // backBufferTile has to be valid and ready to use 
     327        if (!this.backBufferTile || !this.backBufferTile.imgDiv) { 
     328            return; 
     329        } 
    307330 
     331        // calculate the ratio of change between the current resolution of the 
     332        // backBufferTile and the layer.  If several animations happen in a 
     333        // row, then the backBufferTile will scale itself appropriately for 
     334        // each request. 
     335        var ratio = 1; 
     336        if (this.backBufferTile.resolution) { 
     337            ratio = this.backBufferTile.resolution / this.layer.getResolution(); 
     338        } 
     339         
     340        // if the ratio is not the same as it was last time (i.e. we are 
     341        // zooming), then we need to adjust the backBuffer tile 
     342        if (ratio != this.lastRatio) { 
     343            if (this.layer.transitionEffect == 'resize') { 
     344                // In this case, we can just immediately resize the  
     345                // backBufferTile. 
     346                var upperLeft = new OpenLayers.LonLat( 
     347                    this.backBufferTile.bounds.left,  
     348                    this.backBufferTile.bounds.top 
     349                ); 
     350                var size = new OpenLayers.Size( 
     351                    this.backBufferTile.size.w * ratio, 
     352                    this.backBufferTile.size.h * ratio 
     353                ); 
     354 
     355                var px = this.layer.map.getLayerPxFromLonLat(upperLeft); 
     356                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame,  
     357                                                 null, px, size); 
     358                var imageSize = this.backBufferTile.imageSize; 
     359                imageSize = new OpenLayers.Size(imageSize.w * ratio,  
     360                                                imageSize.h * ratio); 
     361                var imageOffset = this.backBufferTile.imageOffset; 
     362                if(imageOffset) { 
     363                    imageOffset = new OpenLayers.Pixel( 
     364                        imageOffset.x * ratio, imageOffset.y * ratio 
     365                    ); 
     366                } 
     367 
     368                OpenLayers.Util.modifyDOMElement( 
     369                    this.backBufferTile.imgDiv, null, imageOffset, imageSize 
     370                ) ; 
     371 
     372                this.backBufferTile.show(); 
     373            } 
     374        } else { 
     375            // default effect is just to leave the existing tile 
     376            // until the new one loads if this is a singleTile and 
     377            // there was no change in resolution.  Otherwise we 
     378            // don't bother to show the backBufferTile at all 
     379            if (this.layer.singleTile) { 
     380                this.backBufferTile.show(); 
     381            } else { 
     382                this.backBufferTile.hide(); 
     383            } 
     384        } 
     385        this.lastRatio = ratio; 
     386 
     387    }, 
     388     
     389    /**  
     390     * Method: show 
     391     * Show the tile by showing its frame. 
     392     */ 
     393    show: function() { 
     394        this.frame.style.display = ''; 
     395        // Force a reflow on gecko based browsers to actually show the element 
     396        // before continuing execution. 
     397        if (navigator.userAgent.toLowerCase().indexOf("gecko") != -1) {  
     398            this.frame.scrollLeft = this.frame.scrollLeft;  
     399        }  
     400    }, 
     401     
     402    /**  
     403     * Method: hide 
     404     * Hide the tile by hiding its frame. 
     405     */ 
     406    hide: function() { 
     407        this.frame.style.display = 'none'; 
     408    }, 
     409     
    308410    CLASS_NAME: "OpenLayers.Tile.Image" 
    309411  } 
    310412); 
  • 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: lastRatio 
     91     * {Float} Used in transition code only.  This is the previous ratio 
     92     *     of the back buffer tile resolution to the map resolution.  Compared 
     93     *     with the current ratio to determine if zooming occurred. 
     94     */ 
     95    lastRatio: 1, 
     96 
     97    /** 
     98     * Property: isFirstDraw 
     99     * {Boolean} Is this the first time the tile is being drawn? 
     100     *     This is used to force resetBackBuffer to synchronize 
     101     *     the backBufferTile with the foreground tile the first time 
     102     *     the foreground tile loads so that if the user zooms 
     103     *     before the layer has fully loaded, the backBufferTile for 
     104     *     tiles that have been loaded can be used. 
     105     */ 
     106    isFirstDraw: true, 
     107         
     108    /** 
     109     * Property: backBufferTile 
     110     * {<OpenLayers.Tile>} A clone of the tile used to create transition 
     111     *     effects when the tile is moved or changes resolution. 
     112     */ 
     113    backBufferTile: null, 
     114         
    83115    /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. 
    84116     *             there is no need for the base tile class to have a url. 
    85117     *  
     
    111143     * Nullify references to prevent circular references and memory leaks. 
    112144     */ 
    113145    destroy:function() { 
     146        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS,  
     147                this.layer.transitionEffect) != -1) { 
     148            this.layer.events.unregister("loadend", this, this.resetBackBuffer); 
     149            this.events.unregister('loadend', this, this.resetBackBuffer);             
     150        } else { 
     151            this.events.unregister('loadend', this, this.showTile); 
     152        } 
    114153        this.layer  = null; 
    115154        this.bounds = null; 
    116155        this.size = null; 
     
    118157         
    119158        this.events.destroy(); 
    120159        this.events = null; 
     160         
     161        /* clean up the backBufferTile if it exists */ 
     162        if (this.backBufferTile) { 
     163            this.backBufferTile.destroy(); 
     164            this.backBufferTile = null; 
     165        } 
    121166    }, 
    122167     
    123168    /** 
     
    156201     *     depend on the return to know if they should draw or not. 
    157202     */ 
    158203    draw: function() { 
    159          
    160         //clear tile's contents and mark as not drawn 
    161         this.clear(); 
    162          
    163204        var maxExtent = this.layer.maxExtent; 
    164205        var withinMaxExtent = (maxExtent && 
    165206                               this.bounds.intersectsBounds(maxExtent, false)); 
    166207  
    167208        // The only case where we *wouldn't* want to draw the tile is if the  
    168209        // tile is outside its layer's maxExtent. 
    169         return (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     210        var drawTile = (withinMaxExtent || this.layer.displayOutsideMaxExtent); 
     211 
     212        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) { 
     213            if (drawTile) { 
     214                //we use a clone of this tile to create a double buffer for visual 
     215                //continuity.  The backBufferTile is used to create transition 
     216                //effects while the tile in the grid is repositioned and redrawn 
     217                if (!this.backBufferTile) { 
     218                    this.backBufferTile = this.clone(); 
     219                    this.backBufferTile.hide(); 
     220                    // this is important.  It allows the backBuffer to place itself 
     221                    // appropriately in the DOM.  The Image subclass needs to put 
     222                    // the backBufferTile behind the main tile so the tiles can 
     223                    // load over top and display as soon as they are loaded. 
     224                    this.backBufferTile.isBackBuffer = true; 
     225                     
     226                    // potentially end any transition effects when the tile loads 
     227                    this.events.register('loadend', this, this.resetBackBuffer); 
     228                     
     229                    // clear transition back buffer tile only after all tiles in 
     230                    // this layer have loaded to avoid visual glitches 
     231                    this.layer.events.register("loadend", this, this.resetBackBuffer); 
     232                } 
     233                // run any transition effects 
     234                this.startTransition(); 
     235            } else { 
     236                // if we aren't going to draw the tile, then the backBuffer should 
     237                // be hidden too! 
     238                if (this.backBufferTile) { 
     239                    this.backBufferTile.clear(); 
     240                } 
     241            } 
     242        } else { 
     243            if (drawTile && this.isFirstDraw) { 
     244                this.events.register('loadend', this, this.showTile); 
     245                this.isFirstDraw = false; 
     246            }    
     247        }     
     248        this.shouldDraw = drawTile; 
     249         
     250        //clear tile's contents and mark as not drawn 
     251        this.clear(); 
     252         
     253        return drawTile; 
    170254    }, 
    171255     
    172256    /**  
     
    237321                                       topLeft.lat);   
    238322        return bounds; 
    239323    },         
    240  
     324     
     325    /**  
     326     * Method: startTransition 
     327     * Prepare the tile for a transition effect.  To be 
     328     *     implemented by subclasses. 
     329     */ 
     330    startTransition: function() {}, 
     331     
     332    /**  
     333     * Method: resetBackBuffer 
     334     * Triggered by two different events, layer loadend, and tile loadend. 
     335     *     In any of these cases, we check to see if we can hide the  
     336     *     backBufferTile yet and update its parameters to match the  
     337     *     foreground tile. 
     338     * 
     339     * Basic logic: 
     340     *  - If the backBufferTile hasn't been drawn yet, reset it 
     341     *  - If layer is still loading, show foreground tile but don't hide 
     342     *    the backBufferTile yet 
     343     *  - If layer is done loading, reset backBuffer tile and show  
     344     *    foreground tile 
     345     */ 
     346    resetBackBuffer: function() { 
     347        this.showTile(); 
     348        if (this.backBufferTile &&  
     349            (this.isFirstDraw || !this.layer.numLoadingTiles)) { 
     350            this.isFirstDraw = false; 
     351            // check to see if the backBufferTile is within the max extents 
     352            // before rendering it  
     353            var maxExtent = this.layer.maxExtent; 
     354            var withinMaxExtent = (maxExtent && 
     355                                   this.bounds.intersectsBounds(maxExtent, false)); 
     356            if (withinMaxExtent) { 
     357                this.backBufferTile.position = this.position; 
     358                this.backBufferTile.bounds = this.bounds; 
     359                this.backBufferTile.size = this.size; 
     360                this.backBufferTile.imageSize = this.layer.imageSize || this.size; 
     361                this.backBufferTile.imageOffset = this.layer.imageOffset; 
     362                this.backBufferTile.resolution = this.layer.getResolution(); 
     363                this.backBufferTile.renderTile(); 
     364            } 
     365        } 
     366    }, 
     367         
     368    /**  
     369     * Method: showTile 
     370     * Show the tile only if it should be drawn. 
     371     */ 
     372    showTile: function() {  
     373        if (this.shouldDraw) { 
     374            this.show(); 
     375        } 
     376    }, 
     377     
     378    /**  
     379     * Method: show 
     380     * Show the tile.  To be implemented by subclasses. 
     381     */ 
     382    show: function() { }, 
     383     
     384    /**  
     385     * Method: hide 
     386     * Hide the tile.  To be implemented by subclasses. 
     387     */ 
     388    hide: function() { }, 
     389     
    241390    CLASS_NAME: "OpenLayers.Tile" 
    242391}); 
  • 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.   
     264     * 
     265     * There are currently two supported values: 
     266     *  - *null* No transition effect (the default). 
     267     *  - *resize*  Existing tiles are resized on zoom to provide a visual 
     268     *    effect of the zoom having taken place immediately.  As the 
     269     *    new tiles become available, they are drawn over top of the 
     270     *    resized tiles. 
     271     */ 
     272    transitionEffect: null, 
    260273     
    261274    /** 
     275     * Property: SUPPORTED_TRANSITIONS 
     276     * {Array} An immutable (that means don't change it!) list of supported  
     277     *     transitionEffect values. 
     278     */ 
     279    SUPPORTED_TRANSITIONS: ['resize'], 
     280     
     281    /** 
    262282     * Constructor: OpenLayers.Layer 
    263283     * 
    264284     * Parameters: 
  • examples/transition.html

    old new  
     1<html xmlns="http://www.w3.org/1999/xhtml"> 
     2  <head> 
     3    <title>OpenLayers Transitions Example</title> 
     4    <style type="text/css"> 
     5        #mapDiv { 
     6            width: 400px; 
     7            height: 400px; 
     8            border: 1px solid black; 
     9        } 
     10    </style> 
     11    <script src="../lib/OpenLayers.js"></script> 
     12    <script type="text/javascript"> 
     13        var map; 
     14        function init(){ 
     15            map = new OpenLayers.Map('mapDiv', {maxResolution: 'auto'}); 
     16 
     17            var single_default_effect = new OpenLayers.Layer.WMS( 
     18                "WMS untiled default",  
     19                "http://labs.metacarta.com/wms/vmap0?", 
     20                {layers: 'basic'},  
     21                {singleTile: true} 
     22            ); 
     23            var single_resize_effect = new OpenLayers.Layer.WMS( 
     24                "WMS untiled resize",  
     25                "http://labs.metacarta.com/wms/vmap0?", 
     26                {layers: 'basic'},  
     27                {singleTile: true, transitionEffect: 'resize'} 
     28            ); 
     29            var tiled_default_effect = new OpenLayers.Layer.WMS( 
     30                "WMS tiled default ",  
     31                "http://labs.metacarta.com/wms/vmap0?", 
     32                {layers: 'basic'} 
     33            ); 
     34            var tiled_resize_effect = new OpenLayers.Layer.WMS( 
     35                "WMS tiled resize",  
     36                "http://labs.metacarta.com/wms/vmap0?", 
     37                {layers: 'basic'},  
     38                {transitionEffect: 'resize'} 
     39            ); 
     40 
     41            map.addLayers([single_default_effect, single_resize_effect, 
     42                           tiled_default_effect, tiled_resize_effect]); 
     43            map.addControl(new OpenLayers.Control.LayerSwitcher()); 
     44            map.setCenter(new OpenLayers.LonLat(6.5, 40.5), 4); 
     45        } 
     46    </script> 
     47  </head> 
     48  <body onload="init()"> 
     49    <h1 id="title">Transition Example</h1> 
     50    <p id="shortdesc"> 
     51        Demonstrates the use of transition effects in tiled and untiled layers. 
     52    </p> 
     53    <div id="mapDiv"></div> 
     54    <div id="docs"> 
     55        There are two transitions that are currently implemented: null (the 
     56        default) and 'resize'.  The default transition effect is used when no 
     57        transition is specified and is implemented as no transition effect except 
     58        for panning singleTile layers.  The 'resize' effect resamples the current 
     59        tile and displays it stretched or compressed until the new tile is available. 
     60    <ul> 
     61        <li>The first layer is an untiled WMS layer with no transition effect.</li> 
     62        <li>The second layer is an untiled WMS layer with a 'resize' effect. </li> 
     63        <li>The third layer is a tiled WMS layer with no transition effect. </li> 
     64        <li>The fourth layer is a tiled WMS layer with a 'resize' effect. </li> 
     65    </ul> 
     66    </div> 
     67  </body> 
     68  </body> 
     69</html>