فهرست منبع

refactor: clean map page and tune heading mode

zhangyan 2 هفته پیش
والد
کامیت
ceb5479c69

+ 1 - 2
miniprogram/app.ts

@@ -9,8 +9,7 @@ App<IAppOption>({
 
     // 登录
     wx.login({
-      success: res => {
-        console.log(res.code)
+      success: () => {
         // 发送 res.code 到后台换取 openId, sessionKey, unionId
       },
     })

+ 4 - 4
miniprogram/engine/map/mapEngine.ts

@@ -25,11 +25,11 @@ const INERTIA_MIN_SPEED = 0.02
 const PREVIEW_RESET_DURATION_MS = 140
 const UI_SYNC_INTERVAL_MS = 80
 const ROTATE_STEP_DEG = 15
-const AUTO_ROTATE_FRAME_MS = 12
-const AUTO_ROTATE_EASE = 0.2
+const AUTO_ROTATE_FRAME_MS = 10
+const AUTO_ROTATE_EASE = 0.24
 const AUTO_ROTATE_SNAP_DEG = 0.1
-const AUTO_ROTATE_DEADZONE_DEG = 0.35
-const AUTO_ROTATE_MAX_STEP_DEG = 1.35
+const AUTO_ROTATE_DEADZONE_DEG = 1.5
+const AUTO_ROTATE_MAX_STEP_DEG = 0.9
 const AUTO_ROTATE_HEADING_SMOOTHING = 0.32
 
 const SAMPLE_TRACK_WGS84: LonLatPoint[] = [

+ 0 - 247
miniprogram/engine/renderer/canvasMapRenderer.ts

@@ -1,247 +0,0 @@
-import { getTileSizePx, type CameraState } from '../camera/camera'
-import {
-  TileStore,
-  type TileStoreCallbacks,
-  type TileStoreStats,
-} from '../tile/tileStore'
-import { type LonLatPoint } from '../../utils/projection'
-import { type MapLayer } from '../layer/mapLayer'
-import { TileLayer } from '../layer/tileLayer'
-import { TrackLayer } from '../layer/trackLayer'
-import { GpsLayer } from '../layer/gpsLayer'
-
-const RENDER_FRAME_MS = 16
-
-export interface CanvasMapScene {
-  tileSource: string
-  zoom: number
-  centerTileX: number
-  centerTileY: number
-  viewportWidth: number
-  viewportHeight: number
-  visibleColumns: number
-  overdraw: number
-  translateX: number
-  translateY: number
-  rotationRad: number
-  previewScale: number
-  previewOriginX: number
-  previewOriginY: number
-  track: LonLatPoint[]
-  gpsPoint: LonLatPoint
-}
-
-export type CanvasMapRendererStats = TileStoreStats
-
-function buildCamera(scene: CanvasMapScene): CameraState {
-  return {
-    centerWorldX: scene.centerTileX,
-    centerWorldY: scene.centerTileY,
-    viewportWidth: scene.viewportWidth,
-    viewportHeight: scene.viewportHeight,
-    visibleColumns: scene.visibleColumns,
-    rotationRad: scene.rotationRad,
-  }
-}
-
-export class CanvasMapRenderer {
-  canvas: any
-  ctx: any
-  dpr: number
-  scene: CanvasMapScene | null
-  tileStore: TileStore
-  tileLayer: TileLayer
-  layers: MapLayer[]
-  renderTimer: number
-  animationTimer: number
-  destroyed: boolean
-  animationPaused: boolean
-  pulseFrame: number
-  lastStats: CanvasMapRendererStats
-  onStats?: (stats: CanvasMapRendererStats) => void
-  onTileError?: (message: string) => void
-
-  constructor(
-    onStats?: (stats: CanvasMapRendererStats) => void,
-    onTileError?: (message: string) => void,
-  ) {
-    this.onStats = onStats
-    this.onTileError = onTileError
-    this.canvas = null
-    this.ctx = null
-    this.dpr = 1
-    this.scene = null
-    this.tileStore = new TileStore({
-      onTileReady: () => {
-        this.scheduleRender()
-      },
-      onTileError: (message) => {
-        if (this.onTileError) {
-          this.onTileError(message)
-        }
-        this.scheduleRender()
-      },
-    } satisfies TileStoreCallbacks)
-    this.tileLayer = new TileLayer()
-    this.layers = [
-      this.tileLayer,
-      new TrackLayer(),
-      new GpsLayer(),
-    ]
-    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.canvas = canvasNode
-    this.ctx = canvasNode.getContext('2d')
-    this.dpr = dpr || 1
-
-    canvasNode.width = Math.max(1, Math.floor(width * this.dpr))
-    canvasNode.height = Math.max(1, Math.floor(height * this.dpr))
-
-    if (typeof this.ctx.setTransform === 'function') {
-      this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0)
-    } else {
-      this.ctx.scale(this.dpr, this.dpr)
-    }
-
-    this.tileStore.attachCanvas(canvasNode)
-    this.startAnimation()
-    this.scheduleRender()
-  }
-
-  updateScene(scene: CanvasMapScene): 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.tileStore.destroy()
-    this.canvas = null
-    this.ctx = null
-    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, 33) as unknown as number
-    }
-
-    tick()
-  }
-
-  scheduleRender(): void {
-    if (this.renderTimer || !this.ctx || !this.scene || this.destroyed) {
-      return
-    }
-
-    this.renderTimer = setTimeout(() => {
-      this.renderTimer = 0
-      this.renderFrame()
-    }, RENDER_FRAME_MS) as unknown as number
-  }
-
-  emitStats(stats: CanvasMapRendererStats): 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)
-    }
-  }
-
-  renderFrame(): void {
-    if (!this.ctx || !this.scene) {
-      return
-    }
-
-    const scene = this.scene
-    const ctx = this.ctx
-    const camera = buildCamera(scene)
-    const tileSize = getTileSizePx(camera)
-
-    ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight)
-    ctx.fillStyle = '#dbeed4'
-    ctx.fillRect(0, 0, scene.viewportWidth, scene.viewportHeight)
-
-    if (!tileSize) {
-      this.emitStats(this.tileStore.getStats(0, 0))
-      return
-    }
-
-    const previewScale = scene.previewScale || 1
-    const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2
-    const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2
-
-    ctx.save()
-    ctx.translate(previewOriginX, previewOriginY)
-    ctx.scale(previewScale, previewScale)
-    ctx.translate(-previewOriginX, -previewOriginY)
-
-    for (const layer of this.layers) {
-      layer.draw({
-        ctx,
-        camera,
-        scene,
-        pulseFrame: this.pulseFrame,
-        tileStore: this.tileStore,
-      })
-    }
-
-    ctx.restore()
-    this.emitStats(this.tileStore.getStats(this.tileLayer.lastVisibleTileCount, this.tileLayer.lastReadyTileCount))
-  }
-}

