map.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. import { MapEngine, type MapEngineStageRect, type MapEngineViewState } from '../../engine/map/mapEngine'
  2. import { loadRemoteMapConfig } from '../../utils/remoteMapConfig'
  3. type CompassTickData = {
  4. angle: number
  5. long: boolean
  6. major: boolean
  7. }
  8. type CompassLabelData = {
  9. text: string
  10. angle: number
  11. rotateBack: number
  12. radius: number
  13. className: string
  14. }
  15. type SideButtonMode = 'all' | 'left' | 'right' | 'hidden'
  16. type MapPageData = MapEngineViewState & {
  17. showDebugPanel: boolean
  18. statusBarHeight: number
  19. topInsetHeight: number
  20. hudPanelIndex: number
  21. mockBridgeUrlDraft: string
  22. mockHeartRateBridgeUrlDraft: string
  23. panelTimerText: string
  24. panelMileageText: string
  25. panelDistanceValueText: string
  26. panelProgressText: string
  27. panelSpeedValueText: string
  28. compassTicks: CompassTickData[]
  29. compassLabels: CompassLabelData[]
  30. sideButtonMode: SideButtonMode
  31. showLeftButtonGroup: boolean
  32. showRightButtonGroups: boolean
  33. showBottomDebugButton: boolean
  34. }
  35. const INTERNAL_BUILD_VERSION = 'map-build-196'
  36. const REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
  37. let mapEngine: MapEngine | null = null
  38. let stageCanvasAttached = false
  39. function buildSideButtonVisibility(mode: SideButtonMode) {
  40. return {
  41. sideButtonMode: mode,
  42. showLeftButtonGroup: mode === 'all' || mode === 'left' || mode === 'right',
  43. showRightButtonGroups: mode === 'all' || mode === 'right',
  44. showBottomDebugButton: mode !== 'hidden',
  45. }
  46. }
  47. function getNextSideButtonMode(currentMode: SideButtonMode): SideButtonMode {
  48. if (currentMode === 'all') {
  49. return 'left'
  50. }
  51. if (currentMode === 'left') {
  52. return 'right'
  53. }
  54. if (currentMode === 'right') {
  55. return 'hidden'
  56. }
  57. return 'left'
  58. }
  59. function buildCompassTicks(): CompassTickData[] {
  60. const ticks: CompassTickData[] = []
  61. for (let angle = 0; angle < 360; angle += 5) {
  62. ticks.push({
  63. angle,
  64. long: angle % 15 === 0,
  65. major: angle % 45 === 0,
  66. })
  67. }
  68. return ticks
  69. }
  70. function buildCompassLabels(): CompassLabelData[] {
  71. return [
  72. { text: '\u5317', angle: 0, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal compass-widget__mark--north' },
  73. { text: '\u4e1c\u5317', angle: 45, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northeast' },
  74. { text: '\u4e1c', angle: 90, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  75. { text: '\u4e1c\u5357', angle: 135, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
  76. { text: '\u5357', angle: 180, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  77. { text: '\u897f\u5357', angle: 225, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
  78. { text: '\u897f', angle: 270, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  79. { text: '\u897f\u5317', angle: 315, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northwest' },
  80. ]
  81. }
  82. function getFallbackStageRect(): MapEngineStageRect {
  83. const systemInfo = wx.getSystemInfoSync()
  84. const width = Math.max(320, systemInfo.windowWidth)
  85. const height = Math.max(280, systemInfo.windowHeight)
  86. return {
  87. width,
  88. height,
  89. left: 0,
  90. top: 0,
  91. }
  92. }
  93. Page({
  94. data: {
  95. showDebugPanel: false,
  96. statusBarHeight: 0,
  97. topInsetHeight: 12,
  98. hudPanelIndex: 0,
  99. panelTimerText: '00:00:00',
  100. panelMileageText: '0m',
  101. panelActionTagText: '目标',
  102. panelDistanceTagText: '点距',
  103. panelDistanceValueText: '--',
  104. panelDistanceUnitText: '',
  105. panelProgressText: '0/0',
  106. gameSessionStatus: 'idle',
  107. gameModeText: '顺序赛',
  108. locationSourceMode: 'real',
  109. locationSourceText: '真实定位',
  110. mockBridgeConnected: false,
  111. mockBridgeStatusText: '未连接',
  112. mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
  113. mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
  114. mockCoordText: '--',
  115. mockSpeedText: '--',
  116. heartRateSourceMode: 'real',
  117. heartRateSourceText: '真实心率',
  118. mockHeartRateBridgeConnected: false,
  119. mockHeartRateBridgeStatusText: '未连接',
  120. mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
  121. mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
  122. mockHeartRateText: '--',
  123. heartRateScanText: '未扫描',
  124. heartRateDiscoveredDevices: [],
  125. panelSpeedValueText: '0',
  126. panelTelemetryTone: 'blue',
  127. panelHeartRateZoneNameText: '--',
  128. panelHeartRateZoneRangeText: '',
  129. heartRateConnected: false,
  130. heartRateStatusText: '心率带未连接',
  131. heartRateDeviceText: '--',
  132. panelHeartRateValueText: '--',
  133. panelHeartRateUnitText: '',
  134. panelCaloriesValueText: '0',
  135. panelCaloriesUnitText: 'kcal',
  136. panelAverageSpeedValueText: '0',
  137. panelAverageSpeedUnitText: 'km/h',
  138. panelAccuracyValueText: '--',
  139. panelAccuracyUnitText: '',
  140. punchButtonText: '打点',
  141. punchButtonEnabled: false,
  142. punchHintText: '等待进入检查点范围',
  143. punchFeedbackVisible: false,
  144. punchFeedbackText: '',
  145. punchFeedbackTone: 'neutral',
  146. contentCardVisible: false,
  147. contentCardTitle: '',
  148. contentCardBody: '',
  149. punchButtonFxClass: '',
  150. punchFeedbackFxClass: '',
  151. contentCardFxClass: '',
  152. mapPulseVisible: false,
  153. mapPulseLeftPx: 0,
  154. mapPulseTopPx: 0,
  155. mapPulseFxClass: '',
  156. stageFxVisible: false,
  157. stageFxClass: '',
  158. compassTicks: buildCompassTicks(),
  159. compassLabels: buildCompassLabels(),
  160. ...buildSideButtonVisibility('left'),
  161. } as unknown as MapPageData,
  162. onLoad() {
  163. const systemInfo = wx.getSystemInfoSync()
  164. const statusBarHeight = systemInfo.statusBarHeight || 0
  165. const menuButtonRect = wx.getMenuButtonBoundingClientRect()
  166. const menuButtonBottom = menuButtonRect && typeof menuButtonRect.bottom === 'number' ? menuButtonRect.bottom : statusBarHeight
  167. mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, {
  168. onData: (patch) => {
  169. const nextPatch = patch as Partial<MapPageData>
  170. const nextData: Partial<MapPageData> = {
  171. ...nextPatch,
  172. }
  173. if (
  174. typeof nextPatch.mockBridgeUrlText === 'string'
  175. && this.data.mockBridgeUrlDraft === this.data.mockBridgeUrlText
  176. ) {
  177. nextData.mockBridgeUrlDraft = nextPatch.mockBridgeUrlText
  178. }
  179. if (
  180. typeof nextPatch.mockHeartRateBridgeUrlText === 'string'
  181. && this.data.mockHeartRateBridgeUrlDraft === this.data.mockHeartRateBridgeUrlText
  182. ) {
  183. nextData.mockHeartRateBridgeUrlDraft = nextPatch.mockHeartRateBridgeUrlText
  184. }
  185. this.setData(nextData)
  186. },
  187. })
  188. this.setData({
  189. ...mapEngine.getInitialData(),
  190. showDebugPanel: false,
  191. statusBarHeight,
  192. topInsetHeight: Math.max(statusBarHeight + 12, menuButtonBottom + 20),
  193. hudPanelIndex: 0,
  194. panelTimerText: '00:00:00',
  195. panelMileageText: '0m',
  196. panelActionTagText: '目标',
  197. panelDistanceTagText: '点距',
  198. panelDistanceValueText: '--',
  199. panelDistanceUnitText: '',
  200. panelProgressText: '0/0',
  201. gameSessionStatus: 'idle',
  202. gameModeText: '顺序赛',
  203. locationSourceMode: 'real',
  204. locationSourceText: '真实定位',
  205. mockBridgeConnected: false,
  206. mockBridgeStatusText: '未连接',
  207. mockBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
  208. mockBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
  209. mockCoordText: '--',
  210. mockSpeedText: '--',
  211. heartRateSourceMode: 'real',
  212. heartRateSourceText: '真实心率',
  213. mockHeartRateBridgeConnected: false,
  214. mockHeartRateBridgeStatusText: '未连接',
  215. mockHeartRateBridgeUrlText: 'wss://gs.gotomars.xyz/mock-gps',
  216. mockHeartRateBridgeUrlDraft: 'wss://gs.gotomars.xyz/mock-gps',
  217. mockHeartRateText: '--',
  218. panelSpeedValueText: '0',
  219. panelTelemetryTone: 'blue',
  220. panelHeartRateZoneNameText: '--',
  221. panelHeartRateZoneRangeText: '',
  222. heartRateConnected: false,
  223. heartRateStatusText: '心率带未连接',
  224. heartRateDeviceText: '--',
  225. panelHeartRateValueText: '--',
  226. panelHeartRateUnitText: '',
  227. panelCaloriesValueText: '0',
  228. panelCaloriesUnitText: 'kcal',
  229. panelAverageSpeedValueText: '0',
  230. panelAverageSpeedUnitText: 'km/h',
  231. panelAccuracyValueText: '--',
  232. panelAccuracyUnitText: '',
  233. punchButtonText: '打点',
  234. punchButtonEnabled: false,
  235. punchHintText: '等待进入检查点范围',
  236. punchFeedbackVisible: false,
  237. punchFeedbackText: '',
  238. punchFeedbackTone: 'neutral',
  239. contentCardVisible: false,
  240. contentCardTitle: '',
  241. contentCardBody: '',
  242. punchButtonFxClass: '',
  243. punchFeedbackFxClass: '',
  244. contentCardFxClass: '',
  245. mapPulseVisible: false,
  246. mapPulseLeftPx: 0,
  247. mapPulseTopPx: 0,
  248. mapPulseFxClass: '',
  249. stageFxVisible: false,
  250. stageFxClass: '',
  251. compassTicks: buildCompassTicks(),
  252. compassLabels: buildCompassLabels(),
  253. ...buildSideButtonVisibility('left'),
  254. })
  255. },
  256. onReady() {
  257. stageCanvasAttached = false
  258. this.measureStageAndCanvas()
  259. this.loadMapConfigFromRemote()
  260. },
  261. onShow() {
  262. if (mapEngine) {
  263. mapEngine.handleAppShow()
  264. }
  265. },
  266. onHide() {
  267. if (mapEngine) {
  268. mapEngine.handleAppHide()
  269. }
  270. },
  271. onUnload() {
  272. if (mapEngine) {
  273. mapEngine.destroy()
  274. mapEngine = null
  275. }
  276. stageCanvasAttached = false
  277. },
  278. loadMapConfigFromRemote() {
  279. const currentEngine = mapEngine
  280. if (!currentEngine) {
  281. return
  282. }
  283. loadRemoteMapConfig(REMOTE_GAME_CONFIG_URL)
  284. .then((config) => {
  285. if (mapEngine !== currentEngine) {
  286. return
  287. }
  288. currentEngine.applyRemoteMapConfig(config)
  289. })
  290. .catch((error) => {
  291. if (mapEngine !== currentEngine) {
  292. return
  293. }
  294. const errorMessage = error && error.message ? error.message : '未知错误'
  295. this.setData({
  296. configStatusText: `载入失败: ${errorMessage}`,
  297. statusText: `远程地图配置载入失败: ${errorMessage} (${INTERNAL_BUILD_VERSION})`,
  298. })
  299. })
  300. },
  301. measureStageAndCanvas() {
  302. const page = this
  303. const applyStage = (rawRect?: Partial<WechatMiniprogram.BoundingClientRectCallbackResult>) => {
  304. const fallbackRect = getFallbackStageRect()
  305. const rect: MapEngineStageRect = {
  306. width: rawRect && typeof rawRect.width === 'number' ? rawRect.width : fallbackRect.width,
  307. height: rawRect && typeof rawRect.height === 'number' ? rawRect.height : fallbackRect.height,
  308. left: rawRect && typeof rawRect.left === 'number' ? rawRect.left : fallbackRect.left,
  309. top: rawRect && typeof rawRect.top === 'number' ? rawRect.top : fallbackRect.top,
  310. }
  311. const currentEngine = mapEngine
  312. if (!currentEngine) {
  313. return
  314. }
  315. currentEngine.setStage(rect)
  316. if (stageCanvasAttached) {
  317. return
  318. }
  319. const canvasQuery = wx.createSelectorQuery().in(page)
  320. canvasQuery.select('#mapCanvas').fields({ node: true, size: true })
  321. canvasQuery.select('#routeLabelCanvas').fields({ node: true, size: true })
  322. canvasQuery.exec((canvasRes) => {
  323. const canvasRef = canvasRes[0] as any
  324. const labelCanvasRef = canvasRes[1] as any
  325. if (!canvasRef || !canvasRef.node) {
  326. page.setData({
  327. statusText: `WebGL 引擎初始化失败 (${INTERNAL_BUILD_VERSION})`,
  328. })
  329. return
  330. }
  331. const dpr = wx.getSystemInfoSync().pixelRatio || 1
  332. try {
  333. currentEngine.attachCanvas(
  334. canvasRef.node,
  335. rect.width,
  336. rect.height,
  337. dpr,
  338. labelCanvasRef && labelCanvasRef.node ? labelCanvasRef.node : undefined,
  339. )
  340. stageCanvasAttached = true
  341. } catch (error) {
  342. page.setData({
  343. statusText: `WebGL 鍒濆鍖栧け璐?(${INTERNAL_BUILD_VERSION})`,
  344. })
  345. }
  346. })
  347. }
  348. const query = wx.createSelectorQuery().in(page)
  349. query.select('.map-stage').boundingClientRect()
  350. query.exec((res) => {
  351. const rect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult | undefined
  352. applyStage(rect)
  353. })
  354. },
  355. handleTouchStart(event: WechatMiniprogram.TouchEvent) {
  356. if (mapEngine) {
  357. mapEngine.handleTouchStart(event)
  358. }
  359. },
  360. handleTouchMove(event: WechatMiniprogram.TouchEvent) {
  361. if (mapEngine) {
  362. mapEngine.handleTouchMove(event)
  363. }
  364. },
  365. handleTouchEnd(event: WechatMiniprogram.TouchEvent) {
  366. if (mapEngine) {
  367. mapEngine.handleTouchEnd(event)
  368. }
  369. },
  370. handleTouchCancel() {
  371. if (mapEngine) {
  372. mapEngine.handleTouchCancel()
  373. }
  374. },
  375. handleRecenter() {
  376. if (mapEngine) {
  377. mapEngine.handleRecenter()
  378. }
  379. },
  380. handleRotateStep() {
  381. if (mapEngine) {
  382. mapEngine.handleRotateStep()
  383. }
  384. },
  385. handleRotationReset() {
  386. if (mapEngine) {
  387. mapEngine.handleRotationReset()
  388. }
  389. },
  390. handleSetManualMode() {
  391. if (mapEngine) {
  392. mapEngine.handleSetManualMode()
  393. }
  394. },
  395. handleSetNorthUpMode() {
  396. if (mapEngine) {
  397. mapEngine.handleSetNorthUpMode()
  398. }
  399. },
  400. handleSetHeadingUpMode() {
  401. if (mapEngine) {
  402. mapEngine.handleSetHeadingUpMode()
  403. }
  404. },
  405. handleCycleNorthReferenceMode() {
  406. if (mapEngine) {
  407. mapEngine.handleCycleNorthReferenceMode()
  408. }
  409. },
  410. handleAutoRotateCalibrate() {
  411. if (mapEngine) {
  412. mapEngine.handleAutoRotateCalibrate()
  413. }
  414. },
  415. handleToggleGpsTracking() {
  416. if (mapEngine) {
  417. mapEngine.handleToggleGpsTracking()
  418. }
  419. },
  420. handleSetRealLocationMode() {
  421. if (mapEngine) {
  422. mapEngine.handleSetRealLocationMode()
  423. }
  424. },
  425. handleSetMockLocationMode() {
  426. if (mapEngine) {
  427. mapEngine.handleSetMockLocationMode()
  428. }
  429. },
  430. handleConnectMockLocationBridge() {
  431. if (mapEngine) {
  432. mapEngine.handleConnectMockLocationBridge()
  433. }
  434. },
  435. handleMockBridgeUrlInput(event: WechatMiniprogram.Input) {
  436. this.setData({
  437. mockBridgeUrlDraft: event.detail.value,
  438. })
  439. },
  440. handleSaveMockBridgeUrl() {
  441. if (mapEngine) {
  442. mapEngine.handleSetMockLocationBridgeUrl(this.data.mockBridgeUrlDraft)
  443. }
  444. },
  445. handleDisconnectMockLocationBridge() {
  446. if (mapEngine) {
  447. mapEngine.handleDisconnectMockLocationBridge()
  448. }
  449. },
  450. handleSetRealHeartRateMode() {
  451. if (mapEngine) {
  452. mapEngine.handleSetRealHeartRateMode()
  453. }
  454. },
  455. handleSetMockHeartRateMode() {
  456. if (mapEngine) {
  457. mapEngine.handleSetMockHeartRateMode()
  458. }
  459. },
  460. handleMockHeartRateBridgeUrlInput(event: WechatMiniprogram.Input) {
  461. this.setData({
  462. mockHeartRateBridgeUrlDraft: event.detail.value,
  463. })
  464. },
  465. handleSaveMockHeartRateBridgeUrl() {
  466. if (mapEngine) {
  467. mapEngine.handleSetMockHeartRateBridgeUrl(this.data.mockHeartRateBridgeUrlDraft)
  468. }
  469. },
  470. handleConnectMockHeartRateBridge() {
  471. if (mapEngine) {
  472. mapEngine.handleConnectMockHeartRateBridge()
  473. }
  474. },
  475. handleDisconnectMockHeartRateBridge() {
  476. if (mapEngine) {
  477. mapEngine.handleDisconnectMockHeartRateBridge()
  478. }
  479. },
  480. handleConnectHeartRate() {
  481. if (mapEngine) {
  482. mapEngine.handleConnectHeartRate()
  483. }
  484. },
  485. handleDisconnectHeartRate() {
  486. if (mapEngine) {
  487. mapEngine.handleDisconnectHeartRate()
  488. }
  489. },
  490. handleConnectHeartRateDevice(event: WechatMiniprogram.BaseEvent<{ deviceId?: string }>) {
  491. if (mapEngine && event.currentTarget && event.currentTarget.dataset && event.currentTarget.dataset.deviceId) {
  492. mapEngine.handleConnectHeartRateDevice(event.currentTarget.dataset.deviceId)
  493. }
  494. },
  495. handleClearPreferredHeartRateDevice() {
  496. if (mapEngine) {
  497. mapEngine.handleClearPreferredHeartRateDevice()
  498. }
  499. },
  500. handleDebugHeartRateBlue() {
  501. if (mapEngine) {
  502. mapEngine.handleDebugHeartRateTone('blue')
  503. }
  504. },
  505. handleDebugHeartRatePurple() {
  506. if (mapEngine) {
  507. mapEngine.handleDebugHeartRateTone('purple')
  508. }
  509. },
  510. handleDebugHeartRateGreen() {
  511. if (mapEngine) {
  512. mapEngine.handleDebugHeartRateTone('green')
  513. }
  514. },
  515. handleDebugHeartRateYellow() {
  516. if (mapEngine) {
  517. mapEngine.handleDebugHeartRateTone('yellow')
  518. }
  519. },
  520. handleDebugHeartRateOrange() {
  521. if (mapEngine) {
  522. mapEngine.handleDebugHeartRateTone('orange')
  523. }
  524. },
  525. handleDebugHeartRateRed() {
  526. if (mapEngine) {
  527. mapEngine.handleDebugHeartRateTone('red')
  528. }
  529. },
  530. handleClearDebugHeartRate() {
  531. if (mapEngine) {
  532. mapEngine.handleClearDebugHeartRate()
  533. }
  534. },
  535. handleToggleOsmReference() {
  536. if (mapEngine) {
  537. mapEngine.handleToggleOsmReference()
  538. }
  539. },
  540. handleStartGame() {
  541. if (mapEngine) {
  542. mapEngine.handleStartGame()
  543. }
  544. },
  545. handleSetClassicMode() {
  546. if (mapEngine) {
  547. mapEngine.handleSetGameMode('classic-sequential')
  548. }
  549. },
  550. handleSetScoreOMode() {
  551. if (mapEngine) {
  552. mapEngine.handleSetGameMode('score-o')
  553. }
  554. },
  555. handleClearMapTestArtifacts() {
  556. if (mapEngine) {
  557. mapEngine.handleClearMapTestArtifacts()
  558. }
  559. },
  560. handleOverlayTouch() {},
  561. handlePunchAction() {
  562. if (!this.data.punchButtonEnabled) {
  563. return
  564. }
  565. if (mapEngine) {
  566. mapEngine.handlePunchAction()
  567. }
  568. },
  569. handleCloseContentCard() {
  570. if (mapEngine) {
  571. mapEngine.closeContentCard()
  572. }
  573. },
  574. handleHudPanelChange(event: WechatMiniprogram.CustomEvent<{ current: number }>) {
  575. this.setData({
  576. hudPanelIndex: event.detail.current || 0,
  577. })
  578. },
  579. handleCycleSideButtons() {
  580. this.setData(buildSideButtonVisibility(getNextSideButtonMode(this.data.sideButtonMode)))
  581. },
  582. handleToggleMapRotateMode() {
  583. if (!mapEngine) {
  584. return
  585. }
  586. if (this.data.orientationMode === 'heading-up') {
  587. mapEngine.handleSetManualMode()
  588. return
  589. }
  590. mapEngine.handleSetHeadingUpMode()
  591. },
  592. handleToggleDebugPanel() {
  593. this.setData({
  594. showDebugPanel: !this.data.showDebugPanel,
  595. })
  596. },
  597. handleCloseDebugPanel() {
  598. this.setData({
  599. showDebugPanel: false,
  600. })
  601. },
  602. handleDebugPanelTap() {},
  603. })