必须先检查navigator.xr是否存在且支持目标模式,再创建xrCompatible canvas并由用户手势触发requestSession,否则会黑屏或报错。

navigator.xr 不是所有浏览器都默认可用,也不是所有设备都能直接进 immersive-vr 模式。想用 HTML 快速跑通 WebXR,核心不是写多炫的 3D 场景,而是先让会话能建起来、画面能分屏、用户点击后不报错。
检查 navigator.xr 是否存在且支持目标模式
很多开发者一上来就调 navigator.xr.requestSession('immersive-vr'),结果在 Safari 或旧版 Chrome 里直接抛 TypeError: navigator.xr is undefined。必须先判空再判支持:
-
navigator.xr本身可能为undefined(如 Safari 17.4 之前完全不支持) - 即使存在,
isSessionSupported('immersive-ar')在 Android Chrome 上可能返回false,但'immersive-vr'可能为true(取决于是否连了头显) - 桌面端 Chrome 默认只支持
'immersive-vr'(需手动启用chrome://flags/#webxr中的实验性标志)
创建 canvas 时必须传 {xrCompatible: true}
WebGL 渲染器如果没声明兼容 XR,后续绑定 XRWebGLLayer 会静默失败——画面黑屏、无报错、session.renderState.baseLayer 为 null。这不是 Three.js 的锅,是底层 WebGLContext 初始化遗漏:
- 用原生 WebGL:创建 context 时必须加
{xrCompatible: true},否则new XRWebGLLayer(session, gl)会 throwInvalidStateError - 用 Three.js:
renderer = new THREE.WebGLRenderer({ xrCompatible: true }),且不能复用已有 renderer 实例(需新造) - 注意:
canvas.width/canvas.height应设为设备像素比缩放后的尺寸,否则左右眼画面模糊
会话必须由用户手势触发,且不能跨帧延迟
requestSession 被浏览器策略严格限制:它必须是用户点击、触摸等“明确交互”的直接回调,不能包在 setTimeout、Promise.then 或 requestAnimationFrame 里。常见翻车点:
- 按钮绑了
onclick="startXR()",但startXR里又套了个await delay(100)→ 直接拒绝 - 用
addEventListener('click', async () => { ... })是安全的;但若中间有 await 非用户事件(比如先 fetch 配置),也大概率被拦截 - 移动端需额外处理 touch 事件:iOS Safari 对
touchend支持不稳定,建议同时监听click和touchstart
XRReferenceSpace 类型选错会导致模型“飘”或“贴地失败”
AR 场景里最常踩的坑是用了 'viewer' 空间却想放物体到地面——viewer 原点在用户眼睛,没有重力对齐;而 'local-floor' 才会自动把 Y=0 对齐到检测出的地面平面:
-
'local':原点在用户初始站立位置,Y 轴向上,适合室内固定空间 VR -
'local-floor':Y=0 强制对齐地面(需 AR 设备支持平面检测),AR 放置必备 -
'unbounded':理论上无限大空间,但目前仅少数设备(如 Quest 浏览器)支持,Chrome for Android 不认 - 调用
session.requestReferenceSpace()后,务必等 Promise resolve 再开始渲染,否则getViewerPose()返回null
WebXR 的麻烦不在 API 多,而在每个环节都有隐式依赖:canvas 兼容性、手势时机、参考空间类型、设备能力反馈顺序——漏掉任意一环,页面就卡在白屏或黑屏,控制台还不报明显错误。真正上线前,至少要在三类环境实测:Android Chrome(AR)、Windows Chrome + Rift(VR)、iOS Safari(基本不支持,得降级)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











