player.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. // import L from 'leaflet'
  2. import config from '@/utils/map/sub/config'
  3. import global from '@/utils/map/sub/global'
  4. import tools from '@/utils/tools'
  5. import store from '@/store/index'
  6. import {
  7. formatTime
  8. } from "@/utils/util.js"
  9. // var interval_creatCircleMarker = null
  10. // var interval_showTrail = null
  11. var togglePlayerFlag = store.state.mapControlPlayer
  12. var toggleTooltipFlag = store.state.mapControlTooltip
  13. var toggleTrailFlag = store.state.mapControlTrail
  14. export default {
  15. playerLayerGroup: null, // 玩家标记图层组
  16. trailLayerGroup: null, // 玩家轨迹图层组
  17. init() {
  18. this.playerLayerGroup = new L.layerGroup()
  19. this.trailLayerGroup = new L.layerGroup()
  20. },
  21. // 即将开始地图缩放时触发本方法
  22. onZoomStart(e) {
  23. if (togglePlayerFlag) {
  24. this.playerLayerGroup.remove()
  25. }
  26. if (toggleTrailFlag) {
  27. this.trailLayerGroup.remove()
  28. }
  29. },
  30. // 地图发生缩放时触发本方法
  31. onZoom(e) {},
  32. // 地图缩放结束时触发本方法
  33. onZoomEnd(e) {
  34. if (toggleTrailFlag) {
  35. this.trailLayerGroup.addTo(global.map)
  36. }
  37. if (togglePlayerFlag) {
  38. this.playerLayerGroup.addTo(global.map)
  39. this.toggleTooltip(store.state.mapControlTooltip)
  40. }
  41. },
  42. togglePlayer(flag) {
  43. if (flag != null) {
  44. togglePlayerFlag = !flag
  45. }
  46. console.log("[togglePlayer]", togglePlayerFlag)
  47. if (togglePlayerFlag) {
  48. this.playerLayerGroup.removeFrom(global.map)
  49. } else {
  50. this.playerLayerGroup.addTo(global.map)
  51. }
  52. togglePlayerFlag = !togglePlayerFlag
  53. store.commit('setMapControlPlayer', togglePlayerFlag)
  54. },
  55. toggleTooltip(flag) {
  56. if (flag != null) {
  57. toggleTooltipFlag = !flag
  58. } else {
  59. // if (!togglePlayerFlag) {
  60. // this.togglePlayer(true)
  61. // toggleTooltipFlag = false
  62. // }
  63. }
  64. for (let i = 0; i < global.storePlayers.length; i++) {
  65. var player = global.storePlayers[i]
  66. if (player != null && player.marker != null) {
  67. // player.marker.toggleTooltip()
  68. // let isopen = player.marker.isTooltipOpen()
  69. if (toggleTooltipFlag) {
  70. player.marker.closeTooltip()
  71. } else {
  72. player.marker.openTooltip()
  73. }
  74. }
  75. }
  76. toggleTooltipFlag = !toggleTooltipFlag
  77. store.commit('setMapControlTooltip', toggleTooltipFlag)
  78. },
  79. toggleTrail(flag) {
  80. if (flag != null) {
  81. toggleTrailFlag = !flag
  82. }
  83. if (toggleTrailFlag) {
  84. this.trailLayerGroup.removeFrom(global.map)
  85. } else {
  86. this.trailLayerGroup.addTo(global.map)
  87. }
  88. toggleTrailFlag = !toggleTrailFlag
  89. store.commit('setMapControlTrail', toggleTrailFlag)
  90. },
  91. drawAllPlayers() {
  92. if (global.players == null) {
  93. console.log('[drawAllPlayers] 玩家列表为空')
  94. return
  95. } else {
  96. console.log('[drawAllPlayers]', global.players)
  97. }
  98. // if (this.playerLayerGroup != null)
  99. // this.playerLayerGroup.clearLayers()
  100. for (let i = 0; i < global.players.length; i++) {
  101. this.drawOnePlayer(global.players[i].id)
  102. // this.drawOnePlayer2(global.players[i].id)
  103. }
  104. },
  105. drawOnePlayer2(playerId, animate = true) {
  106. var that = this
  107. var player = global.getPlayerById(playerId)
  108. var player_position = global.getPlayerPositionById(playerId)
  109. var storePlayer = global.getStorePlayersById(playerId)
  110. // console.log(player, player_position)
  111. if (player == null || player_position == null) {
  112. console.warn('[drawOnePlayer2] 关键数据为空', player, player_position)
  113. return
  114. }
  115. // 首次创建
  116. if (storePlayer == null || storePlayer.marker == null) {
  117. // 在地图上创建 marker 并存储用户信息
  118. var playerIcon = L.icon({
  119. iconUrl: 'static/map/marker-icon.png',
  120. iconSize: [20, 32.8],
  121. iconAnchor: [10, 30]
  122. });
  123. var marker = L.marker([player_position.latitude, player_position.longitude], {
  124. icon: playerIcon
  125. })
  126. .addTo(this.playerLayerGroup)
  127. // .addTo(global.map)
  128. // .bindPopup("Hello, I'm a Marker!<br><img src='my-image.png' width='100'>").openPopup();
  129. .bindTooltip(`${player.name}`, {
  130. permanent: true,
  131. offset: [0, -30],
  132. direction: 'top',
  133. interactive: true
  134. })
  135. marker.type = 'marker'
  136. marker.playerId = player.id // 存储用户信息
  137. marker.animate = animate
  138. marker.on('click', function(e) {
  139. var playerId = e.target.playerId; // 获取点击的 marker 的用户信息
  140. console.log("Selected player ID: " + playerId);
  141. that.handleFocusPlayer(playerId)
  142. });
  143. var tooltip = marker.getTooltip()
  144. tooltip.playerId = player.id // 存储用户信息
  145. tooltip.animate = animate
  146. tooltip.on('click', function(e) {
  147. // console.log("[Marker.Tooltip]", e)
  148. var playerId = e.target.playerId; // 获取点击的 marker 的用户信息
  149. console.log("[Marker.Tooltip] Selected player ID: " + playerId)
  150. that.handleFocusPlayer(playerId)
  151. })
  152. // this.playerLayerGroup.addTo(global.map)
  153. if (storePlayer == null) {
  154. storePlayer = {
  155. id: playerId,
  156. marker: marker, // 玩家标识
  157. markerLayerGroup: this.playerLayerGroup,
  158. trail: null, // 玩家轨迹
  159. trailData: [], // 玩家轨迹信息
  160. interval_creatCircleMarker: null,
  161. interval_showTrail: null,
  162. }
  163. global.storePlayers.push(storePlayer)
  164. } else {
  165. storePlayer.marker = marker
  166. storePlayer.markerLayerGroup = this.playerLayerGroup
  167. }
  168. }
  169. // 非首次创建,直接移动位置
  170. else {
  171. if (!toggleTrailFlag) {
  172. var marker = storePlayer.marker
  173. var curPoint = [player_position.latitude, player_position.longitude]
  174. // console.warn('[drawOnePlayer] 更新玩家的标记位置', storePlayer.marker, curPoint)
  175. marker.setLatLng(curPoint)
  176. }
  177. }
  178. },
  179. // 创建动画效果函数,用于实现circleMarker的大小和透明度随时间变化
  180. animateCircle(circle, minRadius, maxRadius, step = 1) {
  181. var radius = circle.getRadius();
  182. var opacity = circle.options.fillOpacity;
  183. var zoomType = 'zoomIn' // zoomIn: 放大 zoomOut: 缩小
  184. var interval_creatCircleMarker = setInterval(function() {
  185. if (!circle.animate) {
  186. clearInterval(interval_creatCircleMarker)
  187. return
  188. }
  189. // 改变圆形半径和透明度
  190. if (zoomType == 'zoomIn') {
  191. radius = radius + step
  192. // opacity = opacity - 0.1
  193. } else if (zoomType == 'zoomOut') {
  194. radius = radius - step
  195. // opacity = opacity - 0.1
  196. }
  197. circle.setRadius(radius);
  198. circle.setStyle({
  199. fillOpacity: opacity
  200. });
  201. if (radius >= maxRadius) {
  202. zoomType = 'zoomOut'
  203. } else if (radius <= minRadius) {
  204. zoomType = 'zoomIn'
  205. }
  206. }, 50);
  207. return interval_creatCircleMarker
  208. },
  209. getPopupContent(player) {
  210. var popupContent = '<div style="line-height: 20px; text-wrap: nowrap;">'
  211. popupContent += '<div><span style="color: #ff8d1a; font-weight: bold;" onClick="uni.makePhoneCall({phoneNumber: \''
  212. + player.phone + '\'});">' + player.name + ' ( ' + player.phone + ' )' + '</span></div>'
  213. // popupContent += ' &nbsp; <span style="font-weight: bold;">=> ' + player.nextControlPoint.sn + '</span>'
  214. popupContent += '<div>百味豆:' + player.sysPoint + ' &nbsp; 下一点位:' + player.nextControlPoint.orderNO
  215. + ' &nbsp; 打点进度:' + player.EffectiveNum + '/' + player.TotalControlNum + '</div>'
  216. popupContent += '<div>距离:' + tools.fmtDistanctKm(player.distance) + 'km &nbsp; 配速:' + tools.convertSecondsToHMS(player.pace, 2) + '</div>'
  217. popupContent += '<div>心率:' + player.lastHr + ' &nbsp; 平均:' + player.avgHr + ' &nbsp; 最大:' + player.maxHr + '</div>'
  218. popupContent += '<div>Cal:' + Math.round(player.Calorie / 1000) + ' &nbsp; Ck:' + player.Ck
  219. + ' &nbsp; Ei:' + Math.round(player.Ei * 100) / 100 + '</div>'
  220. popupContent += '</div>'
  221. return popupContent
  222. },
  223. drawOnePlayer(playerId, animate = true) {
  224. var that = this
  225. var player = global.getPlayerById(playerId)
  226. var player_position = global.getPlayerPositionById(playerId)
  227. var storePlayer = global.getStorePlayersById(playerId)
  228. console.log('[drawOnePlayer]', player, player_position)
  229. if (player == null) {
  230. console.warn('[drawOnePlayer] 玩家数据为空', player)
  231. return
  232. }
  233. if (player_position == null) {
  234. console.log('[drawOnePlayer] 玩家位置数据为空', player_position)
  235. return
  236. }
  237. // var popupContent = this.getPopupContent(player)
  238. // 首次创建
  239. if (storePlayer == null || storePlayer.marker == null) {
  240. // console.log('[drawOnePlayer] storePlayer == null', playerId)
  241. var playerColor = this.getPlayerColor(playerId)
  242. var options = config.gStyle.marker.default
  243. options.fillColor = playerColor
  244. options.stroke = config.gStyle.common.stroke
  245. options.color = config.gStyle.common.color
  246. // console.log('[drawOnePlayer] options', options)
  247. var marker = L.circleMarker([player_position.latitude, player_position.longitude], options)
  248. .addTo(this.playerLayerGroup)
  249. // .bindPopup(popupContent)
  250. .bindTooltip(`${player.name}`, config.gStyle.marker.tooltip)
  251. marker.type = 'circleMarker'
  252. marker.playerId = player.id // 存储用户信息
  253. marker.animate = animate
  254. marker.on('click', function(e) {
  255. // console.log("[Marker]", e)
  256. var playerId = e.target.playerId; // 获取点击的 marker 的用户信息
  257. console.log("[Marker] Selected player ID: " + playerId)
  258. that.handleFocusPlayer(playerId)
  259. })
  260. var tooltip = marker.getTooltip()
  261. tooltip.playerId = player.id // 存储用户信息
  262. tooltip.animate = animate
  263. tooltip.on('click', function(e) {
  264. // console.log("[Marker.Tooltip]", e)
  265. var playerId = e.target.playerId; // 获取点击的 marker 的用户信息
  266. console.log("[Marker.Tooltip] Selected player ID: " + playerId)
  267. that.handleFocusPlayer(playerId)
  268. })
  269. var interval_creatCircleMarker = null
  270. if (animate) {
  271. interval_creatCircleMarker = this.animateCircle(marker, 5, 10, 0.5)
  272. }
  273. // this.playerLayerGroup.addTo(global.map)
  274. if (storePlayer == null) {
  275. storePlayer = {
  276. id: playerId,
  277. marker: marker, // 玩家标识
  278. markerLayerGroup: this.playerLayerGroup,
  279. trail: null, // 玩家轨迹 外层描边轨迹
  280. trail2: null, // 玩家轨迹 内层填充轨迹
  281. trailLayerGroup: null,
  282. trailData: [], // 玩家轨迹信息
  283. interval_creatCircleMarker: interval_creatCircleMarker,
  284. interval_showTrail: null,
  285. }
  286. global.storePlayers.push(storePlayer)
  287. } else {
  288. storePlayer.marker = marker
  289. storePlayer.markerLayerGroup = this.playerLayerGroup
  290. storePlayer.interval_creatCircleMarker = interval_creatCircleMarker
  291. }
  292. }
  293. // 非首次创建,直接移动位置
  294. else {
  295. if (!toggleTrailFlag) {
  296. var marker = storePlayer.marker
  297. var curPoint = [player_position.latitude, player_position.longitude]
  298. // console.log('[drawOnePlayer] 更新玩家的标记位置', storePlayer.marker, curPoint)
  299. marker.setLatLng(curPoint)
  300. marker.bringToFront()
  301. // marker.setPopupContent(popupContent)
  302. }
  303. }
  304. },
  305. handleFocusPlayer(playerId) {
  306. console.log("[handleFocusPlayer] 当前选中玩家ID: " + playerId)
  307. if (global.focusPlayerId > 0) {
  308. // 先把之前选中的目标恢复成默认状态
  309. var unfocusPlayer = global.getStorePlayersById(global.focusPlayerId)
  310. // console.log("unfocusPlayer", unfocusPlayer)
  311. var playerColor = this.getPlayerColor(global.focusPlayerId)
  312. if (unfocusPlayer != null && unfocusPlayer.marker != null && unfocusPlayer.marker.type == 'circleMarker') {
  313. var options = config.gStyle.marker.default
  314. options.fillColor = playerColor
  315. unfocusPlayer.marker.setStyle(options)
  316. }
  317. if (unfocusPlayer != null && unfocusPlayer.trail != null) {
  318. var options = config.gStyle.trail.default
  319. options.color = playerColor
  320. unfocusPlayer.trail2.setStyle(options)
  321. }
  322. }
  323. global.focusPlayerId = playerId
  324. global.getCaller().focusPlayerId = playerId
  325. var focusPlayer = global.getStorePlayersById(global.focusPlayerId)
  326. // console.log("focusPlayer", focusPlayer)
  327. if (focusPlayer != null && focusPlayer.trail != null) {
  328. var options = config.gStyle.trail.focus
  329. focusPlayer.trail2.setStyle(options)
  330. focusPlayer.trail.bringToFront()
  331. focusPlayer.trail2.bringToFront()
  332. }
  333. if (focusPlayer != null && focusPlayer.marker != null && focusPlayer.marker.type == 'circleMarker') {
  334. var options = config.gStyle.marker.focus
  335. options.stroke = config.gStyle.common.stroke
  336. options.color = config.gStyle.common.color
  337. focusPlayer.marker.setStyle(options)
  338. // focusPlayer.marker.openPopup()
  339. focusPlayer.marker.bringToFront()
  340. }
  341. },
  342. drawAllTrails(duration) {
  343. if (global.players == null) {
  344. console.log('[drawAllTrails] 玩家列表为空')
  345. return
  346. }
  347. // if (this.trailLayerGroup != null)
  348. // this.trailLayerGroup.clearLayers()
  349. for (let i = 0; i < global.players.length; i++) {
  350. this.drawOneTrail(global.players[i].id, duration)
  351. }
  352. },
  353. // 显示运动轨迹 duration:毫秒
  354. drawOneTrail(playerId, duration) {
  355. var player = global.getPlayerById(playerId)
  356. var player_position = global.getPlayerPositionById(playerId)
  357. var storePlayer = global.getStorePlayersById(playerId)
  358. console.log('[drawOneTrail] param', player, player_position, storePlayer)
  359. if (player == null) {
  360. console.warn('[drawOneTrail] 玩家数据为空', player)
  361. return
  362. }
  363. if (player_position == null) {
  364. console.log('[drawOneTrail] 玩家位置数据为空', player_position)
  365. return
  366. }
  367. if (storePlayer == null || storePlayer.trail == null) {
  368. console.warn('[drawOneTrail] 轨迹数据为空', player, storePlayer)
  369. var trail = null
  370. var trail2 = null
  371. var curPoint = [player_position.latitude, player_position.longitude, +new Date()]
  372. // console.log('[drawOneTrail] curPoint', curPoint)
  373. var trailData = null
  374. var options = config.gStyle.trail.default
  375. // 获取Vuex中暂存的用户数据,用于页面重载后的数据恢复
  376. var holdPlayerData = global.fetchPlayersDataById(playerId)
  377. // console.warn("[drawOneTrail] holdPlayerData", holdPlayerData)
  378. if (holdPlayerData != null && holdPlayerData.trailData != null && holdPlayerData.trailData.length > 0) {
  379. // console.warn("[drawOneTrail] 加载暂存轨迹数据", holdPlayerData.trailData)
  380. // concat方法连接a、b两个数组后,a、b两个数组的数据不变,同时会返回一个新的数组
  381. trailData = holdPlayerData.trailData.concat([curPoint])
  382. // console.warn("[drawOneTrail] 轨迹数据", trailData)
  383. } else {
  384. trailData = [curPoint]
  385. }
  386. // 外层描边轨迹
  387. if (config.gStyle.common.stroke) {
  388. options.weight = 6.9
  389. } else {
  390. options.weight = 0
  391. }
  392. options.color = config.gStyle.common.color
  393. trail = L.polyline(trailData, options)
  394. .addTo(this.trailLayerGroup)
  395. // console.log('[drawOneTrail] trail', trail)
  396. // 内层填充轨迹
  397. var playerColor = this.getPlayerColor(playerId)
  398. options.weight = 3.9
  399. options.color = playerColor
  400. // options.fillColor = playerColor
  401. trail2 = L.polyline(trailData, options)
  402. .addTo(this.trailLayerGroup)
  403. if (storePlayer == null) {
  404. storePlayer = {
  405. id: playerId,
  406. marker: null, // 玩家标识
  407. markerLayerGroup: null,
  408. trail: trail, // 玩家轨迹 外层描边轨迹
  409. trail2: trail2, // 玩家轨迹 内层填充轨迹
  410. trailLayerGroup: this.trailLayerGroup,
  411. trailData: trailData, // 玩家轨迹信息
  412. interval_creatCircleMarker: null,
  413. interval_showTrail: null,
  414. }
  415. global.storePlayers.push(storePlayer)
  416. } else {
  417. storePlayer.trail = trail
  418. storePlayer.trail2 = trail2
  419. storePlayer.trailLayerGroup = this.trailLayerGroup
  420. storePlayer.trailData = trailData
  421. }
  422. var that = this
  423. var interval_showTrail = setInterval(function() {
  424. player = global.getPlayerById(playerId)
  425. if (player == null) {
  426. console.warn('[drawOneTrail] 玩家数据为空 (已完赛或已退赛)', player)
  427. clearInterval(interval_showTrail)
  428. storePlayer.interval_showTrail = null
  429. return
  430. }
  431. // 去除过期的历史轨迹
  432. if (duration > 0) {
  433. var now = +new Date()
  434. storePlayer.trailData = storePlayer.trailData.filter(function(point) {
  435. return (now - point[2]) < duration
  436. });
  437. }
  438. player_position = global.getPlayerPositionById(playerId)
  439. if (player_position == null || player_position.latitude == null || player_position.longitude == null) {
  440. console.warn('[drawOneTrail] 玩家位置信息为空 playerId:' + playerId)
  441. return
  442. }
  443. curPoint = [player_position.latitude, player_position.longitude, +new Date()]
  444. storePlayer.trailData.push(curPoint)
  445. // storePlayer.trailData.push(curPoint.slice(0, 2))
  446. if (toggleTrailFlag) {
  447. // console.log('interval_showTrail', storePlayer.trailData)
  448. trail.setLatLngs(storePlayer.trailData);
  449. trail2.setLatLngs(storePlayer.trailData);
  450. if (storePlayer.marker != null) {
  451. if (toggleTrailFlag) {
  452. // console.warn('[drawOneTrail] 更新玩家的标记位置', storePlayer.marker, curPoint)
  453. storePlayer.marker.setLatLng(curPoint.slice(0, 2)) // 更新玩家的标记位置
  454. storePlayer.marker.bringToFront()
  455. var popupContent = that.getPopupContent(player)
  456. // storePlayer.marker.setPopupContent(popupContent)
  457. }
  458. }
  459. // global.map.setView(point.slice(0, 2), 18); //地图中心跟踪最新位置
  460. }
  461. }, 500);
  462. storePlayer.interval_showTrail = interval_showTrail
  463. } else {
  464. // console.log('[drawOneTrail] 更新玩家轨迹', storePlayer, storePlayer.trail)
  465. }
  466. },
  467. getPlayerColor(playerId) {
  468. var colorIndex = playerId % 10
  469. return config.playerColorList[colorIndex]
  470. },
  471. // // duration: 轨迹保存时间 为0表示保存全部轨迹
  472. // getCurPos(lastpos, duration, player_trail) {
  473. // // console.log(lastpos, pointsT)
  474. // var now = +new Date();
  475. // if (duration > 0) {
  476. // player_trail.points = player_trail.points.filter(function(point) {
  477. // return now - point[2] < duration;
  478. // });
  479. // }
  480. // var step = 0.00001
  481. // var t = 10
  482. // var point = null
  483. // if (player_trail.pointsT < 20 * t)
  484. // point = [lastpos[0] + Math.random() * step, lastpos[1] + Math.random() * step, now];
  485. // else if (player_trail.pointsT >= 20 * t && player_trail.pointsT < 40 * t)
  486. // point = [lastpos[0] - Math.random() * step, lastpos[1] + Math.random() * step, now];
  487. // else if (player_trail.pointsT >= 40 * t && player_trail.pointsT < 60 * t)
  488. // point = [lastpos[0] - Math.random() * step, lastpos[1] - Math.random() * step, now];
  489. // else if (player_trail.pointsT >= 60 * t && player_trail.pointsT < 80 * t)
  490. // point = [lastpos[0] + Math.random() * step, lastpos[1] - Math.random() * step, now];
  491. // // point = [point[0] - Math.random() * 0.00001, point[1] - Math.random() * 0.00001, now];
  492. // player_trail.pointsT++
  493. // if (player_trail.pointsT >= 80 * t)
  494. // player_trail.pointsT = 0
  495. // player_trail.points.push(point);
  496. // return point
  497. // },
  498. free() {
  499. for (let i = 0; i < global.storePlayers.length; i++) {
  500. var storePlayer = global.storePlayers[i]
  501. if (storePlayer.interval_creatCircleMarker != null) {
  502. clearInterval(storePlayer.interval_creatCircleMarker)
  503. }
  504. if (storePlayer.interval_creatCircleMarker != null) {
  505. clearInterval(storePlayer.interval_showTrail)
  506. }
  507. }
  508. }
  509. }