解决上报位置先后顺序问题
孙泽辉 Lv5

今天测试代码,发现经常获取不到用户位置,原来是顺序问题,显现出我对执行时机还是没有很熟练。

需求背景

需求是这样:用户手机端定位自己位置,上报自己所在位置的路况信息。

例如路上有树歪了,那么就可以掏出手机,定位自己所在位置并填写一些描述后上传到服务器,供后台人员核实。

关于定位:默认显示用户位置,如果定位用户位置失败,则使用默认位置INIT_LAT,INIT_LNG

问题显现

目前的问题是定位后会闪一下使用默认的位置。

image

这是我的代码(错误代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import { Component, Vue, Watch } from 'vue-property-decorator';
import { getPosDesc, getUserLocation } from '@/plugins/map'
import { INIT_LAT, INIT_LNG } from '@/config/map'

@Component
export default class PageReport extends Vue {
// 默认使用初始位置
latitude = INIT_LAT
longitude = INIT_LNG
currentPosDesc = '定位中...'
ctx = null

// 组件挂载后自动定位到用户所在位置
async mounted () {
const _ctx = uni.createMapContext('report-map')
this.ctx= _ctx

await this.move2MyPosition()
}

// 将地图中心点移动到用户所在位置
async move2MyPosition() {
const _ctx = this.ctx;
if (!_ctx) return;
uni.showLoading({
title: '定位中...'
})
const userPosition = await getUserLocation()
_ctx.moveToLocation({
latitude: userPosition?.latitude,
longitude: userPosition?.longitude
})
this.latitude = userPosition?.latitude
this.longitude = userPosition?.longitude
uni.hideLoading()
}

// 监听经度变化,当经度变化时重新获取位置描述
@Watch('latitude', { immediate:true })
async onPositionChange(val) {
const { latitude, longitude } = this
const address = await getPosDesc({lat: latitude, lng: longitude})
this.currentPosDesc = address
}

// 监听用户拖动地图事件,用户拖动地图将重新获取中心点位置
mapChange(e) {
this.ctx?.getCenterLocation({
success: (res)=> {
this.latitude = res.latitude
this.longitude = res.longitude
}
})
}
}

上面代码问题很多,讲主要问题,关于需求上面描述的获取位置逻辑,首先是获取用户位置,获取失败再使用默认位置。

而我写的是默认使用默认位置,在组件挂载后获取用户位置。

这里很有可能(极大可能)出现一种情况:

时机1:开始获取默认位置

时机2:开始获取用户位置

时机3:用户位置获取成功,将定位信息显示为用户位置

时机4:此时默认位置获取成功,默认位置信息覆盖用户位置,此时显示默认位置。

也就是说,默认位置结果到来时机晚于用户位置,导致即使用户位置获取成功也仍然显示默认位置。

对此,我的修复代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { Component, Vue, Watch } from 'vue-property-decorator';
import { getPosDesc, getUserLocation } from '@/plugins/map'
import { INIT_LAT, INIT_LNG } from '@/config/map'

@Component
export default class PageReport extends Vue {
// 默认使用初始位置
latitude = null
longitude = null
currentPosDesc = '定位中...'
ctx = null

// 组件挂载后自动定位到用户所在位置
async mounted () {
const _ctx = uni.createMapContext('report-map')
this.ctx= _ctx

// await this.move2MyPosition()
}
async onShow(){
await this.move2MyPosition()
}
// 将地图中心点移动到用户所在位置
async move2MyPosition() {
let _ctx = this.ctx;
if (!_ctx) {
_ctx = uni.createMapContext('report-map')
this.ctx= _ctx
}
uni.showLoading({
title: '定位中...'
})
try{
const userPosition=await getUserLocation()
_ctx.moveToLocation({
latitude: userPosition?.latitude,
longitude: userPosition?.longitude
})
this.latitude = userPosition?.latitude
this.longitude = userPosition?.longitude
}catch (e){
console.log('error',e)
//如果获取用户位置出现异常则使用默认位置
this.latitude = INIT_LAT
this.longitude = INIT_LNG
}finally {
uni.hideLoading()
}
}

// 监听经度变化,当经度变化时重新获取位置描述
@Watch('latitude', /*{ immediate:true } */)
async onPositionChange(val) {
const { latitude, longitude } = this
const address = await getPosDesc({lat: latitude, lng: longitude})
this.currentPosDesc = address
}

// 监听用户拖动地图事件,用户拖动地图将重新获取中心点位置
mapChange(e) {
this.ctx?.getCenterLocation({
success: (res)=> {
this.latitude = res.latitude
this.longitude = res.longitude
}
})
}
}

组件数据latitude,longitude不再首次赋值默认位置,初始状态应为null

获取用户位置不再从组件挂载时执行mounted,而是页面展示时执行onShow

获取用户位置时,因为是从onShow时执行,地图上下文ctx有两种状态:

  • 未执行过mounted(第一次打开页面),ctx === null,此时应创建MapContext
  • 执行过mounted(第二次打开页面),ctx === MapContext,此时直接使用ctx

在获取用户位置时,有两种结果:

  • 获取位置成功,直接赋值经纬度latitude,longitude为用户位置
  • 获取用户位置失败(原因可能是用户未开启定位权限等),赋值默认位置。

获取位置描述不再从组件执行顺序中默认执行,而是等待第一次位置坐标结果到来时获取描述。

结果

此时问题解决:

image

这里发现显示中心位置的小把手没了,原因在于定位问题,同时为mapcover-image添加v-if即可

1
2
3
4
5
6
7
8
9
10
11
12
13
<view class="map-wrap">
<map id="report-map"
v-if="longitude&&latitude"
show-location :markers="markers"
@regionchange="mapChange"
class="tencent-map" :latitude="latitude"
:longitude="longitude">
<cover-view class="location-btn" @click="move2MyPosition">
<cover-image @click="move2MyPosition" src="/static/icon/report/location.png" class="icon" mode="widthFix" />
</cover-view>
</map>
<cover-image v-if="longitude&&latitude" class="current-site-icon" src="/static/icon/report/choice-marker.png"/>
<view>
 Comments
Comment plugin failed to load
Loading comment plugin
Powered by Hexo & Theme Keep
Total words 85.5k