map.ts 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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 MapPageData = MapEngineViewState & {
  16. showDebugPanel: boolean
  17. statusBarHeight: number
  18. topInsetHeight: number
  19. panelTimerText: string
  20. panelMileageText: string
  21. panelDistanceValueText: string
  22. panelProgressText: string
  23. panelSpeedValueText: string
  24. compassTicks: CompassTickData[]
  25. compassLabels: CompassLabelData[]
  26. }
  27. const INTERNAL_BUILD_VERSION = 'map-build-106'
  28. const REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/wxmini/test/game.json'
  29. let mapEngine: MapEngine | null = null
  30. function buildCompassTicks(): CompassTickData[] {
  31. const ticks: CompassTickData[] = []
  32. for (let angle = 0; angle < 360; angle += 5) {
  33. ticks.push({
  34. angle,
  35. long: angle % 15 === 0,
  36. major: angle % 45 === 0,
  37. })
  38. }
  39. return ticks
  40. }
  41. function buildCompassLabels(): CompassLabelData[] {
  42. return [
  43. { text: '\u5317', angle: 0, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal compass-widget__mark--north' },
  44. { text: '\u4e1c\u5317', angle: 45, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northeast' },
  45. { text: '\u4e1c', angle: 90, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  46. { text: '\u4e1c\u5357', angle: 135, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
  47. { text: '\u5357', angle: 180, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  48. { text: '\u897f\u5357', angle: 225, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate' },
  49. { text: '\u897f', angle: 270, rotateBack: 0, radius: 68, className: 'compass-widget__mark--cardinal' },
  50. { text: '\u897f\u5317', angle: 315, rotateBack: 0, radius: 58, className: 'compass-widget__mark--intermediate compass-widget__mark--northwest' },
  51. ]
  52. }
  53. function getFallbackStageRect(): MapEngineStageRect {
  54. const systemInfo = wx.getSystemInfoSync()
  55. const width = Math.max(320, systemInfo.windowWidth)
  56. const height = Math.max(280, systemInfo.windowHeight)
  57. return {
  58. width,
  59. height,
  60. left: 0,
  61. top: 0,
  62. }
  63. }
  64. Page({
  65. data: {
  66. showDebugPanel: false,
  67. statusBarHeight: 0,
  68. topInsetHeight: 12,
  69. panelTimerText: '00:00:00',
  70. panelMileageText: '0m',
  71. panelDistanceValueText: '108',
  72. panelProgressText: '0/14',
  73. panelSpeedValueText: '0',
  74. compassTicks: buildCompassTicks(),
  75. compassLabels: buildCompassLabels(),
  76. } as MapPageData,
  77. onLoad() {
  78. const systemInfo = wx.getSystemInfoSync()
  79. const statusBarHeight = systemInfo.statusBarHeight || 0
  80. const menuButtonRect = wx.getMenuButtonBoundingClientRect()
  81. const menuButtonBottom = menuButtonRect && typeof menuButtonRect.bottom === 'number' ? menuButtonRect.bottom : statusBarHeight
  82. mapEngine = new MapEngine(INTERNAL_BUILD_VERSION, {
  83. onData: (patch) => {
  84. this.setData(patch)
  85. },
  86. })
  87. this.setData({
  88. ...mapEngine.getInitialData(),
  89. showDebugPanel: false,
  90. statusBarHeight,
  91. topInsetHeight: Math.max(statusBarHeight + 12, menuButtonBottom + 20),
  92. panelTimerText: '00:00:00',
  93. panelMileageText: '0m',
  94. panelDistanceValueText: '108',
  95. panelProgressText: '0/14',
  96. panelSpeedValueText: '0',
  97. compassTicks: buildCompassTicks(),
  98. compassLabels: buildCompassLabels(),
  99. })
  100. },
  101. onReady() {
  102. this.measureStageAndCanvas()
  103. this.loadMapConfigFromRemote()
  104. },
  105. onUnload() {
  106. if (mapEngine) {
  107. mapEngine.destroy()
  108. mapEngine = null
  109. }
  110. },
  111. loadMapConfigFromRemote() {
  112. const currentEngine = mapEngine
  113. if (!currentEngine) {
  114. return
  115. }
  116. loadRemoteMapConfig(REMOTE_GAME_CONFIG_URL)
  117. .then((config) => {
  118. if (mapEngine !== currentEngine) {
  119. return
  120. }
  121. currentEngine.applyRemoteMapConfig(config)
  122. })
  123. .catch((error) => {
  124. if (mapEngine !== currentEngine) {
  125. return
  126. }
  127. const errorMessage = error && error.message ? error.message : '鏈煡閿欒'
  128. this.setData({
  129. configStatusText: `杞藉叆澶辫触: ${errorMessage}`,
  130. statusText: `杩滅▼鍦板浘閰嶇疆杞藉叆澶辫触: ${errorMessage} (${INTERNAL_BUILD_VERSION})`,
  131. })
  132. })
  133. },
  134. measureStageAndCanvas() {
  135. const page = this
  136. const applyStage = (rawRect?: Partial<WechatMiniprogram.BoundingClientRectCallbackResult>) => {
  137. const fallbackRect = getFallbackStageRect()
  138. const rect: MapEngineStageRect = {
  139. width: rawRect && typeof rawRect.width === 'number' ? rawRect.width : fallbackRect.width,
  140. height: rawRect && typeof rawRect.height === 'number' ? rawRect.height : fallbackRect.height,
  141. left: rawRect && typeof rawRect.left === 'number' ? rawRect.left : fallbackRect.left,
  142. top: rawRect && typeof rawRect.top === 'number' ? rawRect.top : fallbackRect.top,
  143. }
  144. const currentEngine = mapEngine
  145. if (!currentEngine) {
  146. return
  147. }
  148. currentEngine.setStage(rect)
  149. const canvasQuery = wx.createSelectorQuery().in(page)
  150. canvasQuery.select('#mapCanvas').fields({ node: true, size: true })
  151. canvasQuery.exec((canvasRes) => {
  152. const canvasRef = canvasRes[0] as any
  153. if (!canvasRef || !canvasRef.node) {
  154. page.setData({
  155. statusText: `WebGL 寮曟搸鍒濆鍖栧け璐?(${INTERNAL_BUILD_VERSION})`,
  156. })
  157. return
  158. }
  159. const dpr = wx.getSystemInfoSync().pixelRatio || 1
  160. try {
  161. currentEngine.attachCanvas(canvasRef.node, rect.width, rect.height, dpr)
  162. } catch (error) {
  163. page.setData({
  164. statusText: `WebGL 鍒濆鍖栧け璐?(${INTERNAL_BUILD_VERSION})`,
  165. })
  166. }
  167. })
  168. }
  169. const query = wx.createSelectorQuery().in(page)
  170. query.select('.map-stage').boundingClientRect()
  171. query.exec((res) => {
  172. const rect = res[0] as WechatMiniprogram.BoundingClientRectCallbackResult | undefined
  173. applyStage(rect)
  174. })
  175. },
  176. handleTouchStart(event: WechatMiniprogram.TouchEvent) {
  177. if (mapEngine) {
  178. mapEngine.handleTouchStart(event)
  179. }
  180. },
  181. handleTouchMove(event: WechatMiniprogram.TouchEvent) {
  182. if (mapEngine) {
  183. mapEngine.handleTouchMove(event)
  184. }
  185. },
  186. handleTouchEnd(event: WechatMiniprogram.TouchEvent) {
  187. if (mapEngine) {
  188. mapEngine.handleTouchEnd(event)
  189. }
  190. },
  191. handleTouchCancel() {
  192. if (mapEngine) {
  193. mapEngine.handleTouchCancel()
  194. }
  195. },
  196. handleRecenter() {
  197. if (mapEngine) {
  198. mapEngine.handleRecenter()
  199. }
  200. },
  201. handleRotateStep() {
  202. if (mapEngine) {
  203. mapEngine.handleRotateStep()
  204. }
  205. },
  206. handleRotationReset() {
  207. if (mapEngine) {
  208. mapEngine.handleRotationReset()
  209. }
  210. },
  211. handleSetManualMode() {
  212. if (mapEngine) {
  213. mapEngine.handleSetManualMode()
  214. }
  215. },
  216. handleSetNorthUpMode() {
  217. if (mapEngine) {
  218. mapEngine.handleSetNorthUpMode()
  219. }
  220. },
  221. handleSetHeadingUpMode() {
  222. if (mapEngine) {
  223. mapEngine.handleSetHeadingUpMode()
  224. }
  225. },
  226. handleCycleNorthReferenceMode() {
  227. if (mapEngine) {
  228. mapEngine.handleCycleNorthReferenceMode()
  229. }
  230. },
  231. handleAutoRotateCalibrate() {
  232. if (mapEngine) {
  233. mapEngine.handleAutoRotateCalibrate()
  234. }
  235. },
  236. handleToggleGpsTracking() {
  237. if (mapEngine) {
  238. mapEngine.handleToggleGpsTracking()
  239. }
  240. },
  241. handleToggleOsmReference() {
  242. if (mapEngine) {
  243. mapEngine.handleToggleOsmReference()
  244. }
  245. },
  246. handleToggleDebugPanel() {
  247. this.setData({
  248. showDebugPanel: !this.data.showDebugPanel,
  249. })
  250. },
  251. handleCloseDebugPanel() {
  252. this.setData({
  253. showDebugPanel: false,
  254. })
  255. },
  256. handleDebugPanelTap() {},
  257. })