WWDC Quick Look 💓 By SwiftGGTeam
Get started with the HTML Model Element

Get started with the HTML Model Element

观看原视频

Highlight

Apple 将原生的 <model> 元素从 visionOS 扩展到 iOS、iPadOS 和 macOS,开发者写一行 HTML 就能在网页中嵌入交互式 3D 模型,无需引入任何 JavaScript 库,且支持 AR Quick Look 和立体渲染。

核心内容

在网页上展示 3D 模型,过去只有两条路:引入 Google 的 model-viewer,或者用 Three.js 自己搭一套渲染管线。两条路都要加载大量 JavaScript,在 visionOS 上还要额外处理立体渲染(stereoscopic rendering)的性能损耗。

Apple 在 visionOS 上率先推出了原生的 <model> 元素。现在,这个元素来到了 iOS、iPadOS 和 macOS 的 Safari 中。同一行标记,在所有 Apple 平台上都能工作。

00:37

<model> 是原生 HTML 元素,由浏览器直接渲染。visionOS 上自带立体渲染,iPhone 和 iPad 上也能流畅交互。它还是一个正在 W3C 推进的 Web 标准,代码具有向前兼容性。对于不支持该元素的浏览器,官方提供了 polyfill 作为降级方案。

00:48

Session 以一个户外电商网站为例,演示了从获取 3D 资产到上线优化的完整流程。获取资产有三种途径:用 iPhone 扫描实物、在 Blender 等工具中从零创建、或者用 AI 生成。Session 中演示了用几张露营槌的照片,通过 Tripo3D 在几分钟内生成 USDZ 模型的过程。

02:22

USDZ 是 Apple 推荐的格式。它是 Universal Scene Description 的 zip 打包格式,把几何体、材质、纹理、动画都封装在一个文件里。Safari 也支持其他格式,但从兼容性角度,USDZ 是首选。

03:17

详细内容

加载模型与降级处理

<model> 的用法和 <img><video> 一样直观。用 src 属性指向 USDZ 文件即可:

<!-- 使用 src 属性 -->
<model src="mallet.usdz"></model>

<!-- 使用 <source> 子元素指定 MIME 类型 -->
<model>
    <source src="mallet.usdz" type="model/vnd.usdz+zip">
</model>

04:19

降级也很直接。在 <model> 内部放一张 <img>,不支持该元素的浏览器会显示图片:

<model id="mallet" src="mallet.usdz">
    <img src="mallet.png"
         alt="Rubber mallet with wooden handle">
</model>

04:39

关键点:

  • <model> 内部嵌套 <img> 实现优雅降级
  • 旧版 Safari 和其他不支持 <model> 的浏览器会自动渲染图片
  • 用户至少能看到产品的静态视图

3D 模型文件通常有几十 MB,加载需要时间。ready 属性返回一个 Promise,在模型加载完成时 resolve:

<model id="mallet" src="mallet.usdz"></model>

<script>
    const model = document.getElementById("mallet");
    model.ready.then(result => {
        // Hide the loading indicator
    }).catch(error => {
        // Loading failed, show fallback
    });
</script>

05:09

关键点:

  • model.ready 是一个 Promise,模型解析完毕时 resolve
  • then 回调中隐藏 loading 动画
  • catch 回调中处理加载失败,显示降级内容

对于完全不支持 <model> 的浏览器,可以用 polyfill:

<script type="module">
    if (!window.HTMLModelElement) {
        import("model-element-polyfill.js").then(() => {
            // Polyfill ready to use
        });
    }
</script>

05:39

关键点:

  • 通过 window.HTMLModelElement 检测原生支持
  • 不支持时动态加载 polyfill 脚本
  • polyfill 无法模拟 visionOS 上的立体渲染等硬件相关特性

样式与交互

<model> 渲染在自己的虚拟空间中,不会继承页面的背景。需要直接给元素设置 background-color

<model id="mallet" src="mallet.usdz"></model>
<style>
    model {
        background-color: #f4f1ec;
    }
</style>

06:13

关键点:

  • 背景色始终不透明,带透明度的颜色会被转换为不透明色
  • 直接对 <model> 元素写 CSS 即可控制外观

最简单的交互是让用户自由旋转模型。加一个 stagemode="orbit" 属性:

<model id="mallet"
       src="mallet.usdz"
       stagemode="orbit">
</model>

06:47

关键点:

  • stagemode="orbit" 让用户可以左右自由旋转模型
  • 上下倾斜后松手,模型会弹簧回弹到原始角度
  • 模型会自动缩小一点,防止旋转时部分被裁剪

如果需要自定义视角,用 JavaScript 操控 entityTransform。注意要先禁用 stagemode

<model id="boot" src="boot.usdz"></model>
<button id="button-side">Side</button>
<button id="button-reset">Reset</button>

<script>
    const model = document.getElementById("boot");
    const initialTransform = model.entityTransform;

    document.getElementById("button-side")
            .addEventListener("click", () => {
        const transform = new DOMMatrix();
        transform.rotateSelf(0, 135, 0);
        model.entityTransform = transform;
    });

    document.getElementById("button-reset")
            .addEventListener("click", () => {
        model.entityTransform = initialTransform;
    });
</script>

07:31

