L.Map.include(!L.DomUtil.TRANSITION ? {} : { _zoomToIfCenterInView: function(center, zoom, centerOffset) { if (this._animatingZoom) { return true; } if (!this.options.zoomAnimation) { return false; } var zoomDelta = zoom - this._zoom, scale = Math.pow(2, zoomDelta), offset = centerOffset.divideBy(1 - 1/scale); //if offset does not exceed half of the view if (!this._offsetIsWithinView(offset, 1)) { return false; } this._mapPane.className += ' leaflet-zoom-anim'; var centerPoint = this.containerPointToLayerPoint(this.getSize().divideBy(2)), origin = centerPoint.add(offset); this._prepareTileBg(); this._runAnimation(center, zoom, scale, origin); return true; }, _runAnimation: function(center, zoom, scale, origin) { this._animatingZoom = true; this._animateToCenter = center; this._animateToZoom = zoom; var transform = L.DomUtil.TRANSFORM; //dumb FireFox hack, I have no idea why this magic zero translate fixes the scale transition problem if (L.Browser.gecko || window.opera) { this._tileBg.style[transform] += ' translate(0,0)'; } var scaleStr; // Android doesn't like translate/scale chains, transformOrigin + scale works better but // it breaks touch zoom which Anroid doesn't support anyway, so that's a really ugly hack // TODO work around this prettier if (L.Browser.android) { this._tileBg.style[transform + 'Origin'] = origin.x + 'px ' + origin.y + 'px'; scaleStr = 'scale(' + scale + ')'; } else { scaleStr = L.DomUtil.getScaleString(scale, origin); } L.Util.falseFn(this._tileBg.offsetWidth); //hack to make sure transform is updated before running animation var options = {}; options[transform] = this._tileBg.style[transform] + ' ' + scaleStr; this._tileBg.transition.run(options); }, _prepareTileBg: function() { if (!this._tileBg) { this._tileBg = this._createPane('leaflet-tile-pane', this._mapPane); this._tileBg.style.zIndex = 1; } var tilePane = this._tilePane, tileBg = this._tileBg; // prepare the background pane to become the main tile pane //tileBg.innerHTML = ''; tileBg.style[L.DomUtil.TRANSFORM] = ''; tileBg.style.visibility = 'hidden'; // tells tile layers to reinitialize their containers tileBg.empty = true; tilePane.empty = false; this._tilePane = this._panes.tilePane = tileBg; this._tileBg = tilePane; if (!this._tileBg.transition) { this._tileBg.transition = new L.Transition(this._tileBg, {duration: 0.3, easing: 'cubic-bezier(0.25,0.1,0.25,0.75)'}); this._tileBg.transition.on('end', this._onZoomTransitionEnd, this); } this._stopLoadingBgTiles(); }, // stops loading all tiles in the background layer _stopLoadingBgTiles: function() { var tiles = [].slice.call(this._tileBg.getElementsByTagName('img')); for (var i = 0, len = tiles.length; i < len; i++) { if (!tiles[i].complete) { tiles[i].src = ''; tiles[i].parentNode.removeChild(tiles[i]); } } }, _onZoomTransitionEnd: function() { this._restoreTileFront(); L.Util.falseFn(this._tileBg.offsetWidth); this._resetView(this._animateToCenter, this._animateToZoom, true); //TODO clear tileBg on map layersload this._mapPane.className = this._mapPane.className.replace(' leaflet-zoom-anim', ''); //TODO toggleClass util this._animatingZoom = false; }, _restoreTileFront: function() { this._tilePane.innerHTML = ''; this._tilePane.style.visibility = ''; this._tilePane.style.zIndex = 2; this._tileBg.style.zIndex = 1; }, _clearTileBg: function() { if (!this._animatingZoom && !this.touchZoom._zooming) { this._tileBg.innerHTML = ''; } } });