⚠️ 已废弃(2026-09-02):插帧思路治标不治本——根因是 setHeading/setTilt 非公开 API 内部默认 500ms 缓动动画,JS 层插帧是在跟 SDK 自带动画打架。最终方案是 {noAnimation: true} 瞬转。详见 BMapGL 视角跟随 setHeading 内部动画与 noAnimation 瞬转 (BMapGL View-Follow setHeading noAnimation Snap)。本页保留作失败档案。

问题与现象

在 Android WebView 中,指南针二维 UI 可以立即响应设备转向,但 BMapGL Canvas/WebGL 地图的视角跟随会出现延迟、卡帧或旋转动画被打断。常见表现是设备已经转过约 90°,地图仍在追赶旧角度。

条件与根因

  • 学校壳的 CampusCompassBridge 使用旋转矢量传感器,圆周滤波后约每 80ms 推送一次 window.CampusNavigation.receiveNativeHeading(json)。
  • 页面若在每次回调中直接调用 map.setHeading(target),BMapGL 可能启动或维护内部旋转动画;下一次传感器回调又改变目标,多个内部动画互相抢占,表现为卡帧和滞后。
  • 这不是 JS 桥“注册方法有延迟”的证据,更可能是地图渲染提交与 SDK 内部动画的竞争。

解决方案

在“视角跟随”模式中,将最新目标角度交给一个可抢占的 requestAnimationFrame 插帧器:

  1. 使用最短圆周角差展开 0/360° 边界,避免反向大旋转。
  2. 每次转向用 96–144ms 完成,90° 约 120ms,在 60Hz 下产生约 6–8 个离散 setHeading 提交。
  3. 新传感器值到来时,从当前已提交角度重新规划到最新目标,不排队旧目标,降低跟手延迟。
  4. 切换北向/路线模式、恢复导航视图或停止导航渲染时取消插帧,避免过期 rAF 覆盖新状态。
  5. 光标和指南针仍在收到桥回调时立即更新;只有地图相机旋转经过插帧。

验证方法

  • 自动化:frontend 目录执行 npm run test:navi,应通过导航契约、插帧边界和取消逻辑测试。
  • 构建:执行 npm run build,确认 frontend/dist/navi/app.js 同步生成;若 worktree 没有依赖,可临时复用同机已有 node_modules,不要提交 junction。
  • 真机:快速旋转 90°,观察指南针/光标是否立即改变,地图在约 0.1 秒内以 6–8 个短步完成旋转;连续转动时不应出现明显追赶或旧角度回弹。

仓库内另有独立测试页 frontend/public/test/navi-heading.html,已接入 test/gateway.html。页面右侧嵌入真实 ?dragsim=1 导航画布,左侧可发送瞬时 90°/180° 航向、连续 8 点航向,并读取 getNavigationDebugState() 的相机角度和插帧状态;本地开发地址为 /test/navi-heading.html。

适用边界

该策略只针对视角跟随的地图相机。北向上和路线朝前模式保持即时固定角度;如果设备或地图 SDK 在低端机上仍掉帧,应优先降低地图图层/覆盖物负载,而不是继续延长插帧时长。

关联

  • campus-navi-rs:frontend/public/navi/app.js
  • Android 桥:CampusCompassBridge / CompassController

本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Frontend