- Updated tile freshness animation to reduce flicker and improve visual clarity. - Modified MapView component to optimize layer visibility handling and ensure proper map resizing on fullscreen toggle. - Increased tile buffer size in map initialization for better tile loading efficiency. - Implemented logic to limit tile updates to only visible tiles, enhancing rendering performance during map updates.
90 lines
2.8 KiB
TypeScript
90 lines
2.8 KiB
TypeScript
import L, { Util, Browser } from 'leaflet'
|
|
|
|
interface SmartTileLayerCache {
|
|
[key: string]: number | undefined
|
|
}
|
|
|
|
export const SmartTileLayer = L.TileLayer.extend({
|
|
cache: {} as SmartTileLayerCache,
|
|
invalidTile: '',
|
|
map: 0,
|
|
|
|
getTileUrl(coords: { x: number; y: number; z: number }) {
|
|
if (!this._map) return this.invalidTile
|
|
let zoom
|
|
try {
|
|
zoom = this._getZoomForUrl()
|
|
} catch {
|
|
return this.invalidTile
|
|
}
|
|
return this.getTrueTileUrl(coords, zoom)
|
|
},
|
|
|
|
getTrueTileUrl(coords: { x: number; y: number }, zoom: number) {
|
|
const data: Record<string, string | number | undefined> = {
|
|
r: Browser.retina ? '@2x' : '',
|
|
s: this._getSubdomain(coords),
|
|
x: coords.x,
|
|
y: coords.y,
|
|
map: this.map,
|
|
z: zoom,
|
|
}
|
|
if (this._map && !this._map.options.crs.infinite) {
|
|
const invertedY = this._globalTileRange.max.y - coords.y
|
|
if (this.options.tms) {
|
|
data.y = invertedY
|
|
}
|
|
data['-y'] = invertedY
|
|
}
|
|
|
|
const cacheKey = `${data.map}:${data.x}:${data.y}:${data.z}`
|
|
data.cache = this.cache[cacheKey]
|
|
|
|
// Don't request tiles for invalid/unknown map (avoids 404 spam in console)
|
|
const mapId = Number(data.map)
|
|
if (data.map === undefined || data.map === null || mapId < 1) {
|
|
return this.invalidTile
|
|
}
|
|
// Only use placeholder when server explicitly marks tile as invalid (-1)
|
|
if (data.cache === -1) {
|
|
return this.invalidTile
|
|
}
|
|
// Allow tile request when map is valid even if SSE snapshot hasn't arrived yet
|
|
// (avoids empty map when proxy/SSE delays or drops first message)
|
|
if (data.cache === undefined || data.cache === null) {
|
|
data.cache = 0
|
|
}
|
|
|
|
return Util.template(this._url, Util.extend(data, this.options))
|
|
},
|
|
|
|
refresh(x: number, y: number, z: number) {
|
|
let zoom = z
|
|
const maxZoom = this.options.maxZoom
|
|
const zoomReverse = this.options.zoomReverse
|
|
const zoomOffset = this.options.zoomOffset
|
|
|
|
if (zoomReverse) {
|
|
zoom = maxZoom - zoom
|
|
}
|
|
zoom += zoomOffset
|
|
|
|
const key = `${x}:${y}:${zoom}`
|
|
const tile = this._tiles[key]
|
|
if (!tile?.el) return
|
|
const newUrl = this.getTrueTileUrl({ x, y }, z)
|
|
if (tile.el.dataset.tileUrl === newUrl) return
|
|
tile.el.dataset.tileUrl = newUrl
|
|
tile.el.src = newUrl
|
|
tile.el.classList.add('tile-fresh')
|
|
const el = tile.el
|
|
setTimeout(() => el.classList.remove('tile-fresh'), 400)
|
|
},
|
|
}) as unknown as new (urlTemplate: string, options?: L.TileLayerOptions) => L.TileLayer & {
|
|
cache: SmartTileLayerCache
|
|
invalidTile: string
|
|
map: number
|
|
getTrueTileUrl: (coords: { x: number; y: number }, zoom: number) => string
|
|
refresh: (x: number, y: number, z: number) => void
|
|
}
|