完善文创展示控制与结果层基础

This commit is contained in:
2026-03-27 09:41:48 +08:00
parent 33edfba392
commit 0e025c3426
14 changed files with 1153 additions and 38 deletions

View File

@@ -2,6 +2,7 @@ import {
MapEngine,
type MapEngineGameInfoRow,
type MapEngineGameInfoSnapshot,
type MapEngineResultSnapshot,
type MapEngineStageRect,
type MapEngineViewState,
} from '../../engine/map/mapEngine'
@@ -64,6 +65,7 @@ type StoredUserSettings = {
type MapPageData = MapEngineViewState & {
showDebugPanel: boolean
showGameInfoPanel: boolean
showResultScene: boolean
showSystemSettingsPanel: boolean
showCenterScaleRuler: boolean
showPunchHintBanner: boolean
@@ -78,6 +80,11 @@ type MapPageData = MapEngineViewState & {
gameInfoSubtitle: string
gameInfoLocalRows: MapEngineGameInfoRow[]
gameInfoGlobalRows: MapEngineGameInfoRow[]
resultSceneTitle: string
resultSceneSubtitle: string
resultSceneHeroLabel: string
resultSceneHeroValue: string
resultSceneRows: MapEngineGameInfoRow[]
panelTimerText: string
panelMileageText: string
panelDistanceValueText: string
@@ -121,7 +128,7 @@ type MapPageData = MapEngineViewState & {
showRightButtonGroups: boolean
showBottomDebugButton: boolean
}
const INTERNAL_BUILD_VERSION = 'map-build-283'
const INTERNAL_BUILD_VERSION = 'map-build-291'
const USER_SETTINGS_STORAGE_KEY = 'cmr_user_settings_v1'
const CLASSIC_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/classic-sequential.json'
const SCORE_O_REMOTE_GAME_CONFIG_URL = 'https://oss-mbh5.colormaprun.com/gotomars/event/score-o.json'
@@ -494,7 +501,7 @@ function buildSideButtonState(data: Pick<MapPageData, 'sideButtonMode' | 'showGa
: data.gpsLockEnabled
? 'active'
: 'default'
const sideButton4State: SideActionButtonState = data.gameSessionStatus === 'idle' ? 'default' : 'active'
const sideButton4State: SideActionButtonState = data.gameSessionStatus === 'running' ? 'active' : 'muted'
const sideButton11State: SideActionButtonState = data.showGameInfoPanel ? 'active' : 'default'
const sideButton12State: SideActionButtonState = data.showSystemSettingsPanel ? 'active' : 'default'
const sideButton13State: SideActionButtonState = data.showCenterScaleRuler ? 'active' : 'default'
@@ -690,10 +697,21 @@ function buildEmptyGameInfoSnapshot(): MapEngineGameInfoSnapshot {
}
}
function buildEmptyResultSceneSnapshot(): MapEngineResultSnapshot {
return {
title: '本局结果',
subtitle: '未开始',
heroLabel: '本局用时',
heroValue: '--',
rows: [],
}
}
Page({
data: {
showDebugPanel: false,
showGameInfoPanel: false,
showResultScene: false,
showSystemSettingsPanel: false,
showCenterScaleRuler: false,
statusBarHeight: 0,
@@ -714,6 +732,11 @@ Page({
gameInfoSubtitle: '未开始',
gameInfoLocalRows: [],
gameInfoGlobalRows: buildEmptyGameInfoSnapshot().globalRows,
resultSceneTitle: '本局结果',
resultSceneSubtitle: '未开始',
resultSceneHeroLabel: '本局用时',
resultSceneHeroValue: '--',
resultSceneRows: buildEmptyResultSceneSnapshot().rows,
panelTimerText: '00:00:00',
panelMileageText: '0m',
panelActionTagText: '目标',
@@ -942,6 +965,22 @@ Page({
}
}
if (typeof nextPatch.gameSessionStatus === 'string') {
if (
nextPatch.gameSessionStatus !== this.data.gameSessionStatus
&& (nextPatch.gameSessionStatus === 'finished' || nextPatch.gameSessionStatus === 'failed')
) {
this.syncResultSceneSnapshot()
nextData.showResultScene = true
nextData.showDebugPanel = false
nextData.showGameInfoPanel = false
nextData.showSystemSettingsPanel = false
clearGameInfoPanelSyncTimer()
} else if (nextPatch.gameSessionStatus === 'running' || nextPatch.gameSessionStatus === 'idle') {
nextData.showResultScene = false
}
}
if (Object.keys(nextData).length || Object.keys(derivedPatch).length) {
this.setData({
...nextData,
@@ -1341,6 +1380,16 @@ Page({
}
},
handleConnectAllMockSources() {
if (!mapEngine) {
return
}
mapEngine.handleConnectMockLocationBridge()
mapEngine.handleSetMockLocationMode()
mapEngine.handleSetMockHeartRateMode()
mapEngine.handleConnectMockHeartRateBridge()
},
handleMockBridgeUrlInput(event: WechatMiniprogram.Input) {
this.setData({
mockBridgeUrlDraft: event.detail.value,
@@ -1485,7 +1534,7 @@ Page({
},
handleForceExitGame() {
if (!mapEngine || this.data.gameSessionStatus === 'idle') {
if (!mapEngine || this.data.gameSessionStatus !== 'running') {
return
}
@@ -1555,6 +1604,21 @@ Page({
})
},
syncResultSceneSnapshot() {
if (!mapEngine) {
return
}
const snapshot = mapEngine.getResultSceneSnapshot()
this.setData({
resultSceneTitle: snapshot.title,
resultSceneSubtitle: snapshot.subtitle,
resultSceneHeroLabel: snapshot.heroLabel,
resultSceneHeroValue: snapshot.heroValue,
resultSceneRows: snapshot.rows,
})
},
scheduleGameInfoPanelSnapshotSync() {
if (!this.data.showGameInfoPanel) {
clearGameInfoPanelSyncTimer()
@@ -1614,6 +1678,27 @@ Page({
handleGameInfoPanelTap() {},
handleResultSceneTap() {},
handleCloseResultScene() {
this.setData({
showResultScene: false,
})
},
handleRestartFromResult() {
if (!mapEngine) {
return
}
this.setData({
showResultScene: false,
}, () => {
if (mapEngine) {
mapEngine.handleStartGame()
}
})
},
handleOpenSystemSettingsPanel() {
clearGameInfoPanelSyncTimer()
this.setData({

View File

@@ -36,7 +36,7 @@
</view>
<view class="map-stage__overlay-center-layer" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel}}">
<view class="map-stage__overlay-center-layer" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !showSystemSettingsPanel}}">
<view class="center-scale-ruler" wx:if="{{centerScaleRulerVisible}}" style="left: {{centerScaleRulerCenterXPx}}px; top: {{centerScaleRulerZeroYPx}}px; height: {{centerScaleRulerHeightPx}}px;">
<view class="center-scale-ruler__axis" style="bottom: {{centerScaleRulerAxisBottomPx}}px;"></view>
<view class="center-scale-ruler__arrow"></view>
@@ -46,7 +46,7 @@
<view wx:for="{{centerScaleRulerMajorMarks}}" wx:key="key" class="center-scale-ruler__label" style="top: {{item.topPx}}px;">{{item.label}}</view>
</view>
</view>
<view class="map-stage__overlay">
<view class="map-stage__overlay" wx:if="{{!showResultScene}}">
<view class="map-stage__bottom">
<view class="compass-widget">
<view class="compass-widget__heading-wrap">
@@ -80,18 +80,18 @@
</view>
</view>
<view class="game-punch-hint" wx:if="{{showPunchHintBanner && punchHintText}}" style="top: {{topInsetHeight}}px;" catchtouchstart="handlePunchHintTap" catchtouchmove="handlePunchHintTap" catchtouchend="handlePunchHintTap">
<view class="game-punch-hint" wx:if="{{!showResultScene && showPunchHintBanner && punchHintText}}" style="top: {{topInsetHeight}}px;" catchtouchstart="handlePunchHintTap" catchtouchmove="handlePunchHintTap" catchtouchend="handlePunchHintTap">
<view class="game-punch-hint__text">{{punchHintText}}</view>
<view class="game-punch-hint__close" catchtouchstart="handlePunchHintTap" catchtouchmove="handlePunchHintTap" catchtouchend="handlePunchHintTap" catchtap="handleClosePunchHint">×</view>
</view>
<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-toggle {{sideButtonPlacement === 'right' ? 'map-side-toggle--right' : 'map-side-toggle--left'}}" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !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 {{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-column {{sideButtonPlacement === 'right' ? 'map-side-column--right-group' : 'map-side-column--left'}} map-side-column--left-group" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !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="{{sideButton2Class}}" bindtap="handleToggleGpsLock">
<cover-image
@@ -111,15 +111,15 @@
<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">
<cover-view class="map-punch-button {{punchButtonEnabled ? 'map-punch-button--active' : ''}} {{punchButtonFxClass}}" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !showSystemSettingsPanel}}" bindtap="handlePunchAction">
<cover-view class="map-punch-button__text">{{punchButtonText}}</cover-view>
</cover-view>
<cover-view class="screen-button-layer screen-button-layer--start-left" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showBottomDebugButton && gameSessionStatus === 'idle'}}" bindtap="handleStartGame">
<cover-view class="screen-button-layer screen-button-layer--start-left" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !showSystemSettingsPanel && showBottomDebugButton && gameSessionStatus !== 'running'}}" bindtap="handleStartGame">
<cover-view class="screen-button-layer__text screen-button-layer__text--start">开始</cover-view>
</cover-view>
<cover-view class="screen-button-layer screen-button-layer--bottom-left" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel && showBottomDebugButton}}" bindtap="handleToggleDebugPanel">
<cover-view class="screen-button-layer screen-button-layer--bottom-left" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !showSystemSettingsPanel && showBottomDebugButton}}" bindtap="handleToggleDebugPanel">
<cover-view class="screen-button-layer__icon">
<cover-view class="screen-button-layer__line"></cover-view>
<cover-view class="screen-button-layer__stand"></cover-view>
@@ -127,7 +127,7 @@
<cover-view class="screen-button-layer__text">调试</cover-view>
</cover-view>
<swiper wx:if="{{!showGameInfoPanel && !showSystemSettingsPanel}}" class="race-panel-swiper" current="{{hudPanelIndex}}" bindchange="handleHudPanelChange" duration="220" easing-function="easeOutCubic">
<swiper wx:if="{{!showGameInfoPanel && !showResultScene && !showSystemSettingsPanel}}" class="race-panel-swiper" current="{{hudPanelIndex}}" bindchange="handleHudPanelChange" duration="220" easing-function="easeOutCubic">
<swiper-item>
<view class="race-panel race-panel--tone-{{panelTelemetryTone}}">
<view class="race-panel__tag race-panel__tag--top-left">{{panelActionTagText}}</view>
@@ -232,7 +232,7 @@
</view>
</swiper-item>
</swiper>
<view class="race-panel-pager" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showSystemSettingsPanel}}">
<view class="race-panel-pager" wx:if="{{!showDebugPanel && !showGameInfoPanel && !showResultScene && !showSystemSettingsPanel}}">
<view class="race-panel-pager__dot {{hudPanelIndex === 0 ? 'race-panel-pager__dot--active' : ''}}"></view>
<view class="race-panel-pager__dot {{hudPanelIndex === 1 ? 'race-panel-pager__dot--active' : ''}}"></view>
</view>
@@ -276,6 +276,31 @@
</view>
</view>
<view class="result-scene-modal" wx:if="{{showResultScene}}" bindtap="handleCloseResultScene">
<view class="result-scene-modal__dialog" catchtap="handleResultSceneTap">
<view class="result-scene-modal__eyebrow">RESULT</view>
<view class="result-scene-modal__title">{{resultSceneTitle}}</view>
<view class="result-scene-modal__subtitle">{{resultSceneSubtitle}}</view>
<view class="result-scene-modal__hero">
<view class="result-scene-modal__hero-label">{{resultSceneHeroLabel}}</view>
<view class="result-scene-modal__hero-value">{{resultSceneHeroValue}}</view>
</view>
<view class="result-scene-modal__rows">
<view class="result-scene-modal__row" wx:for="{{resultSceneRows}}" wx:key="label">
<text class="result-scene-modal__row-label">{{item.label}}</text>
<text class="result-scene-modal__row-value">{{item.value}}</text>
</view>
</view>
<view class="result-scene-modal__actions">
<view class="result-scene-modal__action result-scene-modal__action--secondary" bindtap="handleCloseResultScene">返回地图</view>
<view class="result-scene-modal__action result-scene-modal__action--primary" bindtap="handleRestartFromResult">再来一局</view>
</view>
</view>
</view>
<view class="game-info-modal" wx:if="{{showSystemSettingsPanel}}" bindtap="handleCloseSystemSettingsPanel">
<view class="game-info-modal__dialog" catchtap="handleSystemSettingsPanelTap">
<view class="game-info-modal__header">
@@ -525,6 +550,9 @@
<view class="debug-section__title">Sensors</view>
<view class="debug-section__desc">定位、罗盘与心率带连接状态</view>
</view>
<view class="control-row">
<view class="control-chip control-chip--primary" bindtap="handleConnectAllMockSources">一键连接模拟源</view>
</view>
<view class="debug-group-title">定位</view>
<view class="info-panel__row">
<text class="info-panel__label">GPS</text>

View File

@@ -1327,6 +1327,130 @@
box-sizing: border-box;
}
.result-scene-modal {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 0 28rpx;
box-sizing: border-box;
background: rgba(7, 18, 12, 0.38);
z-index: 32;
}
.result-scene-modal__dialog {
width: 100%;
max-width: 680rpx;
padding: 36rpx 32rpx 30rpx;
border-radius: 40rpx;
background: rgba(248, 251, 244, 0.98);
box-shadow: 0 22rpx 68rpx rgba(7, 18, 12, 0.24);
box-sizing: border-box;
}
.result-scene-modal__eyebrow {
font-size: 22rpx;
font-weight: 800;
letter-spacing: 4rpx;
color: #5f7a65;
line-height: 1;
}
.result-scene-modal__title {
margin-top: 14rpx;
font-size: 46rpx;
line-height: 1.08;
font-weight: 700;
color: #163020;
}
.result-scene-modal__subtitle {
margin-top: 12rpx;
font-size: 24rpx;
line-height: 1.35;
color: #5f7a65;
}
.result-scene-modal__hero {
margin-top: 28rpx;
padding: 26rpx 24rpx 22rpx;
border-radius: 28rpx;
background: linear-gradient(180deg, rgba(35, 135, 87, 0.12), rgba(35, 135, 87, 0.06));
}
.result-scene-modal__hero-label {
font-size: 22rpx;
font-weight: 700;
color: #4d6852;
}
.result-scene-modal__hero-value {
margin-top: 12rpx;
font-size: 68rpx;
line-height: 1;
font-weight: 800;
color: #163020;
}
.result-scene-modal__rows {
margin-top: 24rpx;
border-radius: 28rpx;
overflow: hidden;
background: rgba(22, 48, 32, 0.04);
}
.result-scene-modal__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
padding: 22rpx 24rpx;
border-bottom: 1rpx solid rgba(22, 48, 32, 0.08);
}
.result-scene-modal__row:last-child {
border-bottom: none;
}
.result-scene-modal__row-label {
font-size: 24rpx;
color: #5f7a65;
}
.result-scene-modal__row-value {
font-size: 26rpx;
font-weight: 700;
color: #163020;
text-align: right;
}
.result-scene-modal__actions {
margin-top: 28rpx;
display: flex;
align-items: center;
gap: 18rpx;
}
.result-scene-modal__action {
flex: 1;
padding: 24rpx 18rpx;
border-radius: 999rpx;
text-align: center;
font-size: 26rpx;
font-weight: 700;
}
.result-scene-modal__action--secondary {
background: rgba(22, 48, 32, 0.08);
color: #163020;
}
.result-scene-modal__action--primary {
background: #163020;
color: #f7fbf2;
}
.debug-section--info {
margin-top: 14rpx;
}