feat: initialize mini program map engine

This commit is contained in:
2026-03-19 15:58:48 +08:00
commit 03abe28d8c
49 changed files with 28584 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
import { createTileGrid, type TileItem } from '../../utils/tile'
import { getTileSizePx, type CameraState } from '../camera/camera'
import { type MapScene } from '../renderer/mapRenderer'
import { type TileStore } from '../tile/tileStore'
import { type MapLayer, type LayerRenderContext } from './mapLayer'
function buildGridKey(scene: MapScene, tileSize: number): string {
return [
scene.tileSource,
scene.zoom,
scene.centerTileX,
scene.centerTileY,
scene.viewportWidth,
scene.viewportHeight,
tileSize,
scene.overdraw,
].join('|')
}
export class TileLayer implements MapLayer {
lastVisibleTileCount: number
lastReadyTileCount: number
cachedGridKey: string
cachedTiles: TileItem[]
constructor() {
this.lastVisibleTileCount = 0
this.lastReadyTileCount = 0
this.cachedGridKey = ''
this.cachedTiles = []
}
prepareTiles(scene: MapScene, camera: CameraState, tileStore: TileStore): TileItem[] {
const tileSize = getTileSizePx(camera)
if (!tileSize) {
this.lastVisibleTileCount = 0
this.lastReadyTileCount = 0
this.cachedGridKey = ''
this.cachedTiles = []
return []
}
const gridKey = buildGridKey(scene, tileSize)
if (gridKey !== this.cachedGridKey) {
this.cachedGridKey = gridKey
this.cachedTiles = createTileGrid({
urlTemplate: scene.tileSource,
zoom: scene.zoom,
centerTileX: scene.centerTileX,
centerTileY: scene.centerTileY,
viewportWidth: scene.viewportWidth,
viewportHeight: scene.viewportHeight,
tileSize,
overdraw: scene.overdraw,
})
}
tileStore.queueVisibleTiles(this.cachedTiles, scene, gridKey)
this.lastVisibleTileCount = this.cachedTiles.length
this.lastReadyTileCount = 0
return this.cachedTiles
}
draw(context: LayerRenderContext): void {
const { ctx, scene, camera, tileStore } = context
const tiles = this.prepareTiles(scene, camera, tileStore)
for (const tile of tiles) {
const entry = tileStore.getEntry(tile.url)
const drawLeft = tile.leftPx + scene.translateX
const drawTop = tile.topPx + scene.translateY
if (entry && entry.status === 'ready' && entry.image) {
ctx.drawImage(entry.image, drawLeft, drawTop, tile.sizePx, tile.sizePx)
this.lastReadyTileCount += 1
continue
}
const parentFallback = tileStore.getParentFallbackSlice(tile, scene)
let drewFallback = false
if (parentFallback) {
ctx.drawImage(
parentFallback.entry.image,
parentFallback.sourceX,
parentFallback.sourceY,
parentFallback.sourceWidth,
parentFallback.sourceHeight,
drawLeft,
drawTop,
tile.sizePx,
tile.sizePx,
)
drewFallback = true
}
const childFallback = tileStore.getChildFallback(tile, scene)
if (childFallback) {
const cellWidth = tile.sizePx / childFallback.division
const cellHeight = tile.sizePx / childFallback.division
for (const child of childFallback.children) {
const childImageWidth = child.entry.image.width || 256
const childImageHeight = child.entry.image.height || 256
ctx.drawImage(
child.entry.image,
0,
0,
childImageWidth,
childImageHeight,
drawLeft + child.offsetX * cellWidth,
drawTop + child.offsetY * cellHeight,
cellWidth,
cellHeight,
)
}
drewFallback = true
}
if (!drewFallback) {
ctx.fillStyle = entry && entry.status === 'error' ? '#d9b2b2' : '#dbeed4'
ctx.fillRect(drawLeft, drawTop, tile.sizePx, tile.sizePx)
}
}
}
}