山海万灵 HarmonyOS 文化知识设计续篇(10):地图缩放与区域选中态协同方案 一、文化地图需要把视口和区域语义同时保留下来山海世界的地域不是普通列表项。读者在昆仑、青丘或海外三经之间切换时既需要看清地图上的空间关系也需要在进入展厅、查看神兽详情后回到此前的区域。区域选择已经承担“当前探索位置”的语义缩放能力应当只负责视口而不能覆盖这个语义。本方案将地图交互拆成两份可独立恢复的数据selectedRegionId表示读者正在探索的地域viewport表示地图画布当前的比例和偏移。前者参与展厅、导览和进度计算后者只服务于阅读视野。两个状态在同一次手势中可以一起变化但它们的来源、保存频率和失效条件不同。状态解决的问题生命周期selectedRegionId确定高亮地域、展厅入口和探索上下文跨页面保留viewport.scale控制地图阅读颗粒度当前地图会话优先viewport.offsetX/Y保留读者观察的位置经节流后保存gesturePhase防止点击与双指缩放互相抢占单次手势结束即清理ArkUI 的可观察状态应直接驱动选中样式和画布变换关于状态更新边界可参考 ArkTS 状态管理概述。二、先划清地图视口的能力边界缩放的目标是帮助读者阅读地域分布、连接线和图例不是提供无限画布也不承担地理测量。为避免小屏设备上出现“放大后找不回内容”的问题视口状态必须具备最小比例、最大比例和边界回弹。区域卡片仍是主要的无障碍选择入口手势不可用时读者可以通过卡片完成同样的地域切换。纳入范围设计处理双指缩放与双击复位将比例限制在 0.85 至 2.40复位回到默认视图单击地域写入选中地域再将视口平滑聚焦到该地域锚点返回地图先恢复有效地域再恢复合法的视口比例和偏移字体放大或窗口变化重新计算地图边界保留选中地域而非旧坐标不纳入范围原因无限制缩放会造成浮点误差、内容丢失与命中区域失真手势直接修改探索进度视口移动不代表完成探索行为以屏幕坐标持久化地域横竖屏和大屏布局变化后坐标没有语义三、用一个显式模型承接手势、选中和恢复地图页只保存渲染所需的即时状态领域层保存能够跨页面恢复的语义状态。regionId无法在最新地域集合中找到时恢复流程回退到默认地域比例或偏移非法时只重置视口不丢弃已经选择的地域。interface MapViewport { scale: number offsetX: number offsetY: number } interface MapInteractionState { selectedRegionId: string viewport: MapViewport gesturePhase: IDLE | PINCHING | PANNING } const DEFAULT_VIEWPORT: MapViewport { scale: 1, offsetX: 0, offsetY: 0 }比例和位移必须经过统一的归一化函数。这样可以避免一次异常手势把画布带到不可见区域也让恢复逻辑和实时渲染共享同一套约束。function normalizeViewport(input: MapViewport, bounds: MapBounds): MapViewport { const scale Math.max(0.85, Math.min(input.scale, 2.40)) const maxX Math.max(0, bounds.contentWidth * scale - bounds.viewportWidth) / 2 const maxY Math.max(0, bounds.contentHeight * scale - bounds.viewportHeight) / 2 return { scale, offsetX: Math.max(-maxX, Math.min(input.offsetX, maxX)), offsetY: Math.max(-maxY, Math.min(input.offsetY, maxY)) } }四、让缩放手势和区域点击有确定的优先级同一张地图里既有可点击地域又有缩放、拖拽和双击复位。事件调度需要先判断手势是否已经跨过移动阈值双指缩放或明显拖动发生后本次触点不再触发地域选择单击在阈值内结束时才交给地域命中测试。这条规则能避免读者放大地图时误入某个展厅。function reduceGesture(state: MapInteractionState, event: MapGesture): MapInteractionState { if (event.type PINCH) { return { ...state, gesturePhase: PINCHING, viewport: normalizeViewport({ scale: state.viewport.scale * event.scaleDelta, offsetX: state.viewport.offsetX event.focusDeltaX, offsetY: state.viewport.offsetY event.focusDeltaY }, event.bounds) } } if (event.type TAP state.gesturePhase IDLE event.regionId) { return { ...state, selectedRegionId: event.regionId } } return { ...state, gesturePhase: IDLE } }选中地域发生变化后页面不直接推导展厅和导览结果。它只发出领域命令由探索状态协调器校验地域与展厅的对应关系并将可恢复的选择写入本地进度。这样卡片选择、地图点击和深链进入可以复用同一个入口。五、把“选中地域”接入已有探索位置而不是另存一份影子状态已有探索位置由地域标识和展厅标识组成。地图方案继续使用这一语义主键并增加独立的视口快照。保存顺序是“选择先确认、视口后节流”区域切换必须立刻可恢复连续缩放只在手势结束或短暂静止后写入避免 Preferences 被高频写入拖慢界面。async function commitRegionSelection(regionId: string): Promisevoid { const region regionCatalog.find((item: RegionItem) item.id regionId) if (region undefined) { return } state.selectedRegionId region.id await explorationStore.savePosition({ selectedRegionId: region.id, selectedHallId: region.hallId }) } async function commitViewportAfterGesture(viewport: MapViewport): Promisevoid { const safeViewport normalizeViewport(viewport, currentBounds) await mapViewStore.saveViewport(safeViewport) }恢复时按“数据有效性优先于视觉连续性”处理。地域目录更新、窗口尺寸变化或资源包替换都可能让旧视口不再适用此时保留可识别的地域重算画布边界再使用默认比例。与轻量设置有关的持久化能力可按 Preferences 开发指导实现。六、为异常路径准备可理解的降级行为失败场景页面行为数据处理地域目录尚未加载显示骨架和不可点击地图不写入选择等目录就绪后恢复默认地域保存视口失败继续显示当前视口并提示下次可能从默认视图进入不影响已确认的地域选择旧地域已下线回退到默认地域展示可选择的替代地域清理失效地域标识大屏尺寸变化重新计算可移动边界并保留选中高亮仅覆盖超出边界的偏移手势能力不可用保留地域卡片、复位按钮和键盘焦点路径不依赖双指操作完成探索网络内容不可用时地图可以展示本地已知地域和当前选择但不能把临时视口误当作探索进度。读者能够继续浏览已加载内容待数据恢复后再同步可用地域集合。七、实施顺序从纯状态开始再接入画布手势第一步建立MapViewport、边界归一化和恢复函数并用固定输入验证比例钳制、偏移回弹和失效地域回退。第二步让地域卡片与地图命中区域共用选择命令确保两种入口产生同一份探索位置。第三步接入缩放与拖拽手势最后把节流后的视口快照写入本地存储。阶段交付物验收条件状态层视口模型、归一化函数、恢复策略非法比例与越界偏移被稳定收敛选择层地图和卡片共用的地域命令两个入口切换到同一展厅与高亮手势层缩放、拖拽、双击复位缩放时不触发误选复位可预测持久化层节流视口快照与地域位置重进地图后恢复合法的选择与视野验收动作包括选择青丘后进入展厅再返回在放大状态拖动到边界后复位旋转或切换宽窗口后确认高亮仍指向同一地域清除无效快照后确认地图回到默认视图。每个动作都应回读选中地域、比例、偏移和最终可见区域而不是只检查按钮文字。八、方案收束语义状态稳定视口状态可控地图缩放的可用性取决于它是否服从探索语义。选中地域继续作为跨页面的主状态视口作为受边界保护的阅读状态手势、卡片和恢复流程通过统一命令协作。这样的分层让后续接入多设备布局、离线目录和更复杂的地图标记时仍能保持明确的失败路径与验收边界。