| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723 |
- import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine'
- import { loadRemoteMapConfig } from '../../utils/remoteMapConfig'
- type CompassTickData = {
- angle: number
- long: boolean
- major: boolean
- }
- type CompassLabelData = {
- text: string
- angle: number
- rotateBack: number
- radius: number
- className: string
- }
- type SideButtonMode = 'all' | 'left' | 'right' | 'hidden'
- type MapPageData = MapEngineViewState & {
- showDebugPanel: boolean
- statusBarHeight: number
- topInsetHeight: number
- hudPanelIndex: number
- mockBridgeUrlDraft: string
- mockHeartRateBridgeUrlDraft: string
- panelTimerText: string
- panelMileageText: string
- panelDistanceValueText: string
- panelProgressText: string
- panelSpeedValueText: string
- compassTicks: CompassTickData[]
- compassLabels: CompassLabelData[]
- sideButtonMode: SideButtonMode
- showLeftButtonGroup: boolean
- showRightButtonGroups: boolean
- showBottomDebugButton: boolean
- }
- const INTERNAL_BUILD_VERSION = 'map-build-196'
- const REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
- let mapEngine: MapEngine | null = null
- let stageCanvasAttached = false
- function buildSideButtonVisibility(mode: SideButtonMode) {
- return {
- sideButtonMode: mode,
- showLeftButtonGroup: mode === 'all' || mode === 'left' || mode === 'right',
- showRightButtonGroups: mode === 'all' || mode === 'right',
- showBottomDebugButton: mode !== 'hidden',
- }
- }
- function getNextSideButtonMode(currentMode: SideButtonMode): SideButtonMode {
- if (currentMode === 'all') {
- return 'left'
- }
- if (currentMode === 'left') {
- return 'right'
- }
- if (currentMode === 'right') {
- return 'hidden'
- }
- return 'left'
- }
- function buildCompassTicks(): CompassTickData[] {
- const ticks: CompassTickData[] = []
- for (let angle = 0; angle < 360; angle += 5) {
- ticks.push({
- angle,
- long: angle % 15 === 0,
- major: angle % 45 === 0,
- })
- }
- return ticks
- }
- function buildCompassLabels(): CompassLabelData[] {
- return [
- { text: '\u5317', angle: 0, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal compass-widget__mark--north' },
- { text: '\u4e1c\u5317', angle: 45, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northeast' },
- { text: '\u4e1c', angle: 90, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
- { text: '\u4e1c\u5357', angle: 135, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
- { text: '\u5357', angle: 180, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
- { text: '\u897f\u5357', angle: 225, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
- { text: '\u897f', angle: 270, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
- { text: '\u897f\u5317', angle: 315, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northwest' },
- ]
- }
- function getFallbackStageRect(): MapEngineStageRect {
- const systemInfo = wx.getSystemInfoSync()
- const width = Math.max(320, systemInfo.windowWidth)
- const height = Math.max(280, systemInfo.windowHeight)
- return {
- width,
- height,
- left: 0,
- top: 0,
- }
- }
- Page({
- data: {
- showDebugPanel: false,
- statusBarHeight: 0,
- topInsetHeight: 12,
- hudPanelIndex: 0,
- panelTimerText: '00:00:00',
- panelMileageText: '0m',
- panelActionTagText: '目标',
- panelDistanceTagText: '点距',
- panelDistanceValueText: '--',
- panelDistanceUnitText: '',
- panelProgressText: '0/0',
- gameSessionStatus: 'idle',
- gameModeText: '顺序赛',
- locationSourceMode: 'real',
- locationSourceText: '真实定位',
- mockBridgeConnected: false,
- mockBridgeStatusText: '未连接',
- mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
- mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
- mockCoordText: '--',
- mockSpeedText: '--',
- heartRateSourceMode: 'real',
- heartRateSourceText: '真实心率',
- mockHeartRateBridgeConnected: false,
- mockHeartRateBridgeStatusText: '未连接',
- mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
- mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
- mockHeartRateText: '--',
- heartRateScanText: '未扫描',
- heartRateDiscoveredDevices: [],
- panelSpeedValueText: '0',
- panelTelemetryTone: 'blue',
- panelHeartRateZoneNameText: '--',
- panelHeartRateZoneRangeText: '',
- heartRateConnected: false,
- heartRateStatusText: '心率带未连接',
- heartRateDeviceText: '--',
- panelHeartRateValueText: '--',
- panelHeartRateUnitText: '',
- panelCaloriesValueText: '0',
- panelCaloriesUnitText: 'kcal',
- panelAverageSpeedValueText: '0',
- panelAverageSpeedUnitText: 'km/h',
- panelAccuracyValueText: '--',
- panelAccuracyUnitText: '',
- punchButtonText: '打点',
- punchButtonEnabled: false,
- punchHintText: '等待进入检查点范围',
- punchFeedbackVisible: false,
- punchFeedbackText: '',
- punchFeedbackTone: 'neutral',
- contentCardVisible: false,
- contentCardTitle: '',
- contentCardBody: '',
- punchButtonFxClass: '',
- punchFeedbackFxClass: '',
- contentCardFxClass: '',
- mapPulseVisible: false,
- mapPulseLeftPx: 0,
- mapPulseTopPx: 0,
- mapPulseFxClass: '',
- stageFxVisible: false,
- stageFxClass: '',
- compassTicks: buildCompassTicks(),
- compassLabels: buildCompassLabels(),
- ...buildSideButtonVisibility('left'),
- } as unknown as MapPageData,
- onLoad() {
- const systemInfo = wx.getSystemInfoSync()
- const statusBarHeight = systemInfo.statusBarHeight || 0
- const menuButtonRect = wx.getMenuButtonBoundingClientRect()
- const menuButtonBottom = menuButtonRect && typeof menuButtonRect.bottom === 'number' ? menuButtonRect.bottom : statusBarHeight
- mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, {
- onData: (patch) => {
- const nextPatch = patch as Partial<MapPageData>
- const nextData: Partial<MapPageData> = {
- ...nextPatch,
- }
- if (
- typeof nextPatch.mockBridgeUrlText === 'string'
- && this.data.mockBridgeUrlDraft === this.data.mockBridgeUrlText
- ) {
- nextData.mockBridgeUrlDraft = nextPatch.mockBridgeUrlText
- }
- if (
- typeof nextPatch.mockHeartRateBridgeUrlText === 'string'
- && this.data.mockHeartRateBridgeUrlDraft === this.data.mockHeartRateBridgeUrlText
- ) {
- nextData.mockHeartRateBridgeUrlDraft = nextPatch.mockHeartRateBridgeUrlText
- }
- this.setData(nextData)
- },
- })
- this.setData({
- ...mapEngine.getInitialData(),
- showDebugPanel: false,
- statusBarHeight,
- topInsetHeight: Math.max(statusBarHeight + 12, menuButtonBottom + 20),
- hudPanelIndex: 0,
- panelTimerText: '00:00:00',
- panelMileageText: '0m',
- panelActionTagText: '目标',
- panelDistanceTagText: '点距',
- panelDistanceValueText: '--',
- panelDistanceUnitText: '',
- panelProgressText: '0/0',
- gameSessionStatus: 'idle',
- gameModeText: '顺序赛',
- locationSourceMode: 'real',
- locationSourceText: '真实定位',
- mockBridgeConnected: false,
- mockBridgeStatusText: '未连接',
- mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
- mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
- mockCoordText: '--',
- mockSpeedText: '--',
- heartRateSourceMode: 'real',
- heartRateSourceText: '真实心率',
- mockHeartRateBridgeConnected: false,
- mockHeartRateBridgeStatusText: '未连接',
- mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
- mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
- mockHeartRateText: '--',
- panelSpeedValueText: '0',
- panelTelemetryTone: 'blue',
- panelHeartRateZoneNameText: '--',
- panelHeartRateZoneRangeText: '',
- heartRateConnected: false,
- heartRateStatusText: '心率带未连接',
- heartRateDeviceText: '--',
- panelHeartRateValueText: '--',
- panelHeartRateUnitText: '',
- panelCaloriesValueText: '0',
- panelCaloriesUnitText: 'kcal',
- panelAverageSpeedValueText: '0',
- panelAverageSpeedUnitText: 'km/h',
- panelAccuracyValueText: '--',
- panelAccuracyUnitText: '',
- punchButtonText: '打点',
- punchButtonEnabled: false,
- punchHintText: '等待进入检查点范围',
- punchFeedbackVisible: false,
- punchFeedbackText: '',
- punchFeedbackTone: 'neutral',
- contentCardVisible: false,
- contentCardTitle: '',
- contentCardBody: '',
- punchButtonFxClass: '',
- punchFeedbackFxClass: '',
- contentCardFxClass: '',
- mapPulseVisible: false,
- mapPulseLeftPx: 0,
- mapPulseTopPx: 0,
- mapPulseFxClass: '',
- stageFxVisible: false,
- stageFxClass: '',
- compassTicks: buildCompassTicks(),
- compassLabels: buildCompassLabels(),
- ...buildSideButtonVisibility('left'),
- })
- },
- onReady() {
- stageCanvasAttached = false
- this.measureStageAndCanvas()
- this.loadMapConfigFromRemote()
- },
- onShow() {
- if (mapEngine) {
- mapEngine.handleAppShow()
- }
- },
- onHide() {
- if (mapEngine) {
- mapEngine.handleAppHide()
- }
- },
- onUnload() {
- if (mapEngine) {
- mapEngine.destroy()
- mapEngine = null
- }
- stageCanvasAttached = false
- },
- loadMapConfigFromRemote() {
- const currentEngine = mapEngine
- if (!currentEngine) {
- return
- }
- loadRemoteMapConfig(REMOTE_GAME_CONFIG_URL)
- .then((config) => {
- if (mapEngine !== currentEngine) {
- return
- }
- currentEngine.applyRemoteMapConfig(config)
- })
- .catch((error) => {
- if (mapEngine !== currentEngine) {
- return
- }
- const errorMessage = error && error.message ? error.message : '未知错误'
- this.setData({
- configStatusText: `载入失败: ${errorMessage}`,
- statusText: `远程地图配置载入失败: ${errorMessage} (${INTERNAL_BUILD_VERSION})`,
- })
- })
- },
- measureStageAndCanvas() {
- const page = this
- const applyStage = (rawRect?: Partial<WechatMiniprogram.BoundingClientRectCallbackResult>) => {
- const fallbackRect = getFallbackStageRect()
- const rect: MapEngineStageRect = {
- width: rawRect && typeof rawRect.width === 'number' ? rawRect.width : fallbackRect.width,
- height: rawRect && typeof rawRect.height === 'number' ? rawRect.height : fallbackRect.height,
- left: rawRect && typeof rawRect.left === 'number' ? rawRect.left : fallbackRect.left,
- top: rawRect && typeof rawRect.top === 'number' ? rawRect.top : fallbackRect.top,
- }
- const currentEngine = mapEngine
- if (!currentEngine) {
- return
- }
- currentEngine.setStage(rect)
- if (stageCanvasAttached) {
- return
- }
- const canvasQuery = wx.createSelectorQuery().in(page)
- canvasQuery.select('#mapCanvas').fields({ node: true, size: true })
- canvasQuery.select('#routeLabelCanvas').fields({ node: true, size: true })
- canvasQuery.exec((canvasRes) => {
- const canvasRef = canvasRes[0] as any
- const labelCanvasRef = canvasRes[1] as any
- if (!canvasRef || !canvasRef.node) {
- page.setData({
- statusText: `WebGL 引擎初始化失败 (${INTERNAL_BUILD_VERSION})`,
- })
- return
- }
- const dpr = wx.getSystemInfoSync().pixelRatio || 1
- try {
- currentEngine.attachCanvas(
- canvasRef.node,
- rect.width,
- rect.height,
- dpr,
- labelCanvasRef && labelCanvasRef.node ? labelCanvasRef.node : undefined,
- )
- stageCanvasAttached = true
- } catch (error) {
- page.setData({
- statusText: `WebGL 鍒濆鍖栧け璐?(${INTERNAL_BUILD_VERSION})`,
- })
- }
- })
- }
- const query = wx.createSelectorQuery().in(page)
- query.select('.map-stage').boundingClientRect()
- query.exec((res) => {
- const rect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult | undefined
- applyStage(rect)
- })
- },
- handleTouchStart(event: WechatMiniprogram.TouchEvent) {
- if (mapEngine) {
- mapEngine.handleTouchStart(event)
- }
- },
- handleTouchMove(event: WechatMiniprogram.TouchEvent) {
- if (mapEngine) {
- mapEngine.handleTouchMove(event)
- }
- },
- handleTouchEnd(event: WechatMiniprogram.TouchEvent) {
- if (mapEngine) {
- mapEngine.handleTouchEnd(event)
- }
- },
- handleTouchCancel() {
- if (mapEngine) {
- mapEngine.handleTouchCancel()
- }
- },
- handleRecenter() {
- if (mapEngine) {
- mapEngine.handleRecenter()
- }
- },
- handleRotateStep() {
- if (mapEngine) {
- mapEngine.handleRotateStep()
- }
- },
- handleRotationReset() {
- if (mapEngine) {
- mapEngine.handleRotationReset()
- }
- },
- handleSetManualMode() {
- if (mapEngine) {
- mapEngine.handleSetManualMode()
- }
- },
- handleSetNorthUpMode() {
- if (mapEngine) {
- mapEngine.handleSetNorthUpMode()
- }
- },
- handleSetHeadingUpMode() {
- if (mapEngine) {
- mapEngine.handleSetHeadingUpMode()
- }
- },
- handleCycleNorthReferenceMode() {
- if (mapEngine) {
- mapEngine.handleCycleNorthReferenceMode()
- }
- },
- handleAutoRotateCalibrate() {
- if (mapEngine) {
- mapEngine.handleAutoRotateCalibrate()
- }
- },
- handleToggleGpsTracking() {
- if (mapEngine) {
- mapEngine.handleToggleGpsTracking()
- }
- },
- handleSetRealLocationMode() {
- if (mapEngine) {
- mapEngine.handleSetRealLocationMode()
- }
- },
- handleSetMockLocationMode() {
- if (mapEngine) {
- mapEngine.handleSetMockLocationMode()
- }
- },
- handleConnectMockLocationBridge() {
- if (mapEngine) {
- mapEngine.handleConnectMockLocationBridge()
- }
- },
- handleMockBridgeUrlInput(event: WechatMiniprogram.Input) {
- this.setData({
- mockBridgeUrlDraft: event.detail.value,
- })
- },
- handleSaveMockBridgeUrl() {
- if (mapEngine) {
- mapEngine.handleSetMockLocationBridgeUrl(this.data.mockBridgeUrlDraft)
- }
- },
- handleDisconnectMockLocationBridge() {
- if (mapEngine) {
- mapEngine.handleDisconnectMockLocationBridge()
- }
- },
- handleSetRealHeartRateMode() {
- if (mapEngine) {
- mapEngine.handleSetRealHeartRateMode()
- }
- },
- handleSetMockHeartRateMode() {
- if (mapEngine) {
- mapEngine.handleSetMockHeartRateMode()
- }
- },
- handleMockHeartRateBridgeUrlInput(event: WechatMiniprogram.Input) {
- this.setData({
- mockHeartRateBridgeUrlDraft: event.detail.value,
- })
- },
- handleSaveMockHeartRateBridgeUrl() {
- if (mapEngine) {
- mapEngine.handleSetMockHeartRateBridgeUrl(this.data.mockHeartRateBridgeUrlDraft)
- }
- },
- handleConnectMockHeartRateBridge() {
- if (mapEngine) {
- mapEngine.handleConnectMockHeartRateBridge()
- }
- },
- handleDisconnectMockHeartRateBridge() {
- if (mapEngine) {
- mapEngine.handleDisconnectMockHeartRateBridge()
- }
- },
- handleConnectHeartRate() {
- if (mapEngine) {
- mapEngine.handleConnectHeartRate()
- }
- },
- handleDisconnectHeartRate() {
- if (mapEngine) {
- mapEngine.handleDisconnectHeartRate()
- }
- },
- handleConnectHeartRateDevice(event: WechatMiniprogram.BaseEvent<{ deviceId?: string }>) {
- if (mapEngine && event.currentTarget && event.currentTarget.dataset && event.currentTarget.dataset.deviceId) {
- mapEngine.handleConnectHeartRateDevice(event.currentTarget.dataset.deviceId)
- }
- },
- handleClearPreferredHeartRateDevice() {
- if (mapEngine) {
- mapEngine.handleClearPreferredHeartRateDevice()
- }
- },
- handleDebugHeartRateBlue() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('blue')
- }
- },
- handleDebugHeartRatePurple() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('purple')
- }
- },
- handleDebugHeartRateGreen() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('green')
- }
- },
- handleDebugHeartRateYellow() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('yellow')
- }
- },
- handleDebugHeartRateOrange() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('orange')
- }
- },
- handleDebugHeartRateRed() {
- if (mapEngine) {
- mapEngine.handleDebugHeartRateTone('red')
- }
- },
- handleClearDebugHeartRate() {
- if (mapEngine) {
- mapEngine.handleClearDebugHeartRate()
- }
- },
- handleToggleOsmReference() {
- if (mapEngine) {
- mapEngine.handleToggleOsmReference()
- }
- },
- handleStartGame() {
- if (mapEngine) {
- mapEngine.handleStartGame()
- }
- },
- handleSetClassicMode() {
- if (mapEngine) {
- mapEngine.handleSetGameMode('classic-sequential')
- }
- },
- handleSetScoreOMode() {
- if (mapEngine) {
- mapEngine.handleSetGameMode('score-o')
- }
- },
- handleClearMapTestArtifacts() {
- if (mapEngine) {
- mapEngine.handleClearMapTestArtifacts()
- }
- },
- handleOverlayTouch() {},
- handlePunchAction() {
- if (!this.data.punchButtonEnabled) {
- return
- }
- if (mapEngine) {
- mapEngine.handlePunchAction()
- }
- },
- handleCloseContentCard() {
- if (mapEngine) {
- mapEngine.closeContentCard()
- }
- },
- handleHudPanelChange(event: WechatMiniprogram.CustomEvent<{ current: number }>) {
- this.setData({
- hudPanelIndex: event.detail.current || 0,
- })
- },
- handleCycleSideButtons() {
- this.setData(buildSideButtonVisibility(getNextSideButtonMode(this.data.sideButtonMode)))
- },
- handleToggleMapRotateMode() {
- if (!mapEngine) {
- return
- }
- if (this.data.orientationMode === 'heading-up') {
- mapEngine.handleSetManualMode()
- return
- }
- mapEngine.handleSetHeadingUpMode()
- },
- handleToggleDebugPanel() {
- this.setData({
- showDebugPanel: !this.data.showDebugPanel,
- })
- },
- handleCloseDebugPanel() {
- this.setData({
- showDebugPanel: false,
- })
- },
- handleDebugPanelTap() {},
- })
|