1.1 前言
Online3DViewer 是预览 3D 模型的网站,效果很好,支持格式也很多
但是它的文档写的不详细,只有 npm 包的简单教程,没有其它使用方式的指引,网上相关的中文资料也很少
所以我写这篇文章,主要分享如何嵌入 Online3DViewer 整站并进行自定义修改
1.2 使用方式
- 使用 npm 包(教程),这种方式没有 UI 界面,只可以加载模型预览,想进行一点改造没法做
- 通过 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 会解压并加载。
实际效果:
这是 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 来做国际化,这里就不讲处理的细节了,直接放源码仓库:
(基于 v0.12.0 的 #461 PR 修改,注意不是最新版,但处理思路是一样的)
1.4 结束
如果帮到你了可以点赞收藏支持一下。
2025/11/08:发布文章