Highlight
Safari on visionOS 2.0 正式支持 WebXR 沉浸式 VR 会话,网页无需 App Store 审核、无需用户下载,就能在 Vision Pro 上呈现全沉浸 3D 体验。
核心内容
做一个 Vision Pro 上的 3D 体验,传统路线是写原生 app——注册开发者账号、过审核、等用户下载。门槛高,触达慢。很多人已经有 3D 内容放在网站上(产品展示、虚拟展厅、数据可视化),但它们受限于 2D 屏幕的浏览方式,无法让用户”走进去”感受空间和比例。
Safari on visionOS 2.0 引入 WebXR 支持,改变了这件事。WebXR 是 W3C 制定的网页标准,专门用于在浏览器中启动沉浸式 XR 会话。用户点一个按钮,Safari 弹出权限确认,之后网页中的 WebGL 内容就铺满整个视野——完全沉浸,跟原生 VR app 一样的效果。退出时按数字表冠或回到主屏幕即可。
这个方案的关键优势:不经过 App Store,不需要用户安装任何东西,打开网页就能用。对已有 WebGL 内容的网站来说,增量工作很小——检测 navigator.xr 是否可用,加一个”进入 VR”按钮,调用 requestSession 切换到沉浸模式。visionOS 还专门为 WebXR 增加了 transient pointer 输入类型,映射了”注视+捏合”的自然交互,同时严格保护用户视线隐私——网站只在捏合瞬间才能获取注视方向和手部位置。
详细内容
安全与隐私机制
WebXR 的安全设计贯穿全栈。(03:27)
- HTTPS 是硬性要求:所有 WebXR 内容必须走 HTTPS,防止中间人注入。
- iFrame 需要
allow="xr-spatial-tracking":防止第三方代码(如广告)未经开发者许可启动 VR 会话。 - 用户主动触发:必须有一个按钮或其他交互元素让用户主动发起 XR 会话,不能页面加载就自动进入。
- 系统保留退出手势:数字表冠和主屏幕手势始终用于退出,开发者不能拦截。
- 视线隐私:在 visionOS 上,spatial input 只在捏合瞬间暴露注视方向,手部位置也只在捏合期间可见。
用 A-Frame 快速搭建沉浸场景
A-Frame 是基于 HTML 声明式的 WebGL 框架,适合 WebXR 入门。以下代码可以在 Vision Pro 上渲染一个带天空和地面的 3D 场景,并支持注视+捏合交互。(09:02)
<html>
<head>
<script src="https://aframe.io/releases/1.5.0/aframe.min.js"></script>
</head>
<body>
<a-scene cursor="rayOrigin: mouse; fuse: false">
<a-box position="0 1.6 -3" color="red"
animation="property: rotation; to: 0 360 0; loop: true; dur: 5000">
</a-box>
<a-sphere position="1 1.6 -3.5" color="blue"
animation="property: position; to: 1 2.0 -3.5; dir: alternate; loop: true; dur: 1000">
</a-sphere>
<a-cylinder position="-1 1.6 -3" radius="0.3" height="1" color="green"></a-cylinder>
<a-plane position="0 0 -3" rotation="-90 0 0" width="20" height="20" color="#7BC8A4"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>
</body>
</html>
关键点:
<a-scene>是 A-Frame 的根元素,自动处理 WebGL 渲染和 WebXR 会话管理。cursor组件使用 raycaster 检测注视目标,在捏合时触发click、mouseenter、mouseleave事件。- 3D 坐标的单位是米——WebXR 使用真实比例,所以
position="0 1.6 -3"表示物体在眼睛高度、前方 3 米处。 animation组件可以为物体添加关键帧动画,无需写 JavaScript。- A-Frame 内置”进入 VR”按钮,点击后自动调用
requestSession。
WebXR 生命周期
WebXR 会话的完整生命周期分四步。(10:48)
// 1. 检测支持
if (navigator.xr) {
const supported = await navigator.xr.isSessionSupported("immersive-vr");
if (supported) {
// 显示"进入 VR"按钮
}
}
// 2. 请求会话
const session = await navigator.xr.requestSession("immersive-vr", {
requiredFeatures: ["local-floor"],
optionalFeatures: ["hand-tracking"]
});
// 3. 建立渲染循环
const gl = canvas.getContext("webgl2");
const glLayer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer: glLayer });
const refSpace = await session.requestReferenceSpace("local-floor");
session.requestAnimationFrame(function onFrame(time, frame) {
const pose = frame.getViewerPose(refSpace);
if (pose) {
// 用 pose.views 渲染左右眼
}
session.requestAnimationFrame(onFrame);
});
// 4. 结束会话
session.addEventListener("end", () => {
// 重新显示"进入 VR"按钮
});
关键点:
isSessionSupported("immersive-vr")检查当前设备是否支持沉浸式 VR,返回一个布尔结果的 Promise。requestSession的第二个参数是 feature 列表:requiredFeatures中的特性如果不可用或被用户拒绝,整个请求会失败;optionalFeatures中的特性不可用时只是不提供,不会导致请求失败。尽量用 optional。local-floor参考空间把坐标系原点放在用户脚附近,适合站立体验。- WebXR 的
requestAnimationFrame挂在 session 上而非 window 上,因为 XR 设备的刷新率可能与显示器不同。 getViewerPose(refSpace)返回当前帧的观察者位姿,包含左右眼的 view 矩阵,用于 WebGL 渲染。- 会话结束时(用户按表冠或开发者调用
session.end()),触发end事件,可以在此重新显示入口按钮。
Transient Pointer:visionOS 的自然输入
visionOS 的”注视+捏合”交互在 WebXR 中被映射为 transient pointer。(15:19)
传统 XR 头显使用 tracked pointer(持续追踪的控制器或手部),而 transient pointer 只在用户做捏合手势时才存在。整个流程:
- 初始状态:网站不知道用户在看哪里,也不知道手的位置。
- 用户捏合:一个新的 input source 被添加到
session.inputSources,targetRayMode为"transient-pointer"。触发inputsourceschange事件和selectstart事件。 - 用户移动手:grip space 跟随拇指和食指的接触点,target ray space 跟随手部移动(而非持续追踪注视方向)。
- 用户释放:触发
select事件(表示成功完成选择),然后触发selectend事件。input source 被移除,网站重新无法感知用户视线和手部。
这种设计用隐私换体验:网站只在用户明确交互时才能获取空间信息,其他时间一无所知。
Hand Tracking
如果需要持续的手部追踪,可以在请求会话时将 "hand-tracking" 加入 feature 列表。(20:06)
开启手追踪后,检测到的手会以 tracked pointer 形式出现在 inputSources 中,每只手包含 25 个关节空间(joint spaces),可以用 WebGL 渲染用户的手。注意:开启手追踪后,你必须自己画用户的手,因为系统不再显示真实手的画面。transient pointer 在手追踪开启时仍然可用——同一个体验可以同时支持近距离精确操作(手追踪)和远距离选择(注视+捏合),两只手同时捏合时最多可能出现 4 个 input source。
测试与调试
开发阶段可以用 visionOS 模拟器测试 WebXR。(23:21)
- 在 localhost 启动 HTTP 服务(localhost 是安全上下文,不需要 HTTPS)。
- 用 WASD 键移动,右键拖动旋转视角,鼠标点击模拟 transient pointer。
- 通过 macOS Safari 的 Web Inspector 远程调试模拟器中的页面:查看 console、设置断点、单步执行 JavaScript。
- 手追踪无法在模拟器中测试,需要真机。
核心启发
-
做什么:为电商网站的产品页添加 WebXR 3D 预览模式。用户点击”在空间中查看”按钮后,产品模型以真实比例出现在 Vision Pro 的空间中,用户可以绕着走、凑近看细节。为什么值得做:Vision Pro 用户可以感知产品的真实尺寸和质感,比 2D 图片直观得多,且无需开发原生 app。怎么开始:用 A-Frame 声明式写法包装现有 3D 模型,加一个
<a-scene>和模型加载组件,检测navigator.xr.isSessionSupported("immersive-vr")后显示入口按钮。 -
做什么:在数据可视化仪表板中添加沉浸式查看模式。用户可以在 3D 空间中浏览数据图表,从不同角度观察数据关系。为什么值得做:空间计算设备天然适合多维度数据的呈现——每个数据维度可以映射到空间的一个轴,用户通过移动头部来”看”数据。怎么开始:用 Three.js 将现有 Chart.js/D3 图表数据映射到 3D 几何体,用 WebXR 的
local-floor参考空间让图表站立在地面上,transient pointer 交互用于选择数据点。 -
做什么:为教育网站构建交互式 3D 教学场景。比如化学分子结构、人体解剖、建筑模型,学生可以在空间中旋转、拆解、组装模型。为什么值得做:3D 教学内容在平面上永远受限于视角和遮挡,在空间中可以自由观察、用手抓取操作,学习效率提升明显。怎么开始:用 Babylon.js 加载已有的 glTF/GLB 模型,启用 hand-tracking 让用户用手直接操作模型部件,transient pointer 用于远距离选择和菜单操作。
关联 Session
- Optimize for the spatial web — 在 visionOS 网页中利用空间照片、Web Speech、Web Audio 等标准提升体验
- Design great visionOS apps — 学习空间计算 app 的沉浸感设计原则,适用于 WebXR 体验设计
- Create enhanced spatial computing experiences with ARKit — 原生 ARKit 提供房间追踪和手部追踪,适合需要更深层系统能力的场景
- Dive deep into volumes and immersive spaces — 原生 SwiftUI 中沉浸空间的定制方法,可与 WebXR 方案对比选型
- What’s new in Quick Look for visionOS — Quick Look 中 3D 模型查看的新增强,是 WebXR 3D 预览的互补方案
评论
GitHub Issues · utterances