这里写自定义目录标题
- 第一种方式
- 第二种方式
- 第三种方式
引言:Leaflet 是一个广泛使用的开源 JavaScript 库,用于创建交互式、可定制的地图应用程序。在 Leaflet 中,默认情况下,瓦片地图是通过切分成多个瓦片来展示的,这些瓦片组合在一起形成完整的地图。然而,当地图的最后一层瓦片存在缺失或不完整时,可能会导致用户在缩放时出现显示问题。本文将介绍如何优化 Leaflet 缩放功能,以便在最后一层瓦片缺失时提供更好的用户体验
第一种方式
您可以使用参数 maxNativeZoom 来定义从哪一层瓦片开始进行缩放。具体来说,maxNativeZoom 参数用于设置地图的最大本机缩放级别,即地图所使用的原始瓦片的最高缩放级别
缺点:必须指定哪一层,但是瓦片的资源,最后一层是不确定的,打个比方,深圳的最后一层是18层,舟山港口的最后一层是17层,那这就不完美了,除非地图是指定区域使用,比如惠州某园区
var tileLayer = L.tileLayer(url, {
maxZoom: 23,
maxNativeZoom: 18,
});
第二种方式
基于第一种方式的缺点,做了优化,基于插件
Leaflet.TileLayer.Fallback(添加链接描述)
使用此插件,您无需显式指定 maxNativezoom 选项。平铺层首先尝试获取当前缩放的平铺图像,但在服务器没有该缩放级别的图像并返回404错误的区域,平铺层将自动请求较低的缩放。
// 引用以上插件后这样设置就好
var myTileLayer = L.tileLayer.fallback(url, {
maxZoom: 22,
});
myTileLayer.addTo(map);
第三种方式
第二种方式只能在返回404错误时监听,这就有个问题了,比如他不是返回404错误,而是返回一张错误的图片,比如
到这里已经技穷,所以基于Leaflet.TileLayer.Fallback 插件源码做了一点小修改,由于这张错误瓦片没有什么唯一标识,比如url和坐标都不能作为唯一标识,所以打算用图片的responseText的长度作为图片的的唯一标识,源码在最后面
Leaflet.TileLayer.Fallback 插件 fallback.js,做了改动后的代码,缺点是多请求了一遍瓦片
L.TileLayer.Fallback = L.TileLayer.extend({
options: {
minNativeZoom: 0,
},
initialize: function (urlTemplate, options) {
L.TileLayer.prototype.initialize.call(this, urlTemplate, options);
},
createTile: function (coords, done) {
var tile = L.TileLayer.prototype.createTile.call(this, coords, done);
tile._originalCoords = coords;
tile._originalSrc = tile.src;
return tile;
},
_createCurrentCoords: function (originalCoords) {
var currentCoords = this._wrapCoords(originalCoords);
currentCoords.fallback = true;
return currentCoords;
},
_originalTileOnError: L.TileLayer.prototype._tileOnError,
_tileOnError: function (done, tile, e) {
// console.log('zoule');
var layer = this, // `this` is bound to the Tile Layer in L.TileLayer.prototype.createTile.
originalCoords = tile._originalCoords,
currentCoords = (tile._currentCoords = tile._currentCoords || layer._createCurrentCoords(originalCoords)),
fallbackZoom = (tile._fallbackZoom = tile._fallbackZoom === undefined ? originalCoords.z - 1 : tile._fallbackZoom - 1),
scale = (tile._fallbackScale = (tile._fallbackScale || 1) * 2),
tileSize = layer.getTileSize(),
style = tile.style,
newUrl,
top,
left;
// If no lower zoom tiles are available, fallback to errorTile.
if (fallbackZoom < layer.options.minNativeZoom) {
return this._originalTileOnError(done, tile, e);
}
// Modify tilePoint for replacement img.
currentCoords.z = fallbackZoom;
currentCoords.x = Math.floor(currentCoords.x / 2);
currentCoords.y = Math.floor(currentCoords.y / 2);
// Generate new src path.
newUrl = layer.getTileUrl(currentCoords);
// Zoom replacement img.
style.width = tileSize.x * scale + 'px';
style.height = tileSize.y * scale + 'px';
// Compute margins to adjust position.
top = (originalCoords.y - currentCoords.y * scale) * tileSize.y;
style.marginTop = -top + 'px';
left = (originalCoords.x - currentCoords.x * scale) * tileSize.x;
style.marginLeft = -left + 'px';
// Crop (clip) image.
// `clip` is deprecated, but browsers support for `clip-path: inset()` is far behind.
// http://caniuse.com/#feat=css-clip-path
style.clip = 'rect(' + top + 'px ' + (left + tileSize.x) + 'px ' + (top + tileSize.y) + 'px ' + left + 'px)';
layer.fire('tilefallback', {
tile: tile,
url: tile._originalSrc,
urlMissing: tile.src,
urlFallback: newUrl,
});
tile.src = newUrl;
},
_tileOnLoad: function (done, tile, e) {
var layer = this, // `this` is bound to the Tile Layer in L.TileLayer.prototype.createTile.
originalCoords = tile._originalCoords,
fallbackZoom = (tile._fallbackZoom = tile._fallbackZoom === undefined ? originalCoords.z - 1 : tile._fallbackZoom - 1);
if (fallbackZoom > 17) {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
if (xhr.responseText.length == 2423) {
var currentCoords = (tile._currentCoords = tile._currentCoords || layer._createCurrentCoords(originalCoords)),
scale = (tile._fallbackScale = (tile._fallbackScale || 1) * 2),
tileSize = layer.getTileSize(),
style = tile.style,
newUrl,
top,
left;
currentCoords.z = fallbackZoom;
currentCoords.x = Math.floor(currentCoords.x / 2);
currentCoords.y = Math.floor(currentCoords.y / 2);
// Generate new src path.
newUrl = layer.getTileUrl(currentCoords);
// Zoom replacement img.
style.width = tileSize.x * scale + 'px';
style.height = tileSize.y * scale + 'px';
// Compute margins to adjust position.
top = (originalCoords.y - currentCoords.y * scale) * tileSize.y;
style.marginTop = -top + 'px';
left = (originalCoords.x - currentCoords.x * scale) * tileSize.x;
style.marginLeft = -left + 'px';
// Crop (clip) image.
// `clip` is deprecated, but browsers support for `clip-path: inset()` is far behind.
// http://caniuse.com/#feat=css-clip-path
style.clip = 'rect(' + top + 'px ' + (left + tileSize.x) + 'px ' + (top + tileSize.y) + 'px ' + left + 'px)';
layer.fire('tilefallback', {
tile: tile,
url: tile._originalSrc,
urlMissing: tile.src,
urlFallback: newUrl,
});
tile.src = newUrl;
} else {
done(null, tile);
}
}
};
// 发送请求获取瓦片文件
xhr.open('GET', tile.src, true);
xhr.send();
} else {
done(null, tile);
}
},
getTileUrl: function (coords) {
var z = (coords.z = coords.fallback ? coords.z : this._getZoomForUrl());
var data = {
r: L.Browser.retina ? '@2x' : '',
s: this._getSubdomain(coords),
x: coords.x,
y: coords.y,
z: z,
};
if (this._map && !this._map.options.crs.infinite) {
var invertedY = this._globalTileRange.max.y - coords.y;
if (this.options.tms) {
data['y'] = invertedY;
}
data['-y'] = invertedY;
}
return L.Util.template(this._url, L.extend(data, this.options));
},
});
// Supply with a factory for consistency with Leaflet.
L.tileLayer.fallback = function (urlTemplate, options) {
return new L.TileLayer.Fallback(urlTemplate, options);
};