完善设置面板并整理动画阶段总结

This commit is contained in:
2026-03-26 17:58:42 +08:00
parent 8b10afe5b9
commit 3b5c4501af
8 changed files with 773 additions and 140 deletions

50
GeminiAnlysis.md Normal file
View File

@@ -0,0 +1,50 @@
# CMR-Mini 项目深度分析报告 (GeminiAnalysis.md)
## 1. 项目定位与核心愿景
**CMR-Mini** 是一个运行在微信小程序环境中的高性能**定向越野 (Orienteering)** 实时竞赛/练习引擎。其核心竞争力在于通过自研的 **WebGL 地图渲染管线** 提供流畅的地图交互并结合高精度多传感器融合技术GPS、罗盘、心率、加速度计等实现精准的运动反馈。
## 2. 核心系统架构分析
### 2.1 地图渲染引擎 (Map Engine)
* **渲染技术**:采用 `Single WebGL Pipeline`。相比微信原生地图组件具有更高的定制化能力特别是在“Heading-Up”朝向朝上模式下的性能表现。
* **瓦片管理**:通过 `TileStore` 实现三级缓存(内存 -> 磁盘 -> 网络),并支持 `tilePersistentCache`
* **投影逻辑**:采用 `WGS84 -> WorldTile -> Camera -> Screen` 的标准 GIS 变换链,能够精准处理地理坐标到屏幕像素的映射。
### 2.2 传感器融合系统 (Sensor System)
* **CompassHeadingController**:核心逻辑在于罗盘数据 (`wx.onCompassChange`) 与设备姿态 (`wx.onDeviceMotionChange`) 的协同。
* **LocationController**:支持真实 GPS 数据与 Mock 模拟器(通过 WebSocket 连接 `mock-gps-sim` 工具)的无缝切换。
* **TelemetryRuntime**:实现了运动参数的实时计算,包括速度、距离目标点距离、心率分区等指标。
### 2.3 游戏逻辑与规则 (Game Logic)
* **GameRuntime**:驱动对局状态机,支持“顺序赛 (Classic Sequential)”与“积分赛 (Score-O)”。
* **PunchPolicy**:实现了自动进入检查点范围触发、手动打点、跳过点位等业务逻辑。
## 3. 指北针 (Compass) 平滑度瓶颈分析
根据目前的实现,指北针的卡顿感主要源于以下三个层面:
1. **采样频率与插值逻辑**
* 目前使用 `interpolateHeadingDeg` 进行线性差值,且 `ABSOLUTE_HEADING_CORRECTION` 为固定系数 (0.44)。这种静态系数在“静态微调”时显得不够敏锐,在“快速旋转”时又显得滞后。
2. **Android/iOS 差异化丢帧**
* Android 传感器回调频率不稳定。
* 逻辑中对 `direction` 进行了严格的数值有效性判断,若系统由于硬件抖动返回短时异常值,会导致视觉上的“跳帧”。
3. **UI 同步周期限制**
* `MapEngine``UI_SYNC_INTERVAL_MS` 设置为 80ms这意味着视觉反馈的最高帧率仅为 12.5Hz,远低于屏幕刷新率,导致指针转动不够丝滑。
## 4. 优化技术路线建议
### 4.1 引入指数加权移动平均 (EWMA) 的动态系数
建议根据旋转角速度动态调整平滑系数。当检测到瞬时角位移较大时,降低平滑度以追求响应速度;当位移较小时,增加平滑度以过滤手抖带来的噪声。
### 4.2 视觉平滑:使用 CSS Transform 或 WebGL 帧间补偿
目前数据是由控制器下发到 UI 的。建议:
* **方案 A (推荐)**:在 UI 层(`.wxml`/`.wxss`)利用 `transition: transform 0.1s linear;` 实现视觉层面的自动补帧。
* **方案 B**:在 WebGL 渲染循环内进行帧间插值,将数据的 12.5Hz 提升到 渲染循环的 60Hz。
### 4.3 预测与死区 (Dead-zone) 过滤
`CompassHeadingController` 中加入微小位移的死区过滤逻辑,避免由于硬件高频微小抖动导致的视图高频重绘,降低系统功耗的同时提升视觉稳定性。
## 5. 结论
CMR-Mini 已经建立了一个非常坚实的专业定向越野引擎基础。后续的优化重点应从“功能的实现”转向“交互的极致平滑”,特别是针对指北针这类核心导向组件,需要更精细化的信号处理策略。
---
*Generated by Gemini CLI Analysis Tool*

0
MyToDo.md Normal file
View File

View File