+ 0 - 67
miniprogram/engine/renderer/canvasOverlayRenderer.ts

@@ -1,67 +0,0 @@
-import { type MapLayer } from '../layer/mapLayer'
-import { buildCamera, type MapScene } from './mapRenderer'
-import { type TileStore } from '../tile/tileStore'
-
-export class CanvasOverlayRenderer {
-  canvas: any
-  ctx: any
-  dpr: number
-  layers: MapLayer[]
-
-  constructor(layers: MapLayer[]) {
-    this.canvas = null
-    this.ctx = null
-    this.dpr = 1
-    this.layers = layers
-  }
-
-  attachCanvas(canvasNode: any, width: number, height: number, dpr: number): void {
-    this.canvas = canvasNode
-    this.ctx = canvasNode.getContext('2d')
-    this.dpr = dpr || 1
-
-    canvasNode.width = Math.max(1, Math.floor(width * this.dpr))
-    canvasNode.height = Math.max(1, Math.floor(height * this.dpr))
-
-    if (typeof this.ctx.setTransform === 'function') {
-      this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0)
-    } else {
-      this.ctx.scale(this.dpr, this.dpr)
-    }
-  }
-
-  clear(): void {
-    this.canvas = null
-    this.ctx = null
-  }
-
-  render(scene: MapScene, tileStore: TileStore, pulseFrame: number): void {
-    if (!this.ctx) {
-      return
-    }
-
-    const camera = buildCamera(scene)
-    const ctx = this.ctx
-    const previewScale = scene.previewScale || 1
-    const previewOriginX = scene.previewOriginX || scene.viewportWidth / 2
-    const previewOriginY = scene.previewOriginY || scene.viewportHeight / 2
-
-    ctx.clearRect(0, 0, scene.viewportWidth, scene.viewportHeight)
-    ctx.save()
-    ctx.translate(previewOriginX, previewOriginY)
-    ctx.scale(previewScale, previewScale)
-    ctx.translate(-previewOriginX, -previewOriginY)
-
-    for (const layer of this.layers) {
-      layer.draw({
-        ctx,
-        camera,
-        scene,
-        pulseFrame,
-        tileStore,
-      })
-    }
-
-    ctx.restore()
-  }
-}

