Highlight
visionOS 26 的 Safari 默认开启 HTML
<model>元素,可在网页内联立体渲染 USDZ 文件,并新增 Website Environments 让网站指定沉浸式背景。
核心内容
过去十几年,Web 上展示 3D 模型只能靠 Three.js、Babylon.js 这样的 JS 库,在 2D canvas 里画几何体。用户看到的依然是平面图像,缺少深度,更没法走过去从另一个角度看。视觉上是 3D,体验上还是 2D。
visionOS 26 的 Safari 引入了一个酝酿多年的提案:HTML <model> 元素。语法和 <img> 一样简单,但渲染行为完全不同——它在页面表面”开了一扇窗”,把 USDZ 模型立体地放在虚拟空间里。用户可以转头从不同角度观察,也可以捏住模型把它拖到 Quick Look 中以真实尺寸查看,甚至直接拖进其他 app。这个元素和文档流、CSS、JavaScript 完全融合:可以用 background-color 让虚拟空间和页面底色一致,可以用 backdrop-filter 在模型上覆盖一层毛玻璃面板,也可以用 position: absolute 让其他 DOM 元素与模型分层叠放。
除了 <model>,这个 session 还讲了沉浸式媒体(180/360 度视频、Apple Immersive Video、空间照片)在 <video> / <img> 里的内联播放与全屏,以及 developer preview 的 <link rel="spatial-backdrop">,让网站为自己指定一个 USDZ 环境,访客可以选择沉浸进去再浏览。
详细内容
<model> 的最简形式(01:00)
<model src="teapot.usdz"></model>
关键点:
src指向一个 USDZ 文件,<model>元素必须显式闭合(不像<img>是 void 元素)。- 浏览器会把 bounding box 的 x-y 面居中缩放到元素内;模型始终渲染在页面表面之后,滚动出视口时不会”戳出来”。
服务器需要正确的 MIME 类型(05:30)
# NGINX mime.types
types {
...
model/vnd.usdz+zip usdz;
}
关键点:
- 旧 CDN / 服务器可能把
.usdz当成application/octet-stream,浏览器会拒绝渲染。 - Apache 用
AddType model/vnd.usdz+zip .usdz,Python 自带的http.server需要修改Handler.extensions_map。
提供降级方案(05:51 ~ 06:17)
<!-- <model-viewer> library from https://modelviewer.dev/ -->
<script type="module"
src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js">
</script>
<model src="camera.usdz">
<!-- Fallback experience for backward compatibility -->
<model-viewer src="camera.glb"></model-viewer>
</model>
关键点:
- 把 fallback 内容直接写在
<model>内部,支持<model>的浏览器忽略它,不支持的浏览器会渲染内部内容。 - 最简单的 fallback 是一张
<img>;想保留旋转体验就嵌一个<model-viewer>这类 2D 库。
检测特性而非 user agent(06:52)
if (window.HTMLModelElement) {
// Supported by this browser
} else {
// Not supported by this browser
}
关键点:
- 检测
window.HTMLModelElement这个全局对象是否存在。 - 不要用 user agent 字符串判断,将来其他浏览器实现这个标准时会判断错。
加载指示器(07:32)
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
if (window.HTMLModelElement) {
mymodel.ready.then(result => {
// Hide the loading indicator
// Show the model
}).catch(error => {
// Loading error occurred, show a retry button
});
}
</script>
关键点:
model.ready返回一个 Promise,资源完全加载后 resolve。- USDZ 文件超过 10MB 是常态,慢网络要好几秒;显示 loading 指示器能防止用户直接关页面。
.catch里准备一个重试按钮,处理网络失败。
用 IBL 控制光照(10:56)
<model src="camera.usdz" environmentmap="sunset.exr"></model>
关键点:
environmentmap属性指定一张 image-based lighting 图(等距柱状投影)。- 推荐 OpenEXR 或 Radiance HDR 格式,可以表达多个数量级的亮度,反射效果真实;JPEG 会让模型看起来很平。
- IBL 文件不打包进 USDZ,便于不同场景应用不同光照。
JavaScript 控制位姿(13:49)
<model src="teapot.usdz" id="mymodel"></model>
<a onclick="turnRight()">Right</a>
<script>
const mymodel = document.getElementById("mymodel");
function turnRight() {
const matrix = mymodel.entityTransform; // DOMMatrixReadOnly
const newMatrix = matrix.rotateAxisAngle(0, 1, 0, 90);
mymodel.entityTransform = newMatrix;
}
</script>
关键点:
entityTransform是一个DOMMatrixReadOnly,读取当前模型的位姿。- 通过
rotateAxisAngle(0, 1, 0, 90)绕 y 轴旋转 90°,再赋值回entityTransform触发渲染更新。 - 想恢复初始状态:
model.entityTransform = new DOMMatrix();
按时间轴跳转动画(17:35)
<model src="camera.usdz" id="mymodel"></model>
<script>
const mymodel = document.getElementById("mymodel");
function openFlash() {
mymodel.currentTime = 1; // Unit is seconds
}
function openScreen() {
mymodel.currentTime = 3; // Unit is seconds
}
</script>
关键点:
currentTime单位是秒,对应 USDZ 文件内的动画时间轴。- 配合
<input type="range">可以做一个手动拖动的动画进度条。 - 自动播放用
<model loop autoplay>;JS 控制用model.play()/model.pause(),状态读model.paused。
用 Three.js 动态生成 USDZ(19:35)
import * as THREE from "three";
import { USDZExporter } from "three/examples/exporters/USDZExporter.js";
async function generateModel() {
const scene = new THREE.Scene();
// ... create a really nice scene procedurally ...
const bytes = await new USDZExporter().parseAsync(scene);
const objURL = URL.createObjectURL(new Blob([bytes]));
const mymodel = document.getElementById("mymodel");
mymodel.setAttribute("src", objURL);
}
关键点:
- Three.js 自带
USDZExporter,把过程化生成的场景导出成 USDZ 字节流。 - 用
URL.createObjectURL包成 blob URL,再赋给<model>的src,浏览器即可立体渲染。 - 适合做参数化产品配置器:用户改颜色 / 改尺寸 → 重新生成 USDZ → 直接预览。
沉浸式媒体内联(23:10)
<video src="spatial_video.mov"></video> <!-- Single file -->
<video src="360_video.m3u8"></video> <!-- HTTP Live Streaming -->
关键点:
- 现有
<video>标签直接支持 180/360 度、广角和 Apple Immersive Video。 - 默认在页面里 2D 内联播放,调用
player.requestFullScreen()全屏后自动以正确投影方式包裹用户。 - 空间照片用
<img src="spatial.heic" controls>即可,加controls属性会显示打开沉浸视图的按钮。
Website Environments(developer preview,26:49)
<link rel="spatial-backdrop" href="office.usdz" environmentmap="lighting.hdr">
关键点:
- 用
<link>给整个页面声明一个 USDZ 沉浸式背景。 - 用户主动选择”沉浸”进去后,整个房间会替换成 office.usdz 的环境。
- 目前是 developer preview 状态,需要在 Safari 设置里手动开启。
核心启发
-
做什么:把产品 3D 展示从 2D canvas 升级到
<model>- 为什么值得做:电商、家具、相机、玩具类商品在 visionOS Safari 上能直接立体展示,转头看不同角度比”360 度图集”沉浸感高一个量级,且无需 native app。
- 怎么开始:用 iPhone + Reality Composer 把样品拍成 USDZ → Mac Preview 导出压缩纹理版本控制在 5MB 以内 → 在原产品页加
<model src>,用<img>做 fallback。
-
做什么:用
<model>+ DOM 做产品配置器- 为什么值得做:不需要再造一套 WebGL 渲染管线,CSS 就能做毛玻璃面板、文字层、按钮;切换配置直接改
src或者用 Three.js 现场生成 USDZ。 - 怎么开始:先在原页用 Three.js 跑通过程化生成场景,然后接入
USDZExporter输出 blob URL;用model.ready控制 loading 态。
- 为什么值得做:不需要再造一套 WebGL 渲染管线,CSS 就能做毛玻璃面板、文字层、按钮;切换配置直接改
-
做什么:把品牌沉浸式短片放到官网
- 为什么值得做:Apple Immersive Video 在 visionOS Safari 上可以直接
<video>内联播放和全屏沉浸,省掉 App Store 上架;普通访客看到的是 2D 内联,Vision Pro 用户全屏后是 180 度沉浸。 - 怎么开始:把素材切成 HLS(
.m3u8)→ 同一份<video>标签同时服务两端 → 在桌面端做好降级海报。
- 为什么值得做:Apple Immersive Video 在 visionOS Safari 上可以直接
-
做什么:尝试
spatial-backdrop给品牌站做专属环境- 为什么值得做:作为 developer preview 早期试水,能让访客”沉浸进店”;先发先得,可以收获媒体关注。
- 怎么开始:用 Reality Composer Pro 做一个简单的店面 / 展厅 USDZ → 配套一张 HDR 光照 → 加
<link rel="spatial-backdrop">,在 Safari 实验性功能里开启验证。
关联 Session
- Optimize your 3D assets for spatial computing — WWDC24 关于压缩 USDZ 文件、控制贴图与几何体的实操指南
- Meet Reality Composer Pro — 在 Mac 上构建 USDZ 模型与场景的入门 session
- Design hover interactions for visionOS — 为 visionOS 设计 hover 反馈,配合 model 元素的注视交互
- Design widgets for visionOS — 在 visionOS 26 上设计可融入空间环境的 widget
评论
GitHub Issues · utterances