|
|
L.MapboxGL = L.Layer.extend({ options: { updateInterval: 32 },
initialize: function (options) { L.setOptions(this, options);
if (options.accessToken) { mapboxgl.accessToken = options.accessToken; } else { throw new Error('You should provide a Mapbox GL access token as a token option.'); }
/** * Create a version of `fn` that only fires once every `time` millseconds. * * @param {Function} fn the function to be throttled * @param {number} time millseconds required between function calls * @param {*} context the value of `this` with which the function is called * @returns {Function} debounced function * @private */ var throttle = function (fn, time, context) { var lock, args, wrapperFn, later;
later = function () { // reset lock and call if queued
lock = false; if (args) { wrapperFn.apply(context, args); args = false; } };
wrapperFn = function () { if (lock) { // called too soon, queue to call later
args = arguments;
} else { // call and lock until later
fn.apply(context, arguments); setTimeout(later, time); lock = true; } };
return wrapperFn; };
// setup throttling the update event when panning
this._throttledUpdate = throttle(L.Util.bind(this._update, this), this.options.updateInterval); },
onAdd: function (map) { if (!this._glContainer) { this._initContainer(); }
map._panes.tilePane.appendChild(this._glContainer);
this._initGL();
this._offset = this._map.containerPointToLayerPoint([0, 0]);
// work around https://github.com/mapbox/mapbox-gl-leaflet/issues/47
if (map.options.zoomAnimation) { L.DomEvent.on(map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this); } },
onRemove: function (map) { if (this._map.options.zoomAnimation) { L.DomEvent.off(this._map._proxy, L.DomUtil.TRANSITION_END, this._transitionEnd, this); }
map.getPanes().tilePane.removeChild(this._glContainer); this._glMap.remove(); this._glMap = null; },
getEvents: function () { return { move: this._throttledUpdate, // sensibly throttle updating while panning
zoomanim: this._animateZoom, // applys the zoom animation to the <canvas>
zoom: this._pinchZoom, // animate every zoom event for smoother pinch-zooming
zoomstart: this._zoomStart, // flag starting a zoom to disable panning
zoomend: this._zoomEnd }; },
_initContainer: function () { var container = this._glContainer = L.DomUtil.create('div', 'leaflet-gl-layer');
var size = this._map.getSize(); container.style.width = size.x + 'px'; container.style.height = size.y + 'px'; },
_initGL: function () { var center = this._map.getCenter();
var options = L.extend({}, this.options, { container: this._glContainer, interactive: false, center: [center.lng, center.lat], zoom: this._map.getZoom() - 1, attributionControl: false });
this._glMap = new mapboxgl.Map(options);
// allow GL base map to pan beyond min/max latitudes
this._glMap.transform.latRange = null;
// treat child <canvas> element like L.ImageOverlay
L.DomUtil.addClass(this._glMap._canvas, 'leaflet-image-layer'); L.DomUtil.addClass(this._glMap._canvas, 'leaflet-zoom-animated'); },
_update: function (e) { // update the offset so we can correct for it later when we zoom
this._offset = this._map.containerPointToLayerPoint([0, 0]);
if (this._zooming) { return; }
var size = this._map.getSize(), container = this._glContainer, gl = this._glMap, topLeft = this._map.containerPointToLayerPoint([0, 0]);
L.DomUtil.setPosition(container, topLeft);
var center = this._map.getCenter();
// gl.setView([center.lat, center.lng], this._map.getZoom() - 1, 0);
// calling setView directly causes sync issues because it uses requestAnimFrame
var tr = gl.transform; tr.center = mapboxgl.LngLat.convert([center.lng, center.lat]); tr.zoom = this._map.getZoom() - 1;
if (gl.transform.width !== size.x || gl.transform.height !== size.y) { container.style.width = size.x + 'px'; container.style.height = size.y + 'px'; if (gl._resize !== null && gl._resize !== undefined){ gl._resize(); } else { gl.resize(); } } else { gl._update(); } },
// update the map constantly during a pinch zoom
_pinchZoom: function (e) { this._glMap.jumpTo({ zoom: this._map.getZoom() - 1, center: this._map.getCenter() }); },
// borrowed from L.ImageOverlay https://github.com/Leaflet/Leaflet/blob/master/src/layer/ImageOverlay.js#L139-L144
_animateZoom: function (e) { var scale = this._map.getZoomScale(e.zoom), offset = this._map._latLngToNewLayerPoint(this._map.getBounds().getNorthWest(), e.zoom, e.center);
L.DomUtil.setTransform(this._glMap._canvas, offset.subtract(this._offset), scale); },
_zoomStart: function (e) { this._zooming = true; },
_zoomEnd: function () { var scale = this._map.getZoomScale(this._map.getZoom()), offset = this._map._latLngToNewLayerPoint(this._map.getBounds().getNorthWest(), this._map.getZoom(), this._map.getCenter());
L.DomUtil.setTransform(this._glMap._canvas, offset.subtract(this._offset), scale);
this._zooming = false; },
_transitionEnd: function (e) { L.Util.requestAnimFrame(function () { var zoom = this._map.getZoom(), center = this._map.getCenter(), offset = this._map.latLngToContainerPoint(this._map.getBounds().getNorthWest());
// reset the scale and offset
L.DomUtil.setTransform(this._glMap._canvas, offset, 1);
// enable panning once the gl map is ready again
this._glMap.once('moveend', L.Util.bind(function () { this._zoomEnd(); }, this));
// update the map position
this._glMap.jumpTo({ center: center, zoom: zoom - 1 }); }, this); }});
L.mapboxGL = function (options) { return new L.MapboxGL(options);};
|