如何嵌入Online3DViewer整站并进行修改

87 阅读4分钟

1.1 前言

Online3DViewer 是预览 3D 模型的网站,效果很好,支持格式也很多

但是它的文档写的不详细,只有 npm 包的简单教程,没有其它使用方式的指引,网上相关的中文资料也很少

所以我写这篇文章,主要分享如何嵌入 Online3DViewer 整站并进行自定义修改

1.2 使用方式

  1. 使用 npm 包(教程),这种方式没有 UI 界面,只可以加载模型预览,想进行一点改造没法做
  2. 通过 iframe 嵌入整个网站来使用,跟线上功能一样,可以自定义修改

使用 npm 包的组件示例:github.com/kovacsv/Onl…

官网示例(等会讲怎么运行,不是直接下载打开):github.com/kovacsv/Onl…

我主要讲第 2 种方式,因为这种方式没有官方教程指引,这里的嵌入,是指拉源码修改构建部署,而不是直接嵌入线上地址。

1.3 运行构建部署

将 Online3DViewer 克隆下来后,观察 package.json,能看到很多执行命令,主要关注:start、create_package

start 命令用于开发调试(需要先安装 http-server),create_package 用于生产部署。

start 命令运行打开后:点击 website 就是调试整个网站,点击 sandbox 就是调试各种使用示例

网站 html:website/index.html

网站 js 入口:source/website/index.js

网站关键 js:source/website/website.js

修改好后就可以运行 create_package 命令,会生成 build/package,其中的 website 就是生产环境要部署的目录(engine 可以不用管)

1.4 嵌入修改后的 Online3DViewer 网站

我的嵌入形式(流程)是:预览附件时,如果文件后缀是 Online3DViewer 支持预览的格式,那就打开一个弹窗,弹窗中使用 iframe 嵌入 Online3DViewer,并把附件通过 postMessage 的方式传递给 Online3DViewer,其内部监听 message 事件在调用 SetModelFilesToHash 预览模型。

这里有一点要注意:某些 3D 模型由多个文件构建而成(例如,obj 文件通常包含一个 mtl 文件和一些纹理文件)。为了正确显示,必须导入所有必需的文件。

这个实际上 Online3DViewer 内部已经做了处理,我们只需要把模型相关的所有附件(附件列表数组)都传递给 Online3DViewer 即可。它发现是一个模型的多个文件,则直接预览,发现有多个模型文件,则弹窗让用户选择一个模型预览。

另外,如果是【包含模型的 zip 压缩包】也可以直接预览,Online3DViewer 会解压并加载。

实际效果:

image.png

这是 Online3DViewer 源码主要修改的地方:

source/website/website.js -> Load()

Load();
{
  // ...
  window.addEventListener('message', (e) => {
    // 因为此项目打包产物在另个项目的public中,随它一起部署,所以origin一定一致。如果你是独立部署,需要修改下
    if (e.origin !== window.location.origin) {
      console.warn('origin不一致', e.origin);
    }

    // 确认是父窗口发送的消息
    if (e.source !== window.parent) {
      console.warn('不是父窗口发的消息');
      return;
    }

    const data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;
    console.log('3DViewer 收到了消息', data);

    const { type, payload } = data;
    if (type === 'loadModel') {
      if (!payload.modelFileList.length) return;
      const urls = payload.modelFileList.map((u) => u.url);
      // 触发初始化加载/清空模型方法:OnHashChange
      this.hashHandler.SetModelFilesToHash(urls);
    }
  });
}

这是嵌入项目主要修改的地方:

<!-- 3D模型预览弹窗 -->
<a-modal
    v-model:open="modelPreviewModal.open"
    :closable="false"
    :width="1200"
    class="model-preview-modal"
    centered
    @cancel="modelPreviewModal.closeModal"
>
    <template #title>
        <div class="modal-header">
            <span class="title">3D模型预览</span>
            <span class="close" @click="modelPreviewModal.closeModal"><CloseOutlined /></span>
        </div>
    </template>

    <div class="modal-body">
        <!--
            1. 因为我是把打包产物放入vue项目的public/3DViewer,和主项目一起部署,所以src从根开始
            2. locale是指定国际化的,支持zh-CN和en-US
        -->
        <iframe
            v-if="modelPreviewModal.open"
            class="model-preview-iframe"
            ref="modelPreviewIframeRef"
            :src="`/3DViewer/index.html#$locale=${locale}`"
        ></iframe>
    </div>

    <template #footer>
        <div class="modal-footer">
            <div class="footer-left"></div>
            <div class="footer-right">
                <a-button type="primary" @click="modelPreviewModal.closeModal">关闭</a-button>
            </div>
        </div>
    </template>
</a-modal>


/**
 * 模型预览弹窗
 */
const modelPreviewModal = reactive<any>({
    open: false,
    isLoad: false,
    showModal: async (modelFileList: any) => {
        modelPreviewModal.open = true
        await nextTick()
        modelPreviewModal.sendMessage(modelFileList)
    },
    closeModal: () => {
        modelPreviewModal.open = false
    },
    sendMessage: (modelFileList: any) => {
        const handle = () => {
            const message = JSON.stringify({
                type: 'loadModel',
                payload: { modelFileList },
            })

            modelPreviewIframeRef.value.contentWindow.postMessage(message, location.origin)
        }

        if (modelPreviewModal.isLoad) {
            handle()
        } else {
            modelPreviewIframeRef.value.addEventListener(
                'load',
                () => {
                    modelPreviewModal.isLoad = true
                    handle()
                },
                { once: true },
            )
        }
    },
})

1.5 关于 Online3DViewer 的国际化处理

Online3DViewer 只有英文,源码是硬编码写的,没有国际化处理

要想支持中文翻译,可以引入 i18next 来做国际化,这里就不讲处理的细节了,直接放源码仓库:

github.com/xiefangqing…

(基于 v0.12.0 的 #461 PR 修改,注意不是最新版,但处理思路是一样的)

1.4 结束

如果帮到你了可以点赞收藏支持一下。

2025/11/08:发布文章