更新记录
1.1.8(2025-08-01) 下载此版本
更新详情
- 修复loader.js中没有处理gltf模型问题
- 预览组件增加模型类别参数:modelType,比如:'glb',可能在本地预览时或模型地址无后缀时用到
- 修复当canvas不是全屏时模型点击位置不对问题(event.js)
- 修改event.js代码,主要是解耦使其在app中可以使用,同时app增加模型点击功能
- 增加在外部调用render.js的方法(但这个并不是一个好方法,有损耗且延迟)
- useThree.js增加getNodes方法,用于获取节点列表,比如在页面中循环渲染,点击显示隐藏模型部件
- useThree.js增加findByKey方法,用于查找Object3D
1.1.7(2025-07-18) 下载此版本
文档错乱重新上传一次
增加fbx、obj模型加载
如果加载fbx模型,贴图材质不显示,即显示黑色,是因为fbx和MeshPhongMaterial材质不兼容,可以尝试重写材质
group.traverse((c) => {
if (c.isMesh) {
const m = c.material;
let material = new THREE.MeshStandardMaterial({});
if (Array.isArray(m)) {
material = m.map(item => {
const tempMaterial = new THREE.MeshStandardMaterial({});
tempMaterial.copy(item);
return tempMaterial
})
} else {
material.copy(m);
}
c.material = material;
}
});
// 小程序这样写
// 小程序texture不是2的n次方也会导致无法正常显示材质
import { traverse } from '@/components/threeJs/index.js'
traverse(group,(c) => {
if (c.isMesh) {
const m = c.material;
let material = new THREE.MeshStandardMaterial({});
if (Array.isArray(m)) {
material = m.map(item => {
const tempMaterial = new THREE.MeshStandardMaterial({});
tempMaterial.copy(item);
return tempMaterial
})
} else {
material.copy(m);
}
c.material = material;
}
});
// 或者这样
const {
fixFbxMaterial
} = useThreeJs()
fixFbxMaterial(group)
示例项目中资源链接统一管理
// main.js
// 主要是方便后期维护,域名更换
app.config.globalProperties['$getUrl'] = (path) => {
if (/http/.test(path)) {
return path
}
return `https://www.ccnihao.fun/public-resource${path}`
}
考虑到材质可能是数组,修复一些材质销毁等逻辑
1.1.6(2025-07-18) 下载此版本
增加fbx、obj模型加载
-
如果加载fbx模型,贴图材质不显示,即显示黑色,是因为fbx和MeshPhongMaterial材质不兼容,可以尝试重写材质
group.traverse((c) => { if (c.isMesh) { const m = c.material; let material = new THREE.MeshStandardMaterial({}); if (Array.isArray(m)) { material = m.map(item => { const tempMaterial = new THREE.MeshStandardMaterial({}); tempMaterial.copy(item); return tempMaterial }) } else { material.copy(m); } c.material = material; } });
// 小程序这样写 // 小程序texture不是2的n次方也会导致无法正常显示材质 import { traverse } from '@/components/threeJs/index.js' traverse(group,(c) => { if (c.isMesh) { const m = c.material; let material = new THREE.MeshStandardMaterial({}); if (Array.isArray(m)) { material = m.map(item => { const tempMaterial = new THREE.MeshStandardMaterial({}); tempMaterial.copy(item); return tempMaterial }) } else { material.copy(m); }
c.material = material;
} }); // 或者这样 const { fixFbxMaterial } = useThreeJs()
// fixFbxMaterial(group)
#### 示例项目中资源链接统一管理
```js
// main.js
// 主要是方便后期维护,域名更换
app.config.globalProperties['$getUrl'] = (path) => {
if (/http/.test(path)) {
return path
}
return `https://www.ccnihao.fun/public-resource${path}`
}
考虑到材质可能是数组,修复一些材质销毁等逻辑
查看更多平台兼容性
uni-app(4.45)
Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
---|---|---|---|---|---|---|---|---|
× | √ | √ | √ | √ | × | - | - | - |
微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 快应用-华为 | 快应用-联盟 |
---|---|---|---|---|---|---|---|---|---|---|
√ | × | √ | × | × | × | - | × | × | × | × |
uni-app x(4.66)
Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
---|---|---|---|---|---|
- | - | - | - | - | - |
其他
多语言 | 暗黑模式 | 宽屏模式 |
---|---|---|
× | × | √ |
简介
此组件仅为快速展示 3D 模型的 demo。 使用 vue3 + three-platformize 实现。 h5和app用的是官方的threejs,app是基于renderjs实现的 目前仅支持微信小程序、抖音小程序,其他小程序可以参考代码修改,或者查看 three-platformize 官网
相关库 github 地址 vue、three.js、three-platformize
使用说明
建议先下载示例代码运行试试,且内部包含完整组件,可直接使用
插件命名原则导致需要组件拼接一个前缀,请直接下载示例工程
- 将 components 下的 threeJs 组件移动到自己项目 components 下
- 下载 three-platformize
npm i three-platformize@1.133.3
- 引入到组件和相关工具方法
<template>
<view class="container">
<!-- onload事件代表初始化完毕 -->
<Stage @onload="onload"> </Stage>
</view>
</template>
import * as THREE from "three-platformize";
import Stage from "@/components/threeJs/stage.vue";
import useThreeJs from "@/components/threeJs/index.js";
const {
getInstance, // 获取canvas、camera、等实例
centerAndScale, // 用于调整模型大小和位置
setEnv, // 设置环境贴图
screenshot, // 截图
} = useThreeJs();
const loadModel = () => {
const { group, loader } = getInstance();
// 如果是.gltf+.bin+texture 请手动转成glb,相关nodejs代码可以参考使用说明
loader
.load(
"https://www.ccnihao.fun/public-resource/DamagedHelmet.gltf.glb",
(p) => {
// 这里是加载进度
// 需要后台正确的设置Content-Length才会有进度
}
)
.then((gltf) => {
group.add(gltf.scene);
// 居中并缩放到合适的位置
centerAndScale();
});
};
// 初始化完毕,开始设置环境贴图,并加载模型
const onload = async () => {
console.log("onload");
// 设置环境贴图,传入hdr文件在线地址即可
await setEnv(
"https://www.ccnihao.fun/public-resource/studio_small_09_1k.hdr",
false
);
// 加载模型逻辑
loadModel();
};
工具相关
- obj 转 glb
# 先安装obj2gltf
npm i obj2gltf
// nodejs
const obj2gltf = require("obj2gltf");
const fs = require("fs");
const path = require("path");
(async () => {
// obj 2 glb
const outPath = "需要导出的路径";
const inPath = "需要转换的obj文件路径";
const options = {
binary: true, // 不传默认导出gltf
};
const glb = await obj2gltf(inPath, options);
const outFile = path.join(
outPath,
`newmodel.${options.binary ? "glb" : "gltf"}`
);
if (options.binary) {
fs.writeFileSync(outFile, glb);
} else {
const data = Buffer.from(JSON.stringify(glb));
fs.writeFileSync(outFile, data);
}
})();
- gltf 转 glb
# 先安装gltf-pipeline
npm i gltf-pipeline
// nodejs
const gltfPipeline = require("gltf-pipeline");
const fsExtra = require("fs-extra");
const path = require("path");
(() => {
// gltf 2 glb
const outPath = "需要导出的路径";
const inPath = "需要转换的obj文件路径";
const gltfToGlb = gltfPipeline.gltfToGlb;
const gltf = fsExtra.readJsonSync(inPath);
const dir = path.dirname(inPath);
const options = { resourceDirectory: dir };
gltfToGlb(gltf, options).then(function (results) {
const outFile = path.join(outPath, `newmodel.glb`);
fsExtra.writeFileSync(outFile, results.glb);
});
})();
常见问题
-
ios 端 bounce 特性问题
主要是会与 OrbitControls 控制器冲突
// 在ios端即使页面尺寸刚好是100vw * 100vh 没有滚动条情况,也会出现拖动回弹的情况(就是页面可以拖动然后松开手指回到初始为止)
// 这时在pages.json中对应的page添加disableScroll即可
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "threeJs",
"navigationStyle": "custom", // 隐藏原生导航栏
"enablePullDownRefresh": false, // 禁止下拉刷新
"disableScroll": true // 在内容刚好沾满屏幕的时候,ios设备可以拖动然后立马回弹,有些情况不是很好用。禁止ios页面回弹(bounce效果)
}
}
]
}
-
在抖音小程序上使用本插件
- three-platformize 的 BytePlatform 代码中有个 bug,导致一直报错无法使用控制器
// 主要是修改/node_modules/three-platformize/src/BytePlatform/index.js 文件
// 找到dispatchTouchEvent方法,在if (changedTouches.length)上面加一行 const changedTouches = e.changedTouches;
dispatchTouchEvent(e = {}) {
const target = { ...this };
const event = {
changedTouches: e.changedTouches.map(touch => new Touch(touch)),
touches: e.touches.map(touch => new Touch(touch)),
targetTouches: Array.prototype.slice.call(
e.touches.map(touch => new Touch(touch)),
),
timeStamp: e.timeStamp,
target: target,
currentTarget: target,
type: e.type,
cancelBubble: false,
cancelable: false,
};
this.canvas.dispatchEvent(event);
const changedTouches = e.changedTouches; // 新加的
if (changedTouches.length) {
const touch = changedTouches[0];
const pointerEvent = {
pageX: touch.pageX,
pageY: touch.pageY,
pointerId: touch.identifier,
type: {
touchstart: 'pointerdown',
touchmove: 'pointermove',
touchend: 'pointerup',
}[e.type],
pointerType: 'touch',
};
this.canvas.dispatchEvent(pointerEvent);
}
}
- 如果你确定使用本插件,请使用 patch-package 给 three-platformize 打补丁(主要是解决重新下载 node_modules 会自动添加上次修改的内容)
# 1. 安装patch-package
npm install patch-package -D
# 2. 在package.json中增加脚本(每次npm i的时候会自动执行postinstall): "postinstall": "patch-package"
# 3. 创建补丁,会在文件目录中生成一个patches文件夹
npx patch-package three-platformize
- 如果你走到了这里说明那你成功了一大半了,我还要告诉你一些其他的问题 (1) 开发者工具抖音基础库 3.x.x.x 不会显示模型,具体需要你自己测试 (2) 开发者工具抖音基础库 2.97.0.2 以上控制器没有效果,但是真机调试没有问题 (3) 在组件内获取 canvas 开发者工具没有问题,但是真机调试会报错,如果你上传体验版没有问题可以忽略,否则请参考示例项目在组件外获取 canvas