Get started with the HTML Model Element
visionOS & Spatial Computing 进阶 15m

HTML 模型元素入门

Get started with the HTML Model Element

2026年6月10日

在 Apple 官方观看视频

一句话判断

✅ Web 端 3D 展示终于从“引入几十 KB 的 JS 库”变成了“写一个 <model> 标签”。

这场 Session 讲了什么

这场 Session 最值得关注的一件事是:Apple 把 visionOS 上的原生 <model> 元素下放到了 iOS、iPadOS 和 macOS 的 Safari 中,直接在 Web 标准层面干掉了第三方 3D 库的必要性。

以前在网页上放个 3D 模型,大家默认用 Google 的 model-viewer 或者 Three.js,不仅要引入庞大的 JS 库,在 visionOS 上还得专门处理立体渲染(Stereoscopic rendering)的 hack。现在就跟写 <img> 标签一样简单,丢个 USDZ 文件进去,浏览器底层直接接管渲染。对于不支持的浏览器,官方提供了 W3C polyfill,甚至还能无缝衔接 AR Quick Look。

这对做电商、3D 资产展示的 Web 开发者来说,直接省掉了一半的前端 3D 胶水代码。你不再需要关心 WebGL 上下文丢失或者内存泄漏,WebKit 底层帮你把脏活累活全干了。

值得深挖的点

原生 <model> 替代 model-viewer 的 Trade-off

Apple 推原生 <model> 元素,表面上是简化 API,实际上是在抢夺 Web 3D 标准的定义权。model-viewer 虽然好用,但它是个 JS 库,在 visionOS 这种需要双目立体渲染的空间计算平台上,JS 层的 WebGL 性能损耗和视角同步是个大坑。原生元素直接走 WebKit 底层渲染管线,帧率更稳,还能直接和 visionOS 的沉浸式网站环境(Immersive website environments)打通。

代价是隐形的生态绑定。目前官方强推 USDZ 格式,虽然 Safari 也支持 glTF/GLB,但像 usdcrush 这种能把 7.9MB 压到 1.9MB 的官方命令行工具只认 USD 体系。如果你现有的管线全是 glTF,迁移成本不低。原生元素的自定义交互需要自己手写 DOMMatrixrequestAnimationFrame 动画,比起 model-viewer 丰富的开箱即用 API,初期会有一定的学习曲线。

stagemode 与自定义 entityTransform 的断层

Session 里花了很大篇幅讲交互。如果你只需要用户随便转转看看,加个 stagemode="orbit" 属性就行,浏览器会自动处理弹簧回弹(spring-back)和防裁剪(clipping protection),模型还会自动缩小一点防止转出屏幕。

但如果你要做“点击按钮切换到特定视角”这种电商常见需求,就必须关掉 stagemode,自己用 JS 操控 entityTransform。这里有个明显的坑:原生元素不会帮你处理自定义旋转时的边界框和裁剪问题,模型转着转着可能就飞出视口了。Apple 给出的方案是开发者自己捕获初始变换、计算缓动(easing)并手动更新 DOMMatrix。这意味着“简单展示”和“深度定制”之间存在断层,想要丝滑的自定义视角切换,前端还是得写不少数学计算代码。

代码片段

1. 最简加载与优雅降级

场景:电商产品页首屏 3D 展示,兼顾旧版浏览器和弱网环境。

<!-- 使用 src 属性加载,内部嵌套 img 作为降级方案 -->
<model id="camping-mallet" src="mallet.usdz">
    <!-- 旧版浏览器或不支持 3D 时显示图片 -->
    <img src="mallet.png" alt="露营槌">
</model>

<script>
    const model = document.getElementById("camping-mallet");
    // 监听 ready promise,模型加载完成后再隐藏 loading 动画
    model.ready.then(() => {
        hideLoadingSpinner();
    }).catch(() => {
        showFallbackImage();
    });
</script>

坑:ready promise 触发时只代表模型解析完毕可以渲染,不代表高分辨率纹理全部下载完成,大模型在弱网下仍可能有短暂的白模闪烁,建议配合骨架屏使用。

2. 自定义视角切换动画

场景:用户点击“侧面”、“顶部”按钮,模型平滑旋转到指定角度。

const model = document.getElementById("boot");
let animationId = null;
let currentAngle = 0;

function rotateTo(targetAngle) {
    // 取消正在进行的动画,防止连续点击导致冲突
    if (animationId) cancelAnimationFrame(animationId);
    
    const startAngle = currentAngle;
    const startTime = performance.now();
    const duration = 500; // 半秒过渡

    function step(now) {
        const progress = Math.min((now - startTime) / duration, 1);
        // 简单的 ease-out 缓动计算
        const ease = 1 - Math.pow(1 - progress, 3); 
        currentAngle = startAngle + (targetAngle - startAngle) * ease;
        
        const transform = new DOMMatrix();
        // 围绕 Y 轴旋转
        transform.rotateSelf(0, currentAngle, 0); 
        model.entityTransform = transform;

        if (progress < 1) {
            animationId = requestAnimationFrame(step);
        }
    }
    animationId = requestAnimationFrame(step);
}

坑:使用 entityTransform 时必须移除 stagemode="orbit",否则原生轨道控制会和你手写的 DOMMatrix 抢占控制权,导致模型在屏幕上疯狂抖动。

最佳实践

已有项目如果 model-viewer 跑得好好的,没必要立刻全量替换。新项目或者需要重点优化 visionOS 体验、追求极致首屏性能的,直接上 <model>。记得在 HTML 里引入 W3C 的 polyfill 脚本,通过 window.HTMLModelElement 判断环境,保证非 Safari 浏览器的基本可用性。

usdcrush 加到 CI/CD 流程里。USDZ 文件动辄几 MB,压缩后体积能减 75% 且肉眼看不出画质损失,这对移动端首屏加载至关重要。同时用 macOS 自带的 usdrecord 命令行工具,写个脚本批量渲染 USDZ 的 fallback 图片,直接替代人工截图。

做自定义旋转动画时,千万记得在 requestAnimationFrame 里先 cancelAnimationFrame 取消上一个动画。另外,手动设置 entityTransform 后,如果发现模型被裁切,需要手动调整 Z 轴位移把模型拉回可视区域,原生元素在自定义模式下不会帮你做防裁剪保护。

还有什么值得关注

  • AI 生成 3D 资产被官方盖章:Apple 在 Session 里点名了 Tripo3D 和 Meshy.ai,说明 AI 文本/图片生成 USDZ 已经成为官方认可的资产生产工作流,不用再死磕传统建模了。
  • AR Quick Look 的无缝衔接:只需用 <a rel="ar"> 标签把 <model> 包裹起来指向同一个 USDZ 文件,iOS 端就能直接唤起原生的 AR 放置体验,无需额外写 JS 桥接。
  • W3C 标准化窗口期:Apple 正在把这个元素推向 W3C 沉浸式 Web 社区组(Immersive Web Community Group),现在去提 Issue 和反馈,最容易影响最终跨平台 API 的设计走向。
HTML Model Element USDZ visionOS WebAR Spatial Computing