关键点:

  • 使用 entityTransform 前需移除 stagemode 属性或设为 "none"
  • DOMMatrix 表示模型在 3D 空间中的方位
  • rotateSelf(0, 135, 0) 绕 Y 轴旋转 135 度
  • 提前保存 initialTransform 用于复位
  • 自定义旋转可能导致模型被裁剪,需要手动调整位置

直接切换视角会显得很生硬。用 requestAnimationFrame 实现平滑过渡:

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

function animateTo(targetAngle) {
    if (animationId) cancelAnimationFrame(animationId);
    const startAngle = currentAngle;
    const startTime = performance.now();

    function step(now) {
        const progress = Math.min((now - startTime) / duration, 1);
        const ease = 1 - Math.pow(1 - progress, 3);
        currentAngle = startAngle + (targetAngle - startAngle) * ease;
        model.entityTransform = new DOMMatrix().rotateSelf(0, currentAngle, 0);
        if (progress < 1) animationId = requestAnimationFrame(step);
    }

    requestAnimationFrame(step);
}

document.getElementById("button-side").addEventListener("click", () => animateTo(135));
document.getElementById("button-reset").addEventListener("click", () => animateTo(0));

08:35

关键点:

  • cancelAnimationFrame 取消正在运行的动画,防止冲突
  • ease = 1 - Math.pow(1 - progress, 3) 实现 ease-out 缓动
  • 每帧用新的 DOMMatrix 更新 entityTransform
  • 500 毫秒的时长兼顾了响应速度和视觉流畅度

动画播放

USDZ 文件可以内嵌在 Blender 或 Maya 中制作的动画。<model> 默认播放第一条动画轨道,用 JavaScript 控制播放速率:

<model id="bottle" src="bottle.usdz"></model>
<button id="button-play" onclick="play(5)">
    Play
</button>
<button id="button-reverse" onclick="play(-5)">
    Reverse
</button>

<script>
    const model = document.getElementById("bottle");

    function play(rate) {
        model.playbackRate = rate;
        model.play();
    }
</script>

10:07

关键点:

  • model.play() 启动动画播放
  • playbackRate 控制播放速度,正值正向播放,负值倒放
  • 示例中用 5 和 -5 实现 5 倍速正向和反向播放

AR Quick Look

<model> 包在 <a rel="ar"> 标签中,iOS 和 iPadOS 用户点击后直接进入 AR Quick Look:

<a rel="ar" href="bottle.usdz">
    <model id="boot" src="bottle.usdz"></model>
</a>

11:06

关键点:

  • <a rel="ar"> 指向与 <model> 相同的 USDZ 文件
  • iOS/iPadOS 上唤起原生 AR 体验
  • visionOS 上模型以立体渲染呈现,用户可以把产品从页面中”拉出来”查看

命令行优化工具

usdcrush 可以大幅压缩 USDZ 文件体积。Session 中的示例把 7.9 MB 的靴子模型压到了 1.9 MB,画质没有肉眼可见的损失。

12:39

usdrecord 可以从 USDZ 文件批量渲染缩略图或降级图片。可以写脚本遍历整个商品目录自动生成 fallback 图片,不需要手动截图。

13:10

这两个工具 macOS 已预装,属于 USD 工具套件的一部分。

核心启发

1. 电商产品页一键 3D 化

把现有商品图片替换成 <model> 标签,加载同一个 USDZ 文件。用户可以在页面上旋转查看产品细节,点击后进入 AR 模式把商品放在家里看效果。入口就是一行 HTML。

  • 实现思路:用 usdrecord 批量生成 fallback 图片,<model> 内部嵌套 <img> 做降级,外层包 <a rel="ar"> 实现 AR 入口

2. 作品集网站的交互式 3D 展示

设计师、建筑师可以在个人网站上直接展示 3D 作品。访客不用下载任何文件,在浏览器里就能旋转、缩放模型。用 stagemode="orbit" 一行代码搞定交互。

  • 实现思路:导出 USDZ 格式,<model stagemode="orbit"> 直接嵌入,配合 CSS 设置背景色融入页面风格

3. 教育类应用的 3D 模型讲解

生物课展示细胞结构、历史课展示文物复原模型。用 entityTransformrequestAnimationFrame 实现预设视角切换,点击”剖面图”按钮平滑旋转到特定角度。

  • 实现思路:预定义几个视角角度,用 animateTo() 函数实现带缓动的视角切换,配合 playbackRate 控制模型内置动画

4. AI 生成 3D 资产的工作流

用 Tripo3D 或 Meshy.ai 从几张产品照片生成 USDZ 模型,用 usdcrush 压缩后部署。整个流程不需要专业 3D 建模技能。

  • 实现思路:照片 -> AI 生成 USDZ -> usdcrush 压缩 -> usdrecord 生成 fallback 图片 -> 上线

5. 跨平台 3D 内容分发

同一套 HTML 代码在 iPhone、iPad、Mac 和 Vision Pro 上都能展示 3D 内容。visionOS 上还有立体深度效果。配合 polyfill 还能覆盖不支持原生 <model> 的浏览器。

  • 实现思路:检测 window.HTMLModelElement,不存在时加载 polyfill,保证最大兼容性

关联 Session

评论

GitHub Issues · utterances