+ 0 - 2
miniprogram/pages/index/index.ts

@@ -1,6 +1,5 @@
 // index.ts
 // 获取应用实例
-const app = getApp<IAppOption>()
 const defaultAvatarUrl = 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0'
 
 Component({
@@ -42,7 +41,6 @@ Component({
       wx.getUserProfile({
         desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
         success: (res) => {
-          console.log(res)
           this.setData({
             userInfo: res.userInfo,
             hasUserInfo: true

+ 13 - 3
miniprogram/pages/map/map.ts

@@ -1,6 +1,10 @@
 import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine'
 
-const INTERNAL_BUILD_VERSION = 'map-build-58'
+type MapPageData = MapEngineViewState & {
+  showDebugPanel: boolean
+}
+
+const INTERNAL_BUILD_VERSION = 'map-build-63'
 
 let mapEngine: MapEngine | null = null
 
@@ -18,7 +22,7 @@ function getFallbackStageRect(): MapEngineStageRect {
 }
 
 Page({
-  data: {} as MapEngineViewState,
+  data: { showDebugPanel: false } as MapPageData,
 
   onLoad() {
     mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, {
@@ -27,7 +31,7 @@ Page({
       },
     })
 
-    this.setData(mapEngine.getInitialData())
+    this.setData({ ...mapEngine.getInitialData(), showDebugPanel: false })
   },
 
   onReady() {
@@ -154,6 +158,12 @@ Page({
       mapEngine.handleAutoRotateCalibrate()
     }
   },
+
+  handleToggleDebugPanel() {
+    this.setData({
+      showDebugPanel: !this.data.showDebugPanel,
+    })
+  },
 })
 
 

+ 71 - 64
miniprogram/pages/map/map.wxml

@@ -48,18 +48,6 @@
   </view>
 
   <scroll-view class="info-panel" scroll-y enhanced show-scrollbar="true">
-    <view class="info-panel__row">
-      <text class="info-panel__label">Build</text>
-      <text class="info-panel__value">{{buildVersion}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Renderer</text>
-      <text class="info-panel__value">{{renderMode}}</text>
-    </view>
-    <view class="info-panel__row info-panel__row--stack">
-      <text class="info-panel__label">Projection</text>
-      <text class="info-panel__value">{{projectionMode}}</text>
-    </view>
     <view class="info-panel__row">
       <text class="info-panel__label">Heading Mode</text>
       <text class="info-panel__value">{{orientationModeText}}</text>
@@ -68,22 +56,6 @@
       <text class="info-panel__label">Sensor Heading</text>
       <text class="info-panel__value">{{sensorHeadingText}}</text>
     </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">North Ref</text>
-      <text class="info-panel__value">{{northReferenceText}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Auto Source</text>
-      <text class="info-panel__value">{{autoRotateSourceText}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Calibration</text>
-      <text class="info-panel__value">{{autoRotateCalibrationText}}</text>
-    </view>
-    <view class="info-panel__row info-panel__row--stack">
-      <text class="info-panel__label">Tile URL</text>
-      <text class="info-panel__value">{{tileSource}}</text>
-    </view>
     <view class="info-panel__row">
       <text class="info-panel__label">Zoom</text>
       <text class="info-panel__value">{{zoom}}</text>
@@ -92,47 +64,82 @@
       <text class="info-panel__label">Rotation</text>
       <text class="info-panel__value">{{rotationText}}</text>
     </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Center Tile</text>
-      <text class="info-panel__value">{{centerText}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Tile Size</text>
-      <text class="info-panel__value">{{tileSizePx}}px</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Visible Tiles</text>
-      <text class="info-panel__value">{{visibleTileCount}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Ready Tiles</text>
-      <text class="info-panel__value">{{readyTileCount}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Memory Tiles</text>
-      <text class="info-panel__value">{{memoryTileCount}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Disk Tiles</text>
-      <text class="info-panel__value">{{diskTileCount}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Cache Hit</text>
-      <text class="info-panel__value">{{cacheHitRateText}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Disk Hits</text>
-      <text class="info-panel__value">{{diskHitCount}}</text>
-    </view>
-    <view class="info-panel__row">
-      <text class="info-panel__label">Net Fetches</text>
-      <text class="info-panel__value">{{networkFetchCount}}</text>
-    </view>
     <view class="info-panel__row info-panel__row--stack">
       <text class="info-panel__label">Status</text>
       <text class="info-panel__value">{{statusText}}</text>
     </view>
 
+    <view class="control-row">
+      <view class="control-chip control-chip--secondary" bindtap="handleToggleDebugPanel">{{showDebugPanel ? '隐藏调试' : '查看调试'}}</view>
+    </view>
+
+    <block wx:if="{{showDebugPanel}}">
+      <view class="info-panel__row">
+        <text class="info-panel__label">Build</text>
+        <text class="info-panel__value">{{buildVersion}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Renderer</text>
+        <text class="info-panel__value">{{renderMode}}</text>
+      </view>
+      <view class="info-panel__row info-panel__row--stack">
+        <text class="info-panel__label">Projection</text>
+        <text class="info-panel__value">{{projectionMode}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">North Ref</text>
+        <text class="info-panel__value">{{northReferenceText}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Auto Source</text>
+        <text class="info-panel__value">{{autoRotateSourceText}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Calibration</text>
+        <text class="info-panel__value">{{autoRotateCalibrationText}}</text>
+      </view>
+      <view class="info-panel__row info-panel__row--stack">
+        <text class="info-panel__label">Tile URL</text>
+        <text class="info-panel__value">{{tileSource}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Center Tile</text>
+        <text class="info-panel__value">{{centerText}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Tile Size</text>
+        <text class="info-panel__value">{{tileSizePx}}px</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Visible Tiles</text>
+        <text class="info-panel__value">{{visibleTileCount}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Ready Tiles</text>
+        <text class="info-panel__value">{{readyTileCount}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Memory Tiles</text>
+        <text class="info-panel__value">{{memoryTileCount}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Disk Tiles</text>
+        <text class="info-panel__value">{{diskTileCount}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Cache Hit</text>
+        <text class="info-panel__value">{{cacheHitRateText}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Disk Hits</text>
+        <text class="info-panel__value">{{diskHitCount}}</text>
+      </view>
+      <view class="info-panel__row">
+        <text class="info-panel__label">Net Fetches</text>
+        <text class="info-panel__value">{{networkFetchCount}}</text>
+      </view>
+    </block>
+
     <view class="control-row">
       <view class="control-chip control-chip--primary" bindtap="handleRecenter">回到首屏</view>
       <view class="control-chip control-chip--secondary" bindtap="handleRotationReset">旋转归零</view>