概述
TileLayer
是 Layer
的子類,繼承自GridLayer
基類,用于加載和顯示瓦片地圖。它提供了加載和顯示瓦片地圖的功能,支持自定義瓦片的 URL 格式和參數。
源碼分析
源碼實現
TileLayer
的源碼實現如下:
export var TileLayer = GridLayer.extend({options: {minZoom: 0, // 最小縮放級別maxZoom: 18, // 最大縮放級別subdomains: "abc", // 子域名,用于分散請求,繞過瀏覽器的并發限制errorTileUrl: "", // 錯誤瓦片的URL,加載失敗時顯示的備用圖片URLzoomOffset: 0, // 縮放偏移量,調整請求中的z值tms: false,// 是否使用TMS格式,y軸反轉zoomReverse: false, // 是否反轉縮放級別detectRetina: false, // 是否檢測視網膜顯示,加載高分辨率瓦片crossOrigin: false, // 控制跨域請求referrerPolicy: false, // 防盜鏈策略},initialize: function (url, options) {this._url = url;options = Util.setOptions(this, options);if (options.detectRetina && Browser.retina && options.maxZoom > 0) {options.tileSize = Math.floor(options.tileSize / 2);if (!options.zoomReverse) {options.zoomOffset++;options.maxZoom = Math.max(options.minZoom, options.maxZoom - 1);} else {options.zoomOffset--;options.minZoom = Math.min(options.maxZoom, options.minZoom + 1);}options.minZoom = Math.max(0, options.minZoom);} else if (!options.zoomReverse) {options.maxZoom = Math.max(options.minZoom, options.maxZoom);} else {options.minZoom = Math.min(options.maxZoom, options.minZoom);}if (typeof options.subdomains === "string") {options.subdomains = options.subdomains.split("");}this.on("tileunload", this._onTileRemove);},setUrl: function (url, noRedraw) {if (this._url === url && noRedraw === undefined) {noRedraw = true;}this._url = url;if (!noRedraw) {this.redraw();}return this;},createTile: function (coords, done) {var tile = document.createElement("img");DomEvent.on(tile, "load", Util.bind(this._tileOnLoad, this, done, tile));DomEvent.on(tile, "error", Util.bind(this._tileOnError, this, done, tile));if (this.options.crossOrigin || this.options.crossOrigin === "") {tile.crossOrigin =this.options.crossOrigin === true ? "" : this.options.crossOrigin;}if (typeof this.options.referrerPolicy === "string") {tile.referrerPolicy = this.options.referrerPolicy;}tile.alt = "";tile.src = this.getTileUrl(coords);return tile;},getTileUrl: function (coords) {var data = {r: Browser.retina ? "@2x" : "",s: this._getSubdomain(coords),x: coords.x,y: coords.y,z: this._getZoomForUrl(),};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 Util.template(this._url, Util.extend(data, this.options));},_tileOnLoad: function (done, tile) {if (Browser.ielt9) {setTimeout(Util.bind(done, this, null, tile), 0);} else {done(null, tile);}},_tileOnError: function (done, tile, e) {var errorUrl = this.options.errorTileUrl;if (errorUrl && tile.getAttribute("src") !== errorUrl) {tile.src = errorUrl;}done(e, tile);},_onTileRemove: function (e) {e.tile.onload = null;},_getZoomForUrl: function () {var zoom = this._tileZoom,maxZoom = this.options.maxZoom,zoomReverse = this.options.zoomReverse,zoomOffset = this.options.zoomOffset;if (zoomReverse) {zoom = maxZoom - zoom;}return zoom + zoomOffset;},_getSubdomain: function (tilePoint) {var index =Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;return this.options.subdomains[index];},_abortLoading: function () {var i, tile;for (i in this._tiles) {if (this._tiles[i].coords.z !== this._tileZoom) {tile = this._tiles[i].el;tile.onload = Util.falseFn;tile.onerror = Util.falseFn;if (!tile.complete) {tile.src = Util.emptyImageUrl;var coords = this._tiles[i].coords;DomUtil.remove(tile);delete this._tiles[i];this.fire("tileabort", { tile, coords });}}}},_removeTile: function (key) {var tile = this._tiles[key];tile.el.setAttribute("src", Util.emptyImageUrl);return GridLayer.prototype._removeTile.call(this, key);},_tileReady: function (coords, err, tile) {if (!this._map ||(tile && tile.getAttribute("src") === Util.emptyImageUrl)) {return;}return GridLayer.prototype._tileReady.call(this, coords, err, tile);},
});export function tileLayer(url, options) {return new TileLayer(url, options);
}
源碼解析
initialize
方法
initialize
方法用于初始化TileLayer
對象,設置默認選項和事件監聽器,保存URL末班
this._url
:存儲瓦片圖層的URL。options
:用戶提供的選項,用于覆蓋默認選項。options.detectRetina
:如果啟用了視網膜檢測,將瓦片大小減半,并調整縮放級別和偏移量。options.subdomains
:如果是字符串,將其拆分為子域名數組。this.on("tileunload", this._onTileRemove)
:監聽tileunload
事件,當瓦片被卸載時調
方法詳解
setUrl(url,noRedraw)
:
- 更新URL模版,若URL未變化且
noRedraw
為true
,則不重繪圖層。 - 調用
redraw()
方法強制刷新,重新繪制圖層。
createTile(coords,done)
:
- 創建一個新的HTML圖片元素作為瓦片。
- 設置圖片元素的
load
和error
事件監聽器,當圖片加載完成或加載失敗時調用done
回調函數。 - 設置圖片元素的
crossOrigin
屬性,控制跨域請求。 - 設置圖片元素的
referrerPolicy
屬性,控制防盜鏈策略。 - 設置圖片元素的
alt
屬性為空字符串。 - 設置圖片元素的
src
屬性為getTileUrl(coords)
方法返回的URL。 - 返回創建的圖片元素。
getTileUrl(coords)
:
- 計算并返回URL模版,將URL中的占位符替換為實際的值。
- 占位符包括:
{r}
:如果啟用了視網膜檢測,替換為@2x
,否則為空字符串。{s}
:根據坐標計算子域名。{x}
:替換為X坐標。{y}
:替換為Y坐標。{z}
:替換為縮放級別。{-y}
:如果啟用了TMS格式,替換為反轉的Y坐標,否則為空字符串。
- 返回替換后的URL。
_tileOnLoad(done,tile)
:
- 當圖片加載完成時調用
done
回調函數,IE9以下延遲觸發。
_tileOnError(done,tile,e)
:
- 當圖片加載失敗時調用
done
回調函數,并設置錯誤信息。 - 如果啟用了錯誤瓦片的URL,將圖片元素的
src
屬性設置為錯誤瓦片的URL,再次嘗試加載。 - 返回錯誤信息。
_getZoomForUrl()
:
- 根據
zoomReverse
和zoomOffset
選項計算縮放級別。 - 如果啟用了
zoomReverse
,將縮放級別反轉。 - 返回計算后的縮放級別。
_getSubdomain(tilePoint)
: - 根據X和Y坐標計算子域名。
- 使用
subdomains
選項中的子域名數組,根據X和Y坐標的絕對值取模計算索引。 - 返回計算得到的子域名。
瓦片管理
_abortLoading()
:中止非當前縮放級別的瓦片加載
- 重置
onload
/onerror
事件監聽器, - 將非當前縮放級別的瓦片的
src
屬性設置為空字符串,并從_tiles
對象中移除。
_removeTile(key)
:
- 從
_tiles
對象中移除指定鍵的瓦片。 - 將瓦片的
src
屬性設置為空字符串,取消網絡請求。 - 調用父類的
_removeTile
方法,移除瓦片元素。
_tileReady(coords,err,tile)
:
- 當瓦片準備就緒時調用。
- 如果URL為空字符串或
_map
不存在,返回。 - 調用父類的
_tileReady
方法,處理瓦片就緒事件。
關鍵邏輯
??- Retina
適配??:通過detectRetina
自動調整瓦片尺寸和縮放,加載高分辨率圖片。
- 子域名輪詢??:分散請求,避免瀏覽器并發限制。
- ?TMS坐標反轉??:通過
tms: true
適配不同的瓦片服務標準。 - 跨域處理??:通過
crossOrigin
屬性解決CORS
問題,訪問像素數據。 - 錯誤恢復??:加載失敗時顯示
errorTileUrl
,提升用戶體驗
總結
TileLayer
是Leaflet
中用于加載和顯示瓦片地圖的核心組件,通過繼承GridLayer
基類,實現了瓦片的加載和管理,支持自定義URL格式和參數,提供了豐富的功能和選項,適用于各種瓦片地圖服務。