player.js 18 KB

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