JavaScript 步行导航:可运行页面与接入详解
本章提供独立 HTML + JavaScript 案例,以及地图选点、真实定位、规划 / 开始分离、偏航、到达、语音和清理流程。使用 load() 返回的 sdk.WalkingNavi,不直接实例化需要内部 Client 的底层导出类。
下载完整步行案例源码 · Android 步行教程 · JavaScript 导航 API
1. 两个名字相近的类,功能不同
| 类 | 用途 | 返回 / 行为 |
|---|---|---|
sdk.Walking |
查询步行路线 | search() 返回含 routes 的规划结果 |
sdk.WalkingNavi |
前台真实步行导航 | calculateWalkRoute() 返回一条 Route,开始后订阅浏览器定位并输出进度 |
sdk.Navi |
驾车导航 | 与 WalkingNavi 的开始签名、回调字段不同 |
sdk.Polyline |
显示折线 | 不会自动产生导航、定位或语音 |
WalkingNavi 不内置地图页面;宿主需要把规划结果的 route.path 画到地图。它的 startNavi() 无参数;不是驾车 startNavi(fix),也不接受 Android 的 GPSNaviMode。不要把 Walking.search() 的整个结果传给开始方法。
2. 运行完整网页案例
- 从下载页下载当前配套 SDK,保留
browser文件夹内 JS、CSS、资源和录音目录。 - 下载本章案例 ZIP,将其解压到 SDK 包根目录,得到下面结构。源码不含真实 Key,不含 SDK 实现。
- 用 HTTP 服务打开包根目录;电脑本机可以使用
http://localhost,手机访问需要受浏览器允许的安全上下文,正式环境使用 HTTPS。file://不能作为定位测试环境。 - 打开
examples/walking/index.html,填写自己的浏览器 Key、服务地址、已授权数据集与语言,点击初始化。 - 点击“定位到我”,允许浏览器定位,再点击地图上的可达终点。按“规划步行 → 开始步行导航”操作。
SDK 解压目录/
├─ browser/
│ ├─ sukha.js
│ ├─ style.css
│ └─ … 配套资源保持原样
└─ examples/walking/
├─ index.html
├─ walking.js
└─ WalkingGuideActivity.java # Android 示例,网页不加载它
本机临时服务可在包根目录运行:
python -m http.server 8080 --bind 127.0.0.1
然后访问 http://localhost:8080/examples/walking/index.html。Key 的域名 / Origin 约束需要允许实际测试地址。手机浏览 http://电脑局域网IP 通常不具备定位要求的安全上下文,不应把这类失败当作路线接口异常。
完整案例有事件日志:可观察 calculateRouteSuccess、startNavi、reCalculateRoute、arriveDestination 及三类错误。点击“停止”、换终点、重新初始化或页面退出时均会清理旧任务;异步请求返回后会检查它是否仍属于当前页面和行程。
3. 最小调用链与音频解锁
先完成 load(),再绑定按钮。以下是核心顺序,完整源码另外包含取消和页面生命周期保护:
const sdk = await window.SukhaSDK.load({
key: 'YOUR_BROWSER_KEY',
serviceBaseUrl: 'https://map.sukha.cn',
datasetId: 'YOUR_DATASET_ID',
coordinateSystem: 'WGS84',
language: 'zh-CN',
voiceLanguage: 'zh-CN'
});
const walk = new sdk.WalkingNavi();
const geo = new sdk.Geolocation({enableHighAccuracy:true, timeout:15000, maximumAge:0});
startButton.onclick = async () => {
// 必须在用户点击的同步调用栈内,且在任何 await 之前。
walk.unlockVoice();
try {
const fix = await geo.getCurrentPosition();
// destination 为用户选择的 [经度, 纬度],与公开坐标模式一致。
const route = await walk.calculateWalkRoute(fix.position, destination);
drawRoute(route.path); // 宿主替换自己的折线。
walk.startNavi(); // 同步、无参数;由它持续订阅后续浏览器定位。
} catch (error) {
showError(error.code, error.message, error.requestId);
}
};
startButton、destination、drawRoute、showError 是宿主 UI,不是 SDK 全局变量。直接运行请使用下载案例。实际项目应禁用重复开始,并用页面 / 请求代次阻止“用户退出后异步返回又启动导航”。
为什么完整案例把“规划”和“开始”分开:用户可以先看距离和路线,再确认出发。规划按钮不自动开始,开始按钮在点击栈先解锁语音,再检查新定位与原规划起点。不要先 await 定位或算路,再调用 unlockVoice()。
4. 返回值、坐标和时间
JavaScript 坐标数组始终是 [经度, 纬度]。例如 [100.02228,21.67623];不要照搬 Android LatLonPoint(纬度,经度) 的顺序。
calculateWalkRoute resolve 的是 一条路线:
| 字段 | 含义 |
|---|---|
route.path |
路线点,可传给 Polyline |
route.distance |
规划总距离,米 |
route.time |
规划预计耗时,秒 |
route.steps |
步行提示和路线点索引;不要假设与驾车完整导航路线结构一致 |
route.travelMode |
步行路线应为 walking |
route.guidanceMode |
步行控制器使用包含几何引导信息的路线 |
route.datasetVersion |
用于联调记录当前路线所用数据版本 |
初始化为 WGS84 时,选点、规划、自己注入的 Fix 都使用 WGS84;初始化为 GCJ02 时使用对应公开模式。SDK 浏览器定位会处理浏览器原始 WGS84 到公开模式的转换,不要二次转换。
手动定位注入的完整格式:
const fix = {
position: [longitude, latitude], // 与公开坐标模式一致
accuracy: accuracyMeters, // 米,保留真实精度
timestamp: originalTimestamp, // Unix 毫秒,原始采样时间
speed: speedMetersPerSecond, // 米/秒;未知传 null
heading: headingDegrees // 角度;未知传 null
};
walk.updateLocation(fix);
这个接口只在导航已开始时处理。当前 WalkingNavi.startNavi 本身会订阅浏览器定位,公开接口没有“外部定位独占模式”选项。普通网页直接使用内置定位即可,不额外建第二个 watch 再重复喂入;混合 App 若需要独占原生定位,不要假设注入接口会禁用浏览器定位,先确认所需集成方式。
不要以 Date.now() 替换旧采样时间,不要把失败位置写成 [0,0],不要给未知精度填 0。案例的开始前检查要求位置不超过 8 秒、精度不大于 35 米;规划起点移动超过 30 米时要求重算,这是案例策略,不是新的 SDK 配置项。
5. 事件名称和真实载荷
| 事件 | 载荷 | 推荐行为 |
|---|---|---|
calculateRouteSuccess |
{route, rerouted?} |
首次与重算都会触发;删除旧 Polyline,再绘制新路线 |
startNavi |
无需读取业务字段 | 显示导航中 |
naviInfoUpdate |
WalkingUpdate 字段直接在对象上 | 更新剩余米数和有效引导 |
reCalculateRoute |
无需读取业务字段 | 只展示正在重算,SDK 已发起请求 |
arriveDestination |
无需读取业务字段 | 展示到达,不自动完成业务订单 |
locationError |
{error} |
定位权限、超时等;提示排查定位 |
voiceError |
{error} |
解锁 / 录音 / 播放问题;与路线错误分开 |
error |
{error} |
自动重算失败等异步错误 |
offlinePreparation |
离线准备状态 | 显示语音准备进度 |
speechComplete |
{text, source} |
录音提示播放完成,可做调试记录 |
首次规划失败通过 calculateWalkRoute() 的 Promise rejection 处理,不要只监听 error 而漏掉 try/catch。事件大小写是 reCalculateRoute,不是 recalculateRoute。
walk.on('naviInfoUpdate', u => {
const text = !u.reliable ? '等待可靠定位'
: u.offRoute ? '已偏离路线'
: u.cue?.instruction || '沿路线前行';
status.textContent = `${text} · 剩余 ${Math.round(u.remainingMeters)} 米`;
});
WalkingUpdate 包含 accepted、reliable、offRoute、arrived、requestReroute、progressMeters、remainingMeters、deviationMeters、可空的 cue。cue 包含 id、instruction、geometryIndex。cue 为空是正常状态,不代表导航失败。
步行没有驾车 info.instruction、remainingSeconds、repeat()、locationChange、wakeLockUnavailable 的完整同名合同。不要复制驾车 HUD 后假定这些字段或事件都存在。语音由 SDK 安排,不要在每次更新时另外播放一遍。
6. 四个可复用案例
案例 A:地点卡片直接出发
传入地点 ID / 名称和公开模式终点坐标。用户点击“步行”时同步 unlockVoice,再取真实定位、规划、显示路线并开始。规划期间锁住开始按钮,换终点时 stopNavi() 并使旧请求代次失效。下载案例的 plan、start、stop 可直接参考。
案例 B:先预览,准备录音后离线出发
规划后先显示 distance / time;读取 getOfflineStatus() 或监听 offlinePreparation。如需明确等待,调用并 await prepareOfflineNavigation()。准备完成后点击开始仍需新鲜定位和音频解锁。
| 状态字段 | 意义 |
|---|---|
routeReady |
当前控制器已有路线,不代表能离线计算新路线 |
voiceState |
idle / preparing / ready / incomplete |
voiceCompleted / voiceTotal |
已完成与总录音数量 |
offlineRerouting |
当前为 false |
backgroundNavigation |
当前为 false |
incomplete 要提示恢复网络后重试,不能显示“离线就绪”。承诺范围是当前页面、已准备的路线与语音;不要把它扩展成刷新页面后自动恢复、离线偏航重算或永久缓存保证。
案例 C:偏航与断网重算
SDK 根据连续有效定位判断偏航并触发重算。UI 显示重算状态,成功后替换折线;不要再次 startNavi。重算失败监听 error,保留明确的失败提示,让用户恢复网络或停止后重新规划,不伪装已经有新路线。
案例 D:离开页面、返回与到达
完整案例在 visibilitychange 进入后台时主动 stop,返回后要求重新规划;在 pagehide 销毁控制器、定位服务、覆盖物和地图。若从浏览器往返缓存恢复,页面提示重新初始化。SPA 在组件卸载时执行相同清理,并移除自己注册的 DOM 监听。
SDK 也会在页面隐藏时取消当前播报,但这不代表浏览器提供后台连续导航。到达事件后不要立刻 stopNavi() 截断刚开始的到达录音;用户关闭页面时仍需正常销毁。
7. 常见错误与处理
| 现象 / 错误 | 原因方向 | 处理 |
|---|---|---|
| 初始化授权失败 | Key 类型 / Origin / 数据集不匹配 | 使用浏览器 Key,核对实际协议、域名、端口和授权 |
NO_ROUTE |
尚未成功规划就开始 / 准备语音 | 用 READY 状态控制按钮 |
CANCELLED |
换终点、停止或新请求取消旧请求 | 旧请求不再覆盖 UI,一般不弹失败框 |
DESTROYED |
卸载后仍持有旧对象 | 新页面创建新实例,不复用已销毁控制器 |
| 无定位弹窗 / 定位失败 | 非安全上下文、系统定位关闭、浏览器拒绝 | HTTPS / localhost,检查站点和系统权限 |
| 有路线无进度 | 未 start / 位置过旧 / 精度不足 / 页面后台 | 记录真实定位与 reliable,保持前台 |
| 没有声音 | await 后才 unlock、媒体限制、资源错误或静音 | 点击栈先解锁,检查 voiceError、资源请求和设备音量 |
| 路线越画越多 | 重算时未销毁旧折线 | 每个行程只保留一个路线图层引用 |
| 已退出却又导航 | 异步操作没有页面和请求代次检查 | 像示例一样用 active(session) 与 run 检查 |
| 与真实位置明显偏移 | 纬经度顺序或坐标模式错误 | JS 经度在前,保持公开模式一致 |
| 步行剩余分钟 undefined | 使用了驾车更新字段 | 读取规划 route.time,实时更新只承诺文档列出的字段 |
排错日志建议保留时间、事件名、错误 code / requestId、规划的 datasetVersion、当前位置精度和采样年龄;不要把 Key、完整用户轨迹等直接写进公共日志。
8. 上线前验收
分别验证初始化失败、首次拒绝权限、恢复权限、弱定位、规划时改终点、规划时离开页面、开始前已移动、连续点击、重算成功 / 失败、语音解锁与静音、切后台、刷新、浏览器后退再返回、到达后退出。再用授权数据集中的真实道路做户外步行验收。
完整 walking.js 在文档构建时对配套 ZIP 的 TypeScript 声明做类型检查;不要求升级 JavaScript SDK。接口检查不等于各浏览器的定位、录音与后台行为验收。