feat: initialize mini program map engine
This commit is contained in:
161
miniprogram/engine/renderer/webglMapRenderer.ts
Normal file
161
miniprogram/engine/renderer/webglMapRenderer.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { TrackLayer } from '../layer/trackLayer'
|
||||
import { GpsLayer } from '../layer/gpsLayer'
|
||||
import { TileLayer } from '../layer/tileLayer'
|
||||
import { TileStore, type TileStoreCallbacks } from '../tile/tileStore'
|
||||
import { type MapRenderer, type MapRendererStats, type MapScene } from './mapRenderer'
|
||||
import { WebGLTileRenderer } from './webglTileRenderer'
|
||||
import { WebGLVectorRenderer } from './webglVectorRenderer'
|
||||
|
||||
const RENDER_FRAME_MS = 16
|
||||
const ANIMATION_FRAME_MS = 33
|
||||
|
||||
export class WebGLMapRenderer implements MapRenderer {
|
||||
tileStore: TileStore
|
||||
tileLayer: TileLayer
|
||||
trackLayer: TrackLayer
|
||||
gpsLayer: GpsLayer
|
||||
tileRenderer: WebGLTileRenderer
|
||||
vectorRenderer: WebGLVectorRenderer
|
||||
scene: MapScene | null
|
||||
renderTimer: number
|
||||
animationTimer: number
|
||||
destroyed: boolean
|
||||
animationPaused: boolean
|
||||
pulseFrame: number
|
||||
lastStats: MapRendererStats
|
||||
onStats?: (stats: MapRendererStats) => void
|
||||
onTileError?: (message: string) => void
|
||||
|
||||
constructor(onStats?: (stats: MapRendererStats) => void, onTileError?: (message: string) => void) {
|
||||
this.onStats = onStats
|
||||
this.onTileError = onTileError
|
||||
this.tileStore = new TileStore({
|
||||
onTileReady: () => {
|
||||
this.scheduleRender()
|
||||
},
|
||||
onTileError: (message) => {
|
||||
if (this.onTileError) {
|
||||
this.onTileError(message)
|
||||
}
|
||||
this.scheduleRender()
|
||||
},
|
||||
} satisfies TileStoreCallbacks)
|
||||
this.tileLayer = new TileLayer()
|
||||
this.trackLayer = new TrackLayer()
|
||||
this.gpsLayer = new GpsLayer()
|
||||
this.tileRenderer = new WebGLTileRenderer(this.tileLayer, this.tileStore)
|
||||
this.vectorRenderer = new WebGLVectorRenderer(this.trackLayer, this.gpsLayer)
|
||||
this.scene = null
|
||||
this.renderTimer = 0
|
||||
this.animationTimer = 0
|
||||
this.destroyed = false
|
||||
this.animationPaused = false
|
||||
this.pulseFrame = 0
|
||||
this.lastStats = {
|
||||
visibleTileCount: 0,
|
||||
readyTileCount: 0,
|
||||
memoryTileCount: 0,
|
||||
diskTileCount: 0,
|
||||
memoryHitCount: 0,
|
||||
diskHitCount: 0,
|
||||
networkFetchCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void {
|
||||
this.tileRenderer.attachCanvas(canvasNode, width, height, dpr)
|
||||
this.vectorRenderer.attachContext(this.tileRenderer.gl, canvasNode)
|
||||
this.startAnimation()
|
||||
this.scheduleRender()
|
||||
}
|
||||
|
||||
updateScene(scene: MapScene): void {
|
||||
this.scene = scene
|
||||
this.scheduleRender()
|
||||
}
|
||||
|
||||
setAnimationPaused(paused: boolean): void {
|
||||
this.animationPaused = paused
|
||||
if (!paused) {
|
||||
this.scheduleRender()
|
||||
}
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
this.destroyed = true
|
||||
if (this.renderTimer) {
|
||||
clearTimeout(this.renderTimer)
|
||||
this.renderTimer = 0
|
||||
}
|
||||
if (this.animationTimer) {
|
||||
clearTimeout(this.animationTimer)
|
||||
this.animationTimer = 0
|
||||
}
|
||||
this.vectorRenderer.destroy()
|
||||
this.tileRenderer.destroy()
|
||||
this.tileStore.destroy()
|
||||
this.scene = null
|
||||
}
|
||||
|
||||
startAnimation(): void {
|
||||
if (this.animationTimer) {
|
||||
return
|
||||
}
|
||||
|
||||
const tick = () => {
|
||||
if (this.destroyed) {
|
||||
this.animationTimer = 0
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.animationPaused) {
|
||||
this.pulseFrame = (this.pulseFrame + 1) % 360
|
||||
this.scheduleRender()
|
||||
}
|
||||
|
||||
this.animationTimer = setTimeout(tick, ANIMATION_FRAME_MS) as unknown as number
|
||||
}
|
||||
|
||||
tick()
|
||||
}
|
||||
|
||||
scheduleRender(): void {
|
||||
if (this.renderTimer || !this.scene || this.destroyed) {
|
||||
return
|
||||
}
|
||||
|
||||
this.renderTimer = setTimeout(() => {
|
||||
this.renderTimer = 0
|
||||
this.renderFrame()
|
||||
}, RENDER_FRAME_MS) as unknown as number
|
||||
}
|
||||
|
||||
renderFrame(): void {
|
||||
if (!this.scene) {
|
||||
return
|
||||
}
|
||||
|
||||
this.tileRenderer.render(this.scene)
|
||||
this.vectorRenderer.render(this.scene, this.pulseFrame)
|
||||
this.emitStats(this.tileStore.getStats(this.tileLayer.lastVisibleTileCount, this.tileLayer.lastReadyTileCount))
|
||||
}
|
||||
|
||||
emitStats(stats: MapRendererStats): void {
|
||||
if (
|
||||
stats.visibleTileCount === this.lastStats.visibleTileCount
|
||||
&& stats.readyTileCount === this.lastStats.readyTileCount
|
||||
&& stats.memoryTileCount === this.lastStats.memoryTileCount
|
||||
&& stats.diskTileCount === this.lastStats.diskTileCount
|
||||
&& stats.memoryHitCount === this.lastStats.memoryHitCount
|
||||
&& stats.diskHitCount === this.lastStats.diskHitCount
|
||||
&& stats.networkFetchCount === this.lastStats.networkFetchCount
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
this.lastStats = stats
|
||||
if (this.onStats) {
|
||||
this.onStats(stats)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user