@@ -0,0 +1,192 @@
# 动画体系阶段性小结
## 1. 当前定位
目前动画体系已经从“页面里临时加 class”的阶段进入了**有主链、有分层、有性能分级**的阶段。
当前主链可以概括为:
- 事件触发
- `feedbackConfig`
- `UiEffectDirector`
- `FeedbackDirector`
- `MapEngine`
- 页面层 / 渲染层消费
也就是:
**事件 -> 效果配置 -> 宿主提交 -> 页面 / Renderer 落地**
这说明动画已经不再是零散实现,而开始进入架构化管理。
---
## 2. 已经完成的内容
### 2.1 HUD 动效
已经完成:
- 打点成功后的 `进度` 动效
- 打点成功后的 `点距` 动效
- HUD 数字轻量过渡:
- 计时
- 里程
- 速度
- 心率
这些动效已经接入正式链路,不是页面单独临时处理。
### 2.2 地图空间动画
已经完成:
- 当前目标点状态强调
- 可打点状态强调
- 已完成点状态过渡
- 已跳过点灰态与标记
- 开始点 / 终点完成后的 settle 外环
- 轻量地图 pulse
### 2.3 局部 UI / Stage 动效
已经完成:
- 轻量 stage flash
- 顶部提示和局部反馈的基础动画承载链
### 2.4 动画性能分级
已经完成 2 级动画分级:
- `standard`
- `lite`
当前 `lite` 的主要策略包括:
- 减少 pulse 层数
- 降低几何分段
- 降低渲染动画频率
- 关闭部分 HUD 动画
- 关闭或减弱某些 stage/UI 动效
这意味着动画体系已经开始考虑**低端机表现**,不是只追求效果。
---
## 3. 当前架构上的价值
动画体系现在已经带来了几个明确收益:
- 动效不再散落在多个页面细节里
- 高频状态变化有了统一反馈语言
- 地图状态和 HUD 状态开始形成一致体验
- 性能分级已经进入体系,可服务低端机
从架构角度看,这意味着:
**动画已经成为正式能力层,而不是临时视觉补丁。**
---
## 4. 当前还不够完整的地方
虽然主链已经成型,但当前还没有完全形成“动画字典”和完整 profile 体系。
目前仍然存在这些不足:
- 哪些事件触发哪些动画,还没有整理成统一字典
- 部分高频状态切换还不够连续
- `跳点` 已有逻辑和状态,但动画语言还不完整
- 危险/高压状态动画还没有正式开始
- 动画 profile 还没有真正配置化
所以当前阶段可以定义为:
**第一阶段后半段:主链已成型,但还需要把高频体验打磨完整。**
---
## 5. 下一阶段最值得做的事情
### 5.1 先整理动画字典
建议先把动画按事件梳理出来,例如:
- `session_started`
- `control_ready`
- `control_completed:start`
- `control_completed:control`
- `control_completed:finish`
- `control_skipped`
- `gps_lock_changed`
- `guidance_state_changed`
- `heart_rate_zone_changed`
并明确每个事件对应:
- 地图动画
- HUD 动画
- UI 动画
- `lite` 下是否保留
这一步是当前最值得优先完成的工作。
### 5.2 补完整“目标状态切换连续感”
继续打磨:
- 当前目标
- 进入可打点
- 打点成功
- 切到下一个目标
让这一整段切换更连贯、更有节奏。
### 5.3 补齐“跳点”动画
建议下一步把跳点也正式纳入动画体系:
- 跳点确认后
- 当前点灰化
- 下一个目标接管强调
- HUD 给出轻量反馈
### 5.4 再做危险 / 高压反馈
这部分适合进入下一阶段:
- 高心率反馈
- 危险区反馈
- 幽灵追逐反馈
- 边缘呼吸 / 紧张感动效
这条线很适合后续玩法扩展。
---
## 6. 建议的实施顺序
推荐继续推进的顺序:
1. 动画字典整理
2. 目标切换连续感补齐
3. 跳点动画补齐
4. 危险 / 高压状态动画
5. 更进一步的配置化 profile
---
## 7. 结论
当前动画体系已经是一个明确的阶段性成果:
- 有主链
- 有分层
- 有高频核心动画
- 有性能分级
接下来最该做的不是“继续零散加动画”,而是:
**把现有能力收成动画字典,并优先打磨目标切换与跳点这两条高频体验链。**

View File

