UNPKG

leaflet

Version:

JavaScript library for mobile-friendly interactive maps

137 lines (101 loc) 4.17 kB
/* * Extends L.Map to handle zoom animations. */ // @namespace Map // @section Animation Options L.Map.mergeOptions({ // @option zoomAnimation: Boolean = true // Whether the map zoom animation is enabled. By default it's enabled // in all browsers that support CSS3 Transitions except Android. zoomAnimation: true, // @option zoomAnimationThreshold: Number = 4 // Won't animate zoom if the zoom difference exceeds this value. zoomAnimationThreshold: 4 }); var zoomAnimated = L.DomUtil.TRANSITION && L.Browser.any3d && !L.Browser.mobileOpera; if (zoomAnimated) { L.Map.addInitHook(function () { // don't animate on browsers without hardware-accelerated transitions or old Android/Opera this._zoomAnimated = this.options.zoomAnimation; // zoom transitions run with the same duration for all layers, so if one of transitionend events // happens after starting zoom animation (propagating to the map pane), we know that it ended globally if (this._zoomAnimated) { this._createAnimProxy(); L.DomEvent.on(this._proxy, L.DomUtil.TRANSITION_END, this._catchTransitionEnd, this); } }); } L.Map.include(!zoomAnimated ? {} : { _createAnimProxy: function () { var proxy = this._proxy = L.DomUtil.create('div', 'leaflet-proxy leaflet-zoom-animated'); this._panes.mapPane.appendChild(proxy); this.on('zoomanim', function (e) { var prop = L.DomUtil.TRANSFORM, transform = proxy.style[prop]; L.DomUtil.setTransform(proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)); // workaround for case when transform is the same and so transitionend event is not fired if (transform === proxy.style[prop] && this._animatingZoom) { this._onZoomTransitionEnd(); } }, this); this.on('load moveend', function () { var c = this.getCenter(), z = this.getZoom(); L.DomUtil.setTransform(proxy, this.project(c, z), this.getZoomScale(z, 1)); }, this); }, _catchTransitionEnd: function (e) { if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) { this._onZoomTransitionEnd(); } }, _nothingToAnimate: function () { return !this._container.getElementsByClassName('leaflet-zoom-animated').length; }, _tryAnimatedZoom: function (center, zoom, options) { if (this._animatingZoom) { return true; } options = options || {}; // don't animate if disabled, not supported or zoom difference is too large if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } // offset is the pixel coords of the zoom origin relative to the current center var scale = this.getZoomScale(zoom), offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale); // don't animate if the zoom origin isn't within one screen from the current center, unless forced if (options.animate !== true && !this.getSize().contains(offset)) { return false; } L.Util.requestAnimFrame(function () { this ._moveStart(true) ._animateZoom(center, zoom, true); }, this); return true; }, _animateZoom: function (center, zoom, startAnim, noUpdate) { if (startAnim) { this._animatingZoom = true; // remember what center/zoom to set after animation this._animateToCenter = center; this._animateToZoom = zoom; L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim'); } // @event zoomanim: ZoomAnimEvent // Fired on every frame of a zoom animation this.fire('zoomanim', { center: center, zoom: zoom, noUpdate: noUpdate }); // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693 setTimeout(L.bind(this._onZoomTransitionEnd, this), 250); }, _onZoomTransitionEnd: function () { if (!this._animatingZoom) { return; } L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim'); this._animatingZoom = false; this._move(this._animateToCenter, this._animateToZoom); // This anim frame should prevent an obscure iOS webkit tile loading race condition. L.Util.requestAnimFrame(function () { this._moveEnd(true); }, this); } });