304 行
8.1 KiB
Plaintext
304 行
8.1 KiB
Plaintext
<template>
|
||
<!-- #ifdef APP -->
|
||
<scroll-view style="flex: 1;padding: 6px;">
|
||
<!-- #endif -->
|
||
<text class="uni-h2">罗盘 API</text>
|
||
<text class="notice">演示 `uni.startCompass`、`uni.stopCompass`、`uni.onCompassChange`、`uni.offCompassChange` 的完整流程。靠近强磁场或金属物体时数据可能抖动,可做“8”字校准。Web平台需使用https</text>
|
||
<text class="margin-v">先绑定监听,再启动采集,更容易观察回调是否生效。</text>
|
||
|
||
<text class="uni-h3">实时信息</text>
|
||
<view class="panel-list">
|
||
<text class="panel-item" v-for="item in infoItems" :key="item.label">{{ item.label }}:{{ item.value }}</text>
|
||
</view>
|
||
|
||
<text class="uni-h3">当前方位</text>
|
||
<view class="compass-card">
|
||
<view class="bg-compass-line"></view>
|
||
<image class="bg-compass" src="/static/compass.png" :style="compassStyle" />
|
||
<view class="direction-center">
|
||
<text class="direction-value">{{ directionText }}</text>
|
||
<text class="direction-degree">{{ degreeText }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="uni-row">
|
||
<button id="btn-bind-primary-listener" class="margin-v" @tap="bindPrimaryListener">注册罗盘Change</button>
|
||
<button id="btn-remove-primary-listener" class="margin-v" style="margin-left: 3px;" @tap="removePrimaryListener">注销罗盘Change</button>
|
||
</view>
|
||
<view class="uni-row">
|
||
<button id="btn-start-compass" class="margin-v" type="primary" @tap="startCompassListen">uni.startCompass</button>
|
||
<button id="btn-stop-compass" class="margin-v" style="margin-left: 3px;" @tap="stopCompassListen">uni.stopCompass</button>
|
||
</view>
|
||
<text>重复绑定测试</text>
|
||
<view class="uni-row">
|
||
<button id="btn-bind-secondary-listener" class="margin-v" @tap="bindSecondaryListener">注册罗盘Change2</button>
|
||
<button id="btn-remove-secondary-listener" class="margin-v" style="margin-left: 3px;" @tap="removeSecondaryListener">注销罗盘Change2</button>
|
||
</view>
|
||
<text class="uni-h3">监听日志</text>
|
||
<view class="panel-list">
|
||
<text class="panel-item" v-for="item in logItems" :key="item.id">{{ item.text }}</text>
|
||
</view>
|
||
<!-- #ifdef APP -->
|
||
</scroll-view>
|
||
<!-- #endif -->
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
type DisplayItem = {
|
||
label : string
|
||
value : string
|
||
}
|
||
|
||
type PageCompassAccuracy = number | string | null
|
||
|
||
type LogItem = {
|
||
id : number
|
||
text : string
|
||
}
|
||
|
||
const statusText = ref('未启动')
|
||
const direction = ref(0)
|
||
const directionText = ref('北')
|
||
const accuracyText = ref('null')
|
||
const primaryBound = ref(false)
|
||
const secondaryBound = ref(false)
|
||
const lastAction = ref('等待调用')
|
||
const lastStatus = ref('未执行')
|
||
const lastPayload = ref('请先绑定监听并开始采集')
|
||
const logItems = ref<Array<LogItem>>([])
|
||
const logId = ref(0)
|
||
|
||
const infoItems = computed(() : Array<DisplayItem> => {
|
||
return [
|
||
{ label: '监听状态', value: statusText.value },
|
||
{ label: '主监听', value: primaryBound.value ? '已绑定' : '未绑定' },
|
||
{ label: '额外监听', value: secondaryBound.value ? '已绑定' : '未绑定' },
|
||
{ label: 'accuracy', value: accuracyText.value },
|
||
{ label: '最近动作', value: lastAction.value },
|
||
{ label: '状态', value: lastStatus.value },
|
||
{ label: '说明', value: lastPayload.value }
|
||
]
|
||
})
|
||
|
||
function pushLog(line : string) : void {
|
||
logId.value = logId.value + 1
|
||
const item : LogItem = {
|
||
id: logId.value,
|
||
text: line
|
||
}
|
||
logItems.value.unshift(item)
|
||
if (logItems.value.length > 10) {
|
||
logItems.value.splice(10)
|
||
}
|
||
}
|
||
|
||
function updateResult(action : string, status : string, payload : string) : void {
|
||
lastAction.value = action
|
||
lastStatus.value = status
|
||
lastPayload.value = payload
|
||
pushLog(`${action} -> ${status}`)
|
||
}
|
||
|
||
function formatDirection(value : number) : string {
|
||
return value.toFixed(1)
|
||
}
|
||
|
||
function formatAccuracy(value : PageCompassAccuracy) : string {
|
||
if (value == null) {
|
||
return 'null'
|
||
}
|
||
if (typeof value == 'number') {
|
||
return value.toFixed(2)
|
||
}
|
||
return value
|
||
}
|
||
|
||
function resolveDirectionText(value : number) : string {
|
||
if (value >= 337.5 || value < 22.5) {
|
||
return '北'
|
||
}
|
||
if (value < 67.5) {
|
||
return '东北'
|
||
}
|
||
if (value < 112.5) {
|
||
return '东'
|
||
}
|
||
if (value < 157.5) {
|
||
return '东南'
|
||
}
|
||
if (value < 202.5) {
|
||
return '南'
|
||
}
|
||
if (value < 247.5) {
|
||
return '西南'
|
||
}
|
||
if (value < 292.5) {
|
||
return '西'
|
||
}
|
||
return '西北'
|
||
}
|
||
|
||
const compassStyle = computed(() : string => {
|
||
return `transform: rotate(${-direction.value}deg);`
|
||
})
|
||
|
||
const degreeText = computed(() : string => {
|
||
return `${formatDirection(direction.value)}°`
|
||
})
|
||
|
||
const primaryListener : OnCompassChangeCallback = (result : OnCompassChangeCallbackResult) => {
|
||
direction.value = result.direction
|
||
directionText.value = resolveDirectionText(result.direction)
|
||
accuracyText.value = formatAccuracy(result.accuracy != null ? result.accuracy : null)
|
||
statusText.value = '监听中'
|
||
}
|
||
|
||
const secondaryListener : OnCompassChangeCallback = (result : OnCompassChangeCallbackResult) => {
|
||
pushLog(`额外监听 -> ${formatDirection(result.direction)}° / accuracy ${formatAccuracy(result.accuracy != null ? result.accuracy : null)}`)
|
||
}
|
||
|
||
function ensurePrimaryBound() : void {
|
||
if (!primaryBound.value) {
|
||
uni.onCompassChange(primaryListener)
|
||
primaryBound.value = true
|
||
}
|
||
}
|
||
|
||
function bindPrimaryListener() : void {
|
||
ensurePrimaryBound()
|
||
updateResult('onCompassChange(primary)', '已绑定', '主监听已注册')
|
||
}
|
||
|
||
function bindSecondaryListener() : void {
|
||
if (!secondaryBound.value) {
|
||
uni.onCompassChange(secondaryListener)
|
||
secondaryBound.value = true
|
||
}
|
||
updateResult('onCompassChange(extra)', '已绑定', '额外监听已注册')
|
||
}
|
||
|
||
function removePrimaryListener() : void {
|
||
uni.offCompassChange(primaryListener)
|
||
primaryBound.value = false
|
||
updateResult('offCompassChange(primary)', '已解绑', '主监听已移除')
|
||
}
|
||
|
||
function removeSecondaryListener() : void {
|
||
uni.offCompassChange(secondaryListener)
|
||
secondaryBound.value = false
|
||
updateResult('offCompassChange(extra)', '已解绑', '额外监听已移除')
|
||
}
|
||
|
||
function startCompassListen() : void {
|
||
ensurePrimaryBound()
|
||
uni.startCompass({
|
||
success: (res) => {
|
||
statusText.value = '监听中'
|
||
const message = res.errMsg != null ? res.errMsg as string : 'startCompass:ok'
|
||
updateResult('startCompass', '成功', message)
|
||
},
|
||
fail: (error) => {
|
||
statusText.value = '启动失败'
|
||
const message = error.errMsg != null ? error.errMsg : 'startCompass:fail'
|
||
updateResult('startCompass', `失败 (${error.errCode})`, message)
|
||
}
|
||
})
|
||
}
|
||
|
||
function stopCompassListen() : void {
|
||
uni.stopCompass({
|
||
success: (res) => {
|
||
statusText.value = '已停止'
|
||
const message = res.errMsg != null ? res.errMsg as string : 'stopCompass:ok'
|
||
updateResult('stopCompass', '成功', message)
|
||
},
|
||
fail: (error) => {
|
||
const message = error.errMsg != null ? error.errMsg : 'stopCompass:fail'
|
||
updateResult('stopCompass', `失败 (${error.errCode})`, message)
|
||
}
|
||
})
|
||
}
|
||
|
||
onUnload(() => {
|
||
uni.offCompassChange(primaryListener)
|
||
uni.offCompassChange(secondaryListener)
|
||
uni.stopCompass()
|
||
});
|
||
|
||
defineExpose({
|
||
startCompassListen
|
||
})
|
||
</script>
|
||
|
||
<style>
|
||
.margin-v {
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.notice {
|
||
color: #6b2d16;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.panel-list {
|
||
margin: 5px 0;
|
||
padding: 10px;
|
||
border: 1px solid #ccc;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.panel-item {
|
||
font-size: 14px;
|
||
margin: 4px 0;
|
||
}
|
||
|
||
.compass-card {
|
||
position: relative;
|
||
margin: 12px auto;
|
||
width: 270px;
|
||
height: 270px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.direction-center {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.direction-value {
|
||
font-size: 30px;
|
||
}
|
||
|
||
.direction-degree {
|
||
font-size: 16px;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.bg-compass {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 270px;
|
||
height: 270px;
|
||
transition-duration: 0.1s;
|
||
}
|
||
|
||
.bg-compass-line {
|
||
position: absolute;
|
||
left: 134px;
|
||
top: -5px;
|
||
width: 3px;
|
||
height: 28px;
|
||
background-color: #1aad19;
|
||
border-radius: 100px;
|
||
z-index: 1;
|
||
}
|
||
</style>
|