@@ -59,6 +59,7 @@ const AUTO_ROTATE_MAX_STEP_DEG = 0.75
const AUTO_ROTATE_HEADING_SMOOTHING = 0.46
const COMPASS_NEEDLE_FRAME_MS = 16
const COMPASS_NEEDLE_SNAP_DEG = 0.08
const COMPASS_BOOTSTRAP_RETRY_DELAY_MS = 700
const COMPASS_TUNING_PRESETS: Record<CompassTuningProfile, {
needleMinSmoothing: number
needleMaxSmoothing: number
@@ -830,6 +831,7 @@ export class MapEngine {
viewSyncTimer: number
autoRotateTimer: number
compassNeedleTimer: number
compassBootstrapRetryTimer: number
pendingViewPatch: Partial<MapEngineViewState>
mounted: boolean
diagnosticUiEnabled: boolean
@@ -838,6 +840,7 @@ export class MapEngine {
smoothedSensorHeadingDeg: number | null
compassDisplayHeadingDeg: number | null
targetCompassDisplayHeadingDeg: number | null
lastCompassSampleAt: number
compassSource: 'compass' | 'motion' | null
compassTuningProfile: CompassTuningProfile
smoothedMovementHeadingDeg: number | null
@@ -1282,6 +1285,7 @@ export class MapEngine {
this.viewSyncTimer = 0
this.autoRotateTimer = 0
this.compassNeedleTimer = 0
this.compassBootstrapRetryTimer = 0
this.pendingViewPatch = {}
this.mounted = false
this.diagnosticUiEnabled = false
@@ -1290,6 +1294,7 @@ export class MapEngine {
this.smoothedSensorHeadingDeg = null
this.compassDisplayHeadingDeg = null
this.targetCompassDisplayHeadingDeg = null
this.lastCompassSampleAt = 0
this.compassSource = null
this.compassTuningProfile = 'balanced'
this.smoothedMovementHeadingDeg = null
@@ -1406,6 +1411,7 @@ export class MapEngine {
this.clearViewSyncTimer()
this.clearAutoRotateTimer()
this.clearCompassNeedleTimer()
this.clearCompassBootstrapRetryTimer()
this.clearPunchFeedbackTimer()
this.clearContentCardTimer()
this.clearMapPulseTimer()
@@ -1424,6 +1430,11 @@ export class MapEngine {
handleAppShow(): void {
this.feedbackDirector.setAppAudioMode('foreground')
if (this.mounted) {
this.lastCompassSampleAt = 0
this.compassController.start()
this.scheduleCompassBootstrapRetry()
}
}
handleAppHide(): void {
@@ -2351,7 +2362,9 @@ export class MapEngine {
})
this.syncRenderer()
this.accelerometerErrorText = null
this.lastCompassSampleAt = 0
this.compassController.start()
this.scheduleCompassBootstrapRetry()
this.gyroscopeController.start()
this.deviceMotionController.start()
}
@@ -2980,6 +2993,8 @@ export class MapEngine {
}
handleCompassHeading(headingDeg: number): void {
this.lastCompassSampleAt = Date.now()
this.clearCompassBootstrapRetryTimer()
this.applyHeadingSample(headingDeg, 'compass')
}
@@ -3584,6 +3599,29 @@ export class MapEngine {
}
}
clearCompassBootstrapRetryTimer(): void {
if (this.compassBootstrapRetryTimer) {
clearTimeout(this.compassBootstrapRetryTimer)
this.compassBootstrapRetryTimer = 0
}
}
scheduleCompassBootstrapRetry(): void {
this.clearCompassBootstrapRetryTimer()
if (!this.mounted) {
return
}
this.compassBootstrapRetryTimer = setTimeout(() => {
this.compassBootstrapRetryTimer = 0
if (!this.mounted || this.lastCompassSampleAt > 0) {
return
}
this.compassController.stop()
this.compassController.start()
}, COMPASS_BOOTSTRAP_RETRY_DELAY_MS) as unknown as number
}
syncCompassDisplayState(): void {
this.setState({
compassNeedleDeg: formatCompassNeedleDegForMode(this.northReferenceMode, this.compassDisplayHeadingDeg),

View File

@@ -29,15 +29,37 @@ type ScaleRulerMajorMarkData = {
topPx: number
label: string
}
type SideButtonMode = 'all' | 'left' | 'right' | 'hidden'
type SideButtonMode = 'shown' | 'hidden'
type SideActionButtonState = 'muted' | 'default' | 'active'
type SideButtonPlacement = 'left' | 'right'
type CenterScaleRulerAnchorMode = 'screen-center' | 'compass-center'
type UserNorthReferenceMode = 'magnetic' | 'true'
type CompassTuningProfile = 'smooth' | 'balanced' | 'responsive'
type SettingLockKey =
| 'lockAnimationLevel'
| 'lockSideButtonPlacement'
| 'lockAutoRotate'
| 'lockCompassTuning'
| 'lockScaleRulerVisible'
| 'lockScaleRulerAnchor'
| 'lockNorthReference'
| 'lockHeartRateDevice'
type StoredUserSettings = {
animationLevel?: AnimationLevel
autoRotateEnabled?: boolean
compassTuningProfile?: CompassTuningProfile
northReferenceMode?: UserNorthReferenceMode
sideButtonPlacement?: SideButtonPlacement
showCenterScaleRuler?: boolean
centerScaleRulerAnchorMode?: CenterScaleRulerAnchorMode
lockAnimationLevel?: boolean
lockSideButtonPlacement?: boolean
lockAutoRotate?: boolean
lockCompassTuning?: boolean
lockScaleRulerVisible?: boolean
lockScaleRulerAnchor?: boolean
lockNorthReference?: boolean
lockHeartRateDevice?: boolean
}
type MapPageData = MapEngineViewState & {
showDebugPanel: boolean
@@ -68,6 +90,16 @@ type MapPageData = MapEngineViewState & {
compassTicks: CompassTickData[]
compassLabels: CompassLabelData[]
sideButtonMode: SideButtonMode
sideButtonPlacement: SideButtonPlacement
autoRotateEnabled: boolean
lockAnimationLevel: boolean
lockSideButtonPlacement: boolean
lockAutoRotate: boolean
lockCompassTuning: boolean
lockScaleRulerVisible: boolean
lockScaleRulerAnchor: boolean
lockNorthReference: boolean
lockHeartRateDevice: boolean
sideToggleIconSrc: string
sideButton2Class: string
sideButton4Class: string
@@ -334,12 +366,45 @@ function loadStoredUserSettings(): StoredUserSettings {
if (normalized.northReferenceMode === 'magnetic' || normalized.northReferenceMode === 'true') {
settings.northReferenceMode = normalized.northReferenceMode
}
if (typeof normalized.autoRotateEnabled === 'boolean') {
settings.autoRotateEnabled = normalized.autoRotateEnabled
}
if (normalized.compassTuningProfile === 'smooth' || normalized.compassTuningProfile === 'balanced' || normalized.compassTuningProfile === 'responsive') {
settings.compassTuningProfile = normalized.compassTuningProfile
}
if (normalized.sideButtonPlacement === 'left' || normalized.sideButtonPlacement === 'right') {
settings.sideButtonPlacement = normalized.sideButtonPlacement
}
if (typeof normalized.showCenterScaleRuler === 'boolean') {
settings.showCenterScaleRuler = normalized.showCenterScaleRuler
}
if (normalized.centerScaleRulerAnchorMode === 'screen-center' || normalized.centerScaleRulerAnchorMode === 'compass-center') {
settings.centerScaleRulerAnchorMode = normalized.centerScaleRulerAnchorMode
}
if (typeof normalized.lockAnimationLevel === 'boolean') {
settings.lockAnimationLevel = normalized.lockAnimationLevel
}
if (typeof normalized.lockSideButtonPlacement === 'boolean') {
settings.lockSideButtonPlacement = normalized.lockSideButtonPlacement
}
if (typeof normalized.lockAutoRotate === 'boolean') {
settings.lockAutoRotate = normalized.lockAutoRotate
}
if (typeof normalized.lockCompassTuning === 'boolean') {
settings.lockCompassTuning = normalized.lockCompassTuning
}
if (typeof normalized.lockScaleRulerVisible === 'boolean') {
settings.lockScaleRulerVisible = normalized.lockScaleRulerVisible
}
if (typeof normalized.lockScaleRulerAnchor === 'boolean') {
settings.lockScaleRulerAnchor = normalized.lockScaleRulerAnchor
}
if (typeof normalized.lockNorthReference === 'boolean') {
settings.lockNorthReference = normalized.lockNorthReference
}
if (typeof normalized.lockHeartRateDevice === 'boolean') {
settings.lockHeartRateDevice = normalized.lockHeartRateDevice
}
return settings
} catch {
return {}
@@ -351,26 +416,24 @@ function persistStoredUserSettings(settings: StoredUserSettings) {
wx.setStorageSync(USER_SETTINGS_STORAGE_KEY, settings)
} catch {}
}
function toggleStoredSettingLock(settings: StoredUserSettings, key: SettingLockKey): StoredUserSettings {
return {
...settings,
[key]: !settings[key],
}
}
function buildSideButtonVisibility(mode: SideButtonMode) {
return {
sideButtonMode: mode,
showLeftButtonGroup: mode === 'all' || mode === 'left' || mode === 'right',
showRightButtonGroups: mode === 'all' || mode === 'right',
showBottomDebugButton: mode !== 'hidden',
showLeftButtonGroup: mode === 'shown',
showRightButtonGroups: false,
showBottomDebugButton: true,
}
}
function getNextSideButtonMode(currentMode: SideButtonMode): SideButtonMode {
if (currentMode === 'all') {
return 'left'
}
if (currentMode === 'left') {
return 'right'
}
if (currentMode === 'right') {
return 'hidden'
}
return 'left'
return currentMode === 'shown' ? 'hidden' : 'shown'
}
function buildCompassTicks(): CompassTickData[] {
const ticks: CompassTickData[] = []
@@ -409,9 +472,6 @@ function getFallbackStageRect(): MapEngineStageRect {
}
function getSideToggleIconSrc(mode: SideButtonMode): string {
if (mode === 'left') {
return '../../assets/btn_more2.png'
}
if (mode === 'hidden') {
return '../../assets/btn_more1.png'
}
@@ -641,6 +701,15 @@ Page({
hudPanelIndex: 0,
configSourceText: '顺序赛配置',
centerScaleRulerAnchorMode: 'screen-center',
autoRotateEnabled: false,
lockAnimationLevel: false,
lockSideButtonPlacement: false,
lockAutoRotate: false,
lockCompassTuning: false,
lockScaleRulerVisible: false,
lockScaleRulerAnchor: false,
lockNorthReference: false,
lockHeartRateDevice: false,
gameInfoTitle: '当前游戏',
gameInfoSubtitle: '未开始',
gameInfoLocalRows: [],
@@ -653,6 +722,7 @@ Page({
panelDistanceUnitText: '',
panelProgressText: '0/0',
showPunchHintBanner: true,
sideButtonPlacement: 'left',
gameSessionStatus: 'idle',
gameModeText: '顺序赛',
gpsLockEnabled: false,
@@ -730,9 +800,9 @@ Page({
centerScaleRulerMajorMarks: [],
compassTicks: buildCompassTicks(),
compassLabels: buildCompassLabels(),
...buildSideButtonVisibility('left'),
...buildSideButtonVisibility('shown'),
...buildSideButtonState({
sideButtonMode: 'left',
sideButtonMode: 'shown',
showGameInfoPanel: false,
showSystemSettingsPanel: false,
showCenterScaleRuler: false,
@@ -787,6 +857,9 @@ Page({
} as MapPageData
const derivedPatch: Partial<MapPageData> = {}
if (typeof nextPatch.orientationMode === 'string') {
nextData.autoRotateEnabled = nextPatch.orientationMode === 'heading-up'
}
if (
this.data.showCenterScaleRuler
&& hasAnyPatchKey(nextPatch as Record<string, unknown>, CENTER_SCALE_RULER_DEP_KEYS)
@@ -886,9 +959,19 @@ Page({
if (storedUserSettings.animationLevel) {
mapEngine.handleSetAnimationLevel(storedUserSettings.animationLevel)
}
const initialAutoRotateEnabled = storedUserSettings.autoRotateEnabled !== false
if (initialAutoRotateEnabled) {
mapEngine.handleSetHeadingUpMode()
} else {
mapEngine.handleSetManualMode()
}
if (storedUserSettings.compassTuningProfile) {
mapEngine.handleSetCompassTuningProfile(storedUserSettings.compassTuningProfile)
}
if (storedUserSettings.northReferenceMode) {
mapEngine.handleSetNorthReferenceMode(storedUserSettings.northReferenceMode)
}
const initialSideButtonPlacement = storedUserSettings.sideButtonPlacement || 'left'
mapEngine.setDiagnosticUiEnabled(false)
centerScaleRulerInputCache = {
@@ -914,6 +997,16 @@ Page({
hudPanelIndex: 0,
configSourceText: '顺序赛配置',
centerScaleRulerAnchorMode: initialCenterScaleRulerAnchorMode,
autoRotateEnabled: initialAutoRotateEnabled,
lockAnimationLevel: !!storedUserSettings.lockAnimationLevel,
lockSideButtonPlacement: !!storedUserSettings.lockSideButtonPlacement,
lockAutoRotate: !!storedUserSettings.lockAutoRotate,
lockCompassTuning: !!storedUserSettings.lockCompassTuning,
lockScaleRulerVisible: !!storedUserSettings.lockScaleRulerVisible,
lockScaleRulerAnchor: !!storedUserSettings.lockScaleRulerAnchor,
lockNorthReference: !!storedUserSettings.lockNorthReference,
lockHeartRateDevice: !!storedUserSettings.lockHeartRateDevice,
sideButtonPlacement: initialSideButtonPlacement,
gameInfoTitle: '当前游戏',
gameInfoSubtitle: '未开始',
gameInfoLocalRows: [],
@@ -996,9 +1089,9 @@ Page({
stageFxClass: '',
compassTicks: buildCompassTicks(),
compassLabels: buildCompassLabels(),
...buildSideButtonVisibility('left'),
...buildSideButtonVisibility('shown'),
...buildSideButtonState({
sideButtonMode: 'left',
sideButtonMode: 'shown',
showGameInfoPanel: false,
showSystemSettingsPanel: false,
showCenterScaleRuler: initialShowCenterScaleRuler,
@@ -1218,24 +1311,6 @@ Page({
}
},
handleSetCompassTuningSmooth() {
if (mapEngine) {
mapEngine.handleSetCompassTuningProfile('smooth')
}
},
handleSetCompassTuningBalanced() {
if (mapEngine) {
mapEngine.handleSetCompassTuningProfile('balanced')
}
},
handleSetCompassTuningResponsive() {
if (mapEngine) {
mapEngine.handleSetCompassTuningProfile('responsive')
}
},
handleAutoRotateCalibrate() {
if (mapEngine) {
mapEngine.handleAutoRotateCalibrate()
@@ -1339,6 +1414,9 @@ Page({
},
handleClearPreferredHeartRateDevice() {
if (this.data.lockHeartRateDevice) {
return
}
if (mapEngine) {
mapEngine.handleClearPreferredHeartRateDevice()
}
@@ -1462,6 +1540,7 @@ Page({
const localRows = snapshot.localRows.concat([
{ label: '比例尺开关', value: this.data.showCenterScaleRuler ? '开启' : '关闭' },
{ label: '比例尺锚点', value: this.data.centerScaleRulerAnchorMode === 'compass-center' ? '指北针圆心' : '屏幕中心' },
{ label: '按钮习惯', value: this.data.sideButtonPlacement === 'right' ? '右手' : '左手' },
{ label: '比例尺可见', value: this.data.centerScaleRulerVisible ? 'true' : 'false' },
{ label: '比例尺中心X', value: `${this.data.centerScaleRulerCenterXPx}px` },
{ label: '比例尺零点Y', value: `${this.data.centerScaleRulerZeroYPx}px` },
@@ -1575,7 +1654,7 @@ Page({
handleSystemSettingsPanelTap() {},
handleSetAnimationLevelStandard() {
if (!mapEngine) {
if (this.data.lockAnimationLevel || !mapEngine) {
return
}
mapEngine.handleSetAnimationLevel('standard')
@@ -1586,7 +1665,7 @@ Page({
},
handleSetAnimationLevelLite() {
if (!mapEngine) {
if (this.data.lockAnimationLevel || !mapEngine) {
return
}
mapEngine.handleSetAnimationLevel('lite')
@@ -1596,8 +1675,89 @@ Page({
})
},
handleSetSideButtonPlacementLeft() {
if (this.data.lockSideButtonPlacement) {
return
}
this.setData({
sideButtonPlacement: 'left',
})
persistStoredUserSettings({
...loadStoredUserSettings(),
sideButtonPlacement: 'left',
})
},
handleSetSideButtonPlacementRight() {
if (this.data.lockSideButtonPlacement) {
return
}
this.setData({
sideButtonPlacement: 'right',
})
persistStoredUserSettings({
...loadStoredUserSettings(),
sideButtonPlacement: 'right',
})
},
handleSetAutoRotateEnabledOn() {
if (this.data.lockAutoRotate || !mapEngine) {
return
}
mapEngine.handleSetHeadingUpMode()
persistStoredUserSettings({
...loadStoredUserSettings(),
autoRotateEnabled: true,
})
},
handleSetAutoRotateEnabledOff() {
if (this.data.lockAutoRotate || !mapEngine) {
return
}
mapEngine.handleSetManualMode()
persistStoredUserSettings({
...loadStoredUserSettings(),
autoRotateEnabled: false,
})
},
handleSetCompassTuningSmooth() {
if (this.data.lockCompassTuning || !mapEngine) {
return
}
mapEngine.handleSetCompassTuningProfile('smooth')
persistStoredUserSettings({
...loadStoredUserSettings(),
compassTuningProfile: 'smooth',
})
},
handleSetCompassTuningBalanced() {
if (this.data.lockCompassTuning || !mapEngine) {
return
}
mapEngine.handleSetCompassTuningProfile('balanced')
persistStoredUserSettings({
...loadStoredUserSettings(),
compassTuningProfile: 'balanced',
})
},
handleSetCompassTuningResponsive() {
if (this.data.lockCompassTuning || !mapEngine) {
return
}
mapEngine.handleSetCompassTuningProfile('responsive')
persistStoredUserSettings({
...loadStoredUserSettings(),
compassTuningProfile: 'responsive',
})
},
handleSetNorthReferenceMagnetic() {
if (!mapEngine) {
if (this.data.lockNorthReference || !mapEngine) {
return
}
mapEngine.handleSetNorthReferenceMode('magnetic')
@@ -1608,7 +1768,7 @@ Page({
},
handleSetNorthReferenceTrue() {
if (!mapEngine) {
if (this.data.lockNorthReference || !mapEngine) {
return
}
mapEngine.handleSetNorthReferenceMode('true')
@@ -1618,6 +1778,18 @@ Page({
})
},
handleToggleSettingLock(event: WechatMiniprogram.TouchEvent) {
const key = event.currentTarget.dataset.key as SettingLockKey | undefined
if (!key) {
return
}
const nextValue = !this.data[key]
this.setData({
[key]: nextValue,
} as Record<string, boolean>)
persistStoredUserSettings(toggleStoredSettingLock(loadStoredUserSettings(), key))
},
handleOverlayTouch() {},
handlePunchAction() {
@@ -1674,16 +1846,24 @@ Page({
}
},
handleToggleMapRotateMode() {
if (!mapEngine) {
if (!mapEngine || this.data.lockAutoRotate) {
return
}
if (this.data.orientationMode === 'heading-up') {
mapEngine.handleSetManualMode()
persistStoredUserSettings({
...loadStoredUserSettings(),
autoRotateEnabled: false,
})
return
}
mapEngine.handleSetHeadingUpMode()
persistStoredUserSettings({
...loadStoredUserSettings(),
autoRotateEnabled: true,
})
},
handleToggleDebugPanel() {
const nextShowDebugPanel = !this.data.showDebugPanel
@@ -1788,6 +1968,9 @@ Page({
},
handleSetCenterScaleRulerVisibleOn() {
if (this.data.lockScaleRulerVisible) {
return
}
this.applyCenterScaleRulerSettings(true, this.data.centerScaleRulerAnchorMode)
persistStoredUserSettings({
...loadStoredUserSettings(),
@@ -1797,6 +1980,9 @@ Page({
},
handleSetCenterScaleRulerVisibleOff() {
if (this.data.lockScaleRulerVisible) {
return
}
this.applyCenterScaleRulerSettings(false, this.data.centerScaleRulerAnchorMode)
persistStoredUserSettings({
...loadStoredUserSettings(),
@@ -1806,6 +1992,9 @@ Page({
},
handleSetCenterScaleRulerAnchorScreenCenter() {
if (this.data.lockScaleRulerAnchor) {
return
}
this.applyCenterScaleRulerSettings(this.data.showCenterScaleRuler, 'screen-center')
persistStoredUserSettings({
...loadStoredUserSettings(),
@@ -1815,6 +2004,9 @@ Page({
},
handleSetCenterScaleRulerAnchorCompassCenter() {
if (this.data.lockScaleRulerAnchor) {
return
}
this.applyCenterScaleRulerSettings(this.data.showCenterScaleRuler, 'compass-center')
persistStoredUserSettings({
...loadStoredUserSettings(),

View File

@@ -85,36 +85,30 @@
<view class="game-punch-hint__close" catchtouchstart="handlePunchHintTap" catchtouchmove="handlePunchHintTap" catchtouchend="handlePunchHintTap" catchtap="handleClosePunchHint">×</view>
</view>
<cover-view class="map-side-toggle" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel}}" style="top: {{topInsetHeight}}px;" bindtap="handleCycleSideButtons">
<cover-view class="map-side-toggle {{sideButtonPlacement === 'right' ? 'map-side-toggle--right' : 'map-side-toggle--left'}}" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel}}" style="top: {{topInsetHeight}}px;" bindtap="handleCycleSideButtons">
<cover-view class="map-side-button map-side-button--icon">
<cover-image class="map-side-button__image" src="{{sideToggleIconSrc}}"></cover-image>
</cover-view>
</cover-view>
<cover-view class="map-side-column map-side-column--left map-side-column--left-group" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showLeftButtonGroup}}" style="top: {{topInsetHeight}}px;">
<cover-view class="map-side-column {{sideButtonPlacement === 'right' ? 'map-side-column--right-group' : 'map-side-column--left'}} map-side-column--left-group" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showLeftButtonGroup}}" style="top: {{topInsetHeight}}px;">
<cover-view class="map-side-button map-side-button--icon" bindtap="handleToggleMapRotateMode"><cover-image class="map-side-button__rotate-image {{orientationMode === 'heading-up' ? 'map-side-button__rotate-image--active' : ''}}" src="../../assets/btn_map_rotate_cropped.png"></cover-image></cover-view>
<cover-view class="map-side-button map-side-button--muted"><cover-view class="map-side-button__text">1</cover-view></cover-view>
<cover-view class="{{sideButton2Class}}" bindtap="handleToggleGpsLock"><cover-view class="map-side-button__text">2</cover-view></cover-view>
<cover-view class="map-side-button map-side-button--active"><cover-view class="map-side-button__text">3</cover-view></cover-view>
<cover-view class="{{sideButton4Class}}" bindtap="handleForceExitGame"><cover-image class="map-side-button__action-image" src="../../assets/btn_exit.png"></cover-image></cover-view>
<cover-view class="{{sideButton2Class}}" bindtap="handleToggleGpsLock">
<cover-image
wx:if="{{gpsLockEnabled}}"
class="map-side-button__action-image"
src="../../assets/btn_locked.png"
></cover-image>
<cover-image
wx:else
class="map-side-button__action-image"
src="../../assets/btn_unlock.png"
></cover-image>
</cover-view>
<cover-view class="map-side-column map-side-column--right-main" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showRightButtonGroups}}" style="top: {{topInsetHeight}}px;">
<cover-view class="map-side-button"><cover-view class="map-side-button__text">5</cover-view></cover-view>
<cover-view class="map-side-button map-side-button--active"><cover-view class="map-side-button__text">6</cover-view></cover-view>
<cover-view class="map-side-button"><cover-view class="map-side-button__text">7</cover-view></cover-view>
<cover-view class="map-side-button map-side-button--active"><cover-view class="map-side-button__text">8</cover-view></cover-view>
<cover-view class="map-side-button map-side-button--muted"><cover-view class="map-side-button__text">9</cover-view></cover-view>
<cover-view class="map-side-button map-side-button--active"><cover-view class="map-side-button__text">10</cover-view></cover-view>
</cover-view>
<cover-view class="map-side-column map-side-column--right-sub" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showRightButtonGroups}}" style="top: {{topInsetHeight}}px;">
<cover-view class="{{sideButton11Class}}" bindtap="handleOpenGameInfoPanel"><cover-image class="map-side-button__action-image" src="../../assets/btn_info.png"></cover-image></cover-view>
<cover-view class="{{sideButton12Class}}" bindtap="handleOpenSystemSettingsPanel"><cover-view class="map-side-button__text">12</cover-view></cover-view>
<cover-view class="map-side-button"><cover-view class="map-side-button__text">13</cover-view></cover-view>
<cover-view class="map-side-button"><cover-view class="map-side-button__text">14</cover-view></cover-view>
<cover-view class="map-side-button"><cover-view class="map-side-button__text">15</cover-view></cover-view>
<cover-view class="{{sideButton16Class}}" bindtap="handleSkipAction"><cover-image class="map-side-button__action-image" src="../../assets/btn_skip_cp.png"></cover-image></cover-view>
<cover-view class="{{sideButton11Class}}" bindtap="handleOpenGameInfoPanel"><cover-image class="map-side-button__action-image" src="../../assets/btn_info.png"></cover-image></cover-view>
<cover-view class="{{sideButton12Class}}" bindtap="handleOpenSystemSettingsPanel"><cover-image class="map-side-button__action-image" src="../../assets/btn_settings.png"></cover-image></cover-view>
<cover-view class="{{sideButton4Class}}" bindtap="handleForceExitGame"><cover-image class="map-side-button__action-image" src="../../assets/btn_exit.png"></cover-image></cover-view>
</cover-view>
<cover-view class="map-punch-button {{punchButtonEnabled ? 'map-punch-button--active' : ''}} {{punchButtonFxClass}}" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel}}" bindtap="handlePunchAction">
@@ -298,71 +292,173 @@
<scroll-view class="game-info-modal__content" scroll-y enhanced show-scrollbar="true">
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">01. 动画性能</view>
<view class="debug-section__desc">根据设备性能切换动画强度,低端机建议精简</view>
</view>
<view class="debug-section__lock {{lockAnimationLevel ? 'debug-section__lock--active' : ''}}" data-key="lockAnimationLevel" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockAnimationLevel ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前级别</text>
<text class="info-panel__value">{{animationLevel === 'lite' ? '精简' : '标准'}}</text>
<text class="info-panel__value">{{animationLevel === 'lite' ? '精简' : '标准'}}{{lockAnimationLevel ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{animationLevel === 'standard' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetAnimationLevelStandard">标准</view>
<view class="control-chip {{animationLevel === 'lite' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetAnimationLevelLite">精简</view>
<view class="control-chip {{animationLevel === 'standard' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockAnimationLevel ? 'control-chip--disabled' : ''}}" bindtap="handleSetAnimationLevelStandard">标准</view>
<view class="control-chip {{animationLevel === 'lite' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockAnimationLevel ? 'control-chip--disabled' : ''}}" bindtap="handleSetAnimationLevelLite">精简</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__title">02. 比例尺显示</view>
<view class="debug-section__desc">控制比例尺显示与否,默认沿用你的本地偏好</view>
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">02. 按钮习惯</view>
<view class="debug-section__desc">切换功能按钮显示在左侧还是右侧,适配左手/右手操作习惯</view>
</view>
<view class="debug-section__lock {{lockSideButtonPlacement ? 'debug-section__lock--active' : ''}}" data-key="lockSideButtonPlacement" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockSideButtonPlacement ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前习惯</text>
<text class="info-panel__value">{{sideButtonPlacement === 'right' ? '右手' : '左手'}}{{lockSideButtonPlacement ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{sideButtonPlacement === 'left' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockSideButtonPlacement ? 'control-chip--disabled' : ''}}" bindtap="handleSetSideButtonPlacementLeft">左手</view>
<view class="control-chip {{sideButtonPlacement === 'right' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockSideButtonPlacement ? 'control-chip--disabled' : ''}}" bindtap="handleSetSideButtonPlacementRight">右手</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">03. 自动转图</view>
<view class="debug-section__desc">控制地图是否跟随朝向自动旋转,外部按钮与这里保持同步</view>
</view>
<view class="debug-section__lock {{lockAutoRotate ? 'debug-section__lock--active' : ''}}" data-key="lockAutoRotate" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockAutoRotate ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前状态</text>
<text class="info-panel__value">{{showCenterScaleRuler ? '显示' : '隐藏'}}</text>
<text class="info-panel__value">{{autoRotateEnabled ? '开启' : '关闭'}}{{lockAutoRotate ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{showCenterScaleRuler ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCenterScaleRulerVisibleOn">显示</view>
<view class="control-chip {{!showCenterScaleRuler ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCenterScaleRulerVisibleOff">隐藏</view>
<view class="control-chip {{autoRotateEnabled ? 'control-chip--active' : 'control-chip--secondary'}} {{lockAutoRotate ? 'control-chip--disabled' : ''}}" bindtap="handleSetAutoRotateEnabledOn">开启</view>
<view class="control-chip {{!autoRotateEnabled ? 'control-chip--active' : 'control-chip--secondary'}} {{lockAutoRotate ? 'control-chip--disabled' : ''}}" bindtap="handleSetAutoRotateEnabledOff">关闭</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__title">03. 比例尺基准点</view>
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">04. 指北针响应</view>
<view class="debug-section__desc">切换指针的平滑与跟手程度,影响指北针响应手感</view>
</view>
<view class="debug-section__lock {{lockCompassTuning ? 'debug-section__lock--active' : ''}}" data-key="lockCompassTuning" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockCompassTuning ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前档位</text>
<text class="info-panel__value">{{compassTuningProfileText}}{{lockCompassTuning ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row control-row--triple">
<view class="control-chip {{compassTuningProfile === 'smooth' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockCompassTuning ? 'control-chip--disabled' : ''}}" bindtap="handleSetCompassTuningSmooth">顺滑</view>
<view class="control-chip {{compassTuningProfile === 'balanced' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockCompassTuning ? 'control-chip--disabled' : ''}}" bindtap="handleSetCompassTuningBalanced">平衡</view>
<view class="control-chip {{compassTuningProfile === 'responsive' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockCompassTuning ? 'control-chip--disabled' : ''}}" bindtap="handleSetCompassTuningResponsive">跟手</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">05. 比例尺显示</view>
<view class="debug-section__desc">控制比例尺显示与否,默认沿用你的本地偏好</view>
</view>
<view class="debug-section__lock {{lockScaleRulerVisible ? 'debug-section__lock--active' : ''}}" data-key="lockScaleRulerVisible" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockScaleRulerVisible ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前状态</text>
<text class="info-panel__value">{{showCenterScaleRuler ? '显示' : '隐藏'}}{{lockScaleRulerVisible ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{showCenterScaleRuler ? 'control-chip--active' : 'control-chip--secondary'}} {{lockScaleRulerVisible ? 'control-chip--disabled' : ''}}" bindtap="handleSetCenterScaleRulerVisibleOn">显示</view>
<view class="control-chip {{!showCenterScaleRuler ? 'control-chip--active' : 'control-chip--secondary'}} {{lockScaleRulerVisible ? 'control-chip--disabled' : ''}}" bindtap="handleSetCenterScaleRulerVisibleOff">隐藏</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">06. 比例尺基准点</view>
<view class="debug-section__desc">设置比例尺零点锚定位置,可跟随屏幕中心或指北针圆心</view>
</view>
<view class="debug-section__lock {{lockScaleRulerAnchor ? 'debug-section__lock--active' : ''}}" data-key="lockScaleRulerAnchor" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockScaleRulerAnchor ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前锚点</text>
<text class="info-panel__value">{{centerScaleRulerAnchorMode === 'compass-center' ? '指北针圆心' : '屏幕中心'}}</text>
<text class="info-panel__value">{{centerScaleRulerAnchorMode === 'compass-center' ? '指北针圆心' : '屏幕中心'}}{{lockScaleRulerAnchor ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{centerScaleRulerAnchorMode === 'screen-center' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCenterScaleRulerAnchorScreenCenter">屏幕中心</view>
<view class="control-chip {{centerScaleRulerAnchorMode === 'compass-center' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCenterScaleRulerAnchorCompassCenter">指北针圆心</view>
<view class="control-chip {{centerScaleRulerAnchorMode === 'screen-center' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockScaleRulerAnchor ? 'control-chip--disabled' : ''}}" bindtap="handleSetCenterScaleRulerAnchorScreenCenter">屏幕中心</view>
<view class="control-chip {{centerScaleRulerAnchorMode === 'compass-center' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockScaleRulerAnchor ? 'control-chip--disabled' : ''}}" bindtap="handleSetCenterScaleRulerAnchorCompassCenter">指北针圆心</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__title">04. 北参考</view>
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">07. 北参考</view>
<view class="debug-section__desc">切换磁北/真北作为地图与指北针参考</view>
</view>
<view class="debug-section__lock {{lockNorthReference ? 'debug-section__lock--active' : ''}}" data-key="lockNorthReference" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockNorthReference ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="info-panel__row">
<text class="info-panel__label">当前参考</text>
<text class="info-panel__value">{{northReferenceText}}</text>
<text class="info-panel__value">{{northReferenceText}}{{lockNorthReference ? ' · 已锁定' : ' · 可编辑'}}</text>
</view>
<view class="control-row">
<view class="control-chip {{northReferenceMode === 'magnetic' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetNorthReferenceMagnetic">磁北</view>
<view class="control-chip {{northReferenceMode === 'true' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetNorthReferenceTrue">真北</view>
<view class="control-chip {{northReferenceMode === 'magnetic' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockNorthReference ? 'control-chip--disabled' : ''}}" bindtap="handleSetNorthReferenceMagnetic">磁北</view>
<view class="control-chip {{northReferenceMode === 'true' ? 'control-chip--active' : 'control-chip--secondary'}} {{lockNorthReference ? 'control-chip--disabled' : ''}}" bindtap="handleSetNorthReferenceTrue">真北</view>
</view>
</view>
<view class="debug-section debug-section--info">
<view class="debug-section__header">
<view class="debug-section__title">05. 心率设备</view>
<view class="debug-section__header-row">
<view class="debug-section__header-main">
<view class="debug-section__title">08. 心率设备</view>
<view class="debug-section__desc">清除已记住的首选心率带设备,下次重新选择</view>
</view>
<view class="debug-section__lock {{lockHeartRateDevice ? 'debug-section__lock--active' : ''}}" data-key="lockHeartRateDevice" bindtap="handleToggleSettingLock">
<text class="debug-section__lock-text">{{lockHeartRateDevice ? '已锁' : '可改'}}</text>
</view>
</view>
</view>
<view class="control-row">
<view class="control-chip control-chip--secondary" bindtap="handleClearPreferredHeartRateDevice">清除首选设备</view>
<view class="control-chip control-chip--secondary {{lockHeartRateDevice ? 'control-chip--disabled' : ''}}" bindtap="handleClearPreferredHeartRateDevice">清除首选设备</view>
</view>
</view>
</scroll-view>
@@ -429,6 +525,7 @@
<view class="debug-section__title">Sensors</view>
<view class="debug-section__desc">定位、罗盘与心率带连接状态</view>
</view>
<view class="debug-group-title">定位</view>
<view class="info-panel__row">
<text class="info-panel__label">GPS</text>
<text class="info-panel__value">{{gpsTrackingText}}</text>
@@ -469,6 +566,7 @@
<text class="info-panel__label">Mock Speed</text>
<text class="info-panel__value">{{mockSpeedText}}</text>
</view>
<view class="debug-group-title">心率</view>
<view class="info-panel__row">
<text class="info-panel__label">Heart Rate</text>
<text class="info-panel__value">{{heartRateStatusText}}</text>
@@ -532,6 +630,7 @@
<text class="info-panel__label">Mock BPM</text>
<text class="info-panel__value">{{mockHeartRateText}}</text>
</view>
<view class="debug-group-title">方向</view>
<view class="info-panel__row">
<text class="info-panel__label">Heading Mode</text>
<text class="info-panel__value">{{orientationModeText}}</text>
@@ -560,11 +659,6 @@
<text class="info-panel__label">Compass Tune</text>
<text class="info-panel__value">{{compassTuningProfileText}}</text>
</view>
<view class="control-row">
<view class="control-chip {{compassTuningProfile === 'smooth' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCompassTuningSmooth">顺滑</view>
<view class="control-chip {{compassTuningProfile === 'balanced' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCompassTuningBalanced">平衡</view>
<view class="control-chip {{compassTuningProfile === 'responsive' ? 'control-chip--active' : 'control-chip--secondary'}}" bindtap="handleSetCompassTuningResponsive">跟手</view>
</view>
<view class="info-panel__row info-panel__row--stack">
<text class="info-panel__label">Accel</text>
<text class="info-panel__value">{{accelerometerText}}</text>

View File

@@ -305,10 +305,17 @@
.map-side-toggle {
position: absolute;
left: 24rpx;
z-index: 19;
}
.map-side-toggle--left {
left: 24rpx;
}
.map-side-toggle--right {
right: 24rpx;
}
.map-side-column {
position: absolute;
display: flex;
@@ -322,6 +329,10 @@
left: 24rpx;
}
.map-side-column--right-group {
right: 24rpx;
}
.map-side-column--left-group {
padding-top: 106rpx;
}
@@ -1417,6 +1428,18 @@
margin-bottom: 12rpx;
}
.debug-section__header-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16rpx;
}
.debug-section__header-main {
flex: 1;
min-width: 0;
}
.debug-section__title {
font-size: 24rpx;
line-height: 1.2;
@@ -1433,6 +1456,36 @@
color: #6a826f;
}
.debug-section__lock {
min-width: 76rpx;
height: 52rpx;
padding: 0 16rpx;
flex: none;
display: flex;
align-items: center;
justify-content: center;
border-radius: 999rpx;
background: rgba(233, 242, 228, 0.92);
box-shadow: inset 0 0 0 1rpx rgba(22, 48, 32, 0.08);
}
.debug-section__lock--active {
background: rgba(45, 106, 79, 0.18);
box-shadow: inset 0 0 0 2rpx rgba(45, 106, 79, 0.18);
}
.debug-section__lock-text {
font-size: 20rpx;
line-height: 1;
font-weight: 700;
color: #45624b;
letter-spacing: 1rpx;
}
.debug-section__lock--active .debug-section__lock-text {
color: #2d6a4f;
}
.info-panel__row {
display: flex;
align-items: flex-start;
@@ -1593,6 +1646,16 @@
.debug-section .control-row:last-child {
margin-bottom: 0;
}
.debug-group-title {
margin-top: 18rpx;
margin-bottom: 8rpx;
font-size: 20rpx;
font-weight: 800;
letter-spacing: 2rpx;
color: #6a826f;
text-transform: uppercase;
}
.control-row--triple .control-chip {
font-size: 23rpx;
}
@@ -1624,6 +1687,10 @@
color: #f7fbf2;
}
.control-chip--disabled {
opacity: 0.48;
}

View File

@@ -13,7 +13,7 @@
},
"coverView": false,
"postcss": false,
"minified": false,
"minified": true,
"enhance": false,
"showShadowRootInWxmlPanel": false,
"packNpmRelationList": [],