leaflet
Version:
JavaScript library for mobile-friendly interactive maps
169 lines (147 loc) • 4.88 kB
JavaScript
import {Layer} from './Layer.js';
import * as DomUtil from '../dom/DomUtil.js';
import * as Util from '../core/Util.js';
import * as DomEvent from '../dom/DomEvent.js';
import {Bounds} from '../geometry/Bounds.js';
/*
* @class BlanketOverlay
* @inherits Layer
*
* Represents an HTML element that covers ("blankets") the entire surface
* of the map.
*
* Do not use this class directly. It's meant for `Renderer`, and for plugins
* that rely on one single HTML element
*/
export class BlanketOverlay extends Layer {
static {
// @section
// @aka BlanketOverlay options
this.setDefaultOptions({
// @option padding: Number = 0.1
// How much to extend the clip area around the map view (relative to its size)
// e.g. 0.1 would be 10% of map view in each direction
padding: 0.1,
// @option continuous: Boolean = false
// When `false`, the blanket will update its position only when the
// map state settles (*after* a pan/zoom animation). When `true`,
// it will update when the map state changes (*during* pan/zoom
// animations)
continuous: false,
});
}
initialize(options) {
Util.setOptions(this, options);
}
onAdd() {
if (!this._container) {
this._initContainer(); // defined by renderer implementations
// always keep transform-origin as 0 0, #8794
this._container.classList.add('leaflet-zoom-animated');
}
this.getPane().appendChild(this._container);
this._resizeContainer();
this._onMoveEnd();
}
onRemove() {
this._destroyContainer();
}
getEvents() {
const events = {
viewreset: this._reset,
zoom: this._onZoom,
moveend: this._onMoveEnd,
zoomend: this._onZoomEnd
};
if (this._zoomAnimated) {
events.zoomanim = this._onAnimZoom;
}
if (this.options.continuous) {
events.move = this._onMoveEnd;
}
return events;
}
_onAnimZoom(ev) {
this._updateTransform(ev.center, ev.zoom);
}
_onZoom() {
this._updateTransform(this._map.getCenter(), this._map.getZoom());
}
_updateTransform(center, zoom) {
const scale = this._map.getZoomScale(zoom, this._zoom),
viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
currentCenterPoint = this._map.project(this._center, zoom),
topLeftOffset = viewHalf.multiplyBy(-scale).add(currentCenterPoint)
.subtract(this._map._getNewPixelOrigin(center, zoom));
DomUtil.setTransform(this._container, topLeftOffset, scale);
}
_onMoveEnd(ev) {
// Update pixel bounds of renderer container (for positioning/sizing/clipping later)
const p = this.options.padding,
size = this._map.getSize(),
min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
this._center = this._map.getCenter();
this._zoom = this._map.getZoom();
this._updateTransform(this._center, this._zoom);
this._onSettled(ev);
this._resizeContainer();
}
_reset() {
this._onSettled();
this._updateTransform(this._center, this._zoom);
this._onViewReset();
}
/*
* @section Subclass interface
* @uninheritable
* Subclasses must define the following methods:
*
* @method _initContainer(): undefined
* Must initialize the HTML element to use as blanket, and store it as
* `this._container`. The base implementation creates a blank `<div>`
*
* @method _destroyContainer(): undefined
* Must destroy the HTML element in `this._container` and free any other
* resources. The base implementation destroys the element and removes
* any event handlers attached to it.
*
* @method _resizeContainer(): Point
* The base implementation resizes the container (based on the map's size
* and taking into account the padding), returning the new size in CSS pixels.
*
* Subclass implementations shall reset container parameters and data
* structures as needed.
*
* @method _onZoomEnd(ev?: PointerEvent): undefined
* (Optional) Runs on the map's `zoomend` event.
*
* @method _onViewReset(ev?: PointerEvent): undefined
* (Optional) Runs on the map's `viewreset` event.
*
* @method _onSettled(): undefined
* Runs whenever the map state settles after changing (at the end of pan/zoom
* animations, etc). This should trigger the bulk of any rendering logic.
*
* If the `continuous` option is set to `true`, then this also runs on
* any map state change (including *during* pan/zoom animations).
*/
_initContainer() {
this._container = DomUtil.create('div');
}
_destroyContainer() {
DomEvent.off(this._container);
this._container.remove();
delete this._container;
}
_resizeContainer() {
const p = this.options.padding,
size = this._map.getSize().multiplyBy(1 + p * 2).round();
this._container.style.width = `${size.x}px`;
this._container.style.height = `${size.y}px`;
return size;
}
_onZoomEnd() {}
_onViewReset() {}
_onSettled() {}
}