leaflet
Version:
JavaScript library for mobile-friendly interactive maps
124 lines (99 loc) • 3.4 kB
JavaScript
/*
* L.Renderer is a base class for renderer implementations (SVG, Canvas);
* handles renderer container, bounds and zoom animation.
*/
L.Renderer = L.Layer.extend({
options: {
// 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; defaults to clip with the map view
padding: 0.1
},
initialize: function (options) {
L.setOptions(this, options);
L.stamp(this);
},
onAdd: function () {
if (!this._container) {
this._initContainer(); // defined by renderer implementations
if (this._zoomAnimated) {
L.DomUtil.addClass(this._container, 'leaflet-zoom-animated');
}
}
this.getPane().appendChild(this._container);
this._update();
},
onRemove: function () {
L.DomUtil.remove(this._container);
},
getEvents: function () {
var events = {
viewreset: this._reset,
zoomstart: this._onZoomStart,
zoom: this._onZoom,
moveend: this._update
};
if (this._zoomAnimated) {
events.zoomanim = this._onAnimZoom;
}
return events;
},
_onAnimZoom: function (ev) {
this._updateTransform(ev.center, ev.zoom);
},
_onZoom: function () {
this._updateTransform(this._map.getCenter(), this._map.getZoom());
},
_onZoomStart: function () {
// Drag-then-pinch interactions might mess up the center and zoom.
// In this case, the easiest way to prevent this is re-do the renderer
// bounds and padding when the zooming starts.
this._update();
},
_updateTransform: function (center, zoom) {
var scale = this._map.getZoomScale(zoom, this._zoom),
position = L.DomUtil.getPosition(this._container),
viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
currentCenterPoint = this._map.project(this._center, zoom),
destCenterPoint = this._map.project(center, zoom),
centerOffset = destCenterPoint.subtract(currentCenterPoint),
topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
L.DomUtil.setTransform(this._container, topLeftOffset, scale);
},
_reset: function () {
this._update();
this._updateTransform(this._center, this._zoom);
},
_update: function () {
// update pixel bounds of renderer container (for positioning/sizing/clipping later)
var p = this.options.padding,
size = this._map.getSize(),
min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
this._bounds = new L.Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
this._center = this._map.getCenter();
this._zoom = this._map.getZoom();
}
});
L.Map.include({
// used by each vector layer to decide which renderer to use
getRenderer: function (layer) {
var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
if (!renderer) {
renderer = this._renderer = (this.options.preferCanvas && L.canvas()) || L.svg();
}
if (!this.hasLayer(renderer)) {
this.addLayer(renderer);
}
return renderer;
},
_getPaneRenderer: function (name) {
if (name === 'overlayPane' || name === undefined) {
return false;
}
var renderer = this._paneRenderers[name];
if (renderer === undefined) {
renderer = (L.SVG && L.svg({pane: name})) || (L.Canvas && L.canvas({pane: name}));
this._paneRenderers[name] = renderer;
}
return renderer;
}
});