1.1. 模型拍摄
下方为在AI资源平台上传后的模型转动不同角度后的视图,业务场景需要模型调转不同角度进行截图,然后对截图进行保存,然后将保存的图片作为模型封面进行展示,最初的做法是直接使用电脑上自带的截图功能,但该做法每次需要手动将图片保存本地然后再点击上传,整个过程烦琐不易于用户操作,因此提出用户可以通过转动模型,点击拍摄按钮,实现一键式的角度拍摄 && 图片上传。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
整体的处理流程如下所示:
1、threejs模型加载暴露一个GF方法给用户,该方法用于获取当前模型的画面数据
2、当用户点击按钮的时候会调用GF方法获取当前画面数据
3、然后该画面数据会进行转码,通过Blob的方式封装成File对象
4、然后将该文件对象上传到Server服务器
关于GF方法本身并不是threejs自身暴露的,而是在代码内部实现的一个回调方法,它的具体实现是根据threeJS在执行的过程中会暴露一个canvas上下文,当GF调用即用户点击拍摄按钮的时候,首先会执行threeJSrender函数来确定当前画面,然后该过程会暴露canvas,再使用toDataURL来获取base64数据
GF代码实现:
fileResultGet() {
// 确保视角更新后,重新渲染场景
this.renderer.render(this.scene, this.camera); // 将此行代码添加在截图逻辑之前
// 设置渲染器输出高分辨率图片
const originalWidth = 600;
const originalHeight = 300;
const scale = 1;
this.renderer.setSize(originalWidth * scale, originalHeight * scale);
// 渲染场景
this.renderer.render(this.scene, this.camera);
// 从WebGL canvas获取DataURL
const imgUrl = this.renderer.domElement.toDataURL('image/png');
// 将渲染器恢复到原始尺寸供日常渲染使用
this.renderer.setSize(originalWidth, originalHeight);
this.renderer.render(this.scene, this.camera);
return dataURLToFile(imgUrl);
}
1.2. GIF拍摄
因为模型有的是动作,所以拍摄的时候需要在一定时间内捕获当中的动作,来形成GIF图来作为模型的展示,它的实现原理与静态图片拍摄类似,整体的处理流程如下所示:
1、threejs模型加载暴露一个TF方法给用户,该方法用于获取当前模型的动态画面数据
2、当用户点击按钮的时候会调用TF方法获取一段时间内的画面数据,其实现是根据定时器以一定频率不断拍摄静态画面,整个过程属于await状态。
3、将拍摄得到的静态画面队列数据,使用Gif.js库进行gif图合成形成gif文件
4、然后将该文件对象上传到Server服务器
1.3. 进度条展示
本身threeJS方法中的loader加载器接受一个回调函数,该函数中可以不断获取当前模型“加载”的进度,但此“加载”并不是threejs单纯加载模型的耗时,而是threejs发送的请求耗时,本身的耗时相对网络请求可忽略不计
具体的代码展示:
fbx: (url: string, scene: any, keyFrame: string) => {
return new Promise((res) => {
regesiterManager(keyFrame);
new FBXLoader(manager).load(
url,
(obj: object) => {
scene.add(obj);
res(obj);
},
// 回调显示进度条
(xhr: object) => onProgress(xhr, keyFrame),
);
});
},
// 回调函数从threejs不断获取进度信息
const onProgress = function (xhr: object, keyFrame: string) {
// // 更新进度条的宽度
let percentComplete = (xhr.loaded / xhr.total) * 100;
const domProgressValue = document.getElementById(`progressValue-${keyFrame}`);
const domProgress = document.getElementById(`canvasProgress-${keyFrame}`);
if (domProgress) {
domProgress.value = percentComplete.toFixed(2);
domProgress.style.opacity = '1';
}
if (domProgressValue) {
domProgressValue.innerHTML = `${percentComplete.toFixed(2)}%`;
domProgressValue.style.opacity = '1';
}
if (percentComplete >= 99.9) {
hideProgress(keyFrame);
}
};
当关闭处于正在加载的模型,怎么中断进度条,从上面可知threeJS对模型的加载主要为http请求,如果要实现中断的效果则需要将onProgress的过程外移,即不依赖threeJS内部的fetch,通过自定义fetch先拿到资源数据,这个过程中fetch进度可知且可通过定义AbortController来实现请求中断,具体的实现如下:
async function fetchWithProgress(url) {
const controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(url, { signal });
if (!response.body) {
throw new Error('ReadableStream not supported');
}
const contentLength = response.headers.get('content-length');
if (!contentLength) {
throw new Error('Content-Length response header unavailable');
}
const totalSize = parseInt(contentLength, 10);
let loadedSize = 0;
const reader = response.body.getReader();
let receivedValue;
// 流读取循环
while (true) {
const { done, value } = await reader.read();
if (done) {
console.log('Download complete');
break;
}
loadedSize += value.length;
console.log(`Download Progress: ${(loadedSize / totalSize * 100).toFixed(2)}%`);
receivedValue = new Uint8Array(value);
}
} catch (error) {
if (error.name === 'AbortError') {
console.log('Download aborted');
} else {
console.error('Download failed:', error);
}
}
// 中断下载的方法
function abortDownload() {
controller.abort();
console.log('Download has been aborted');
}
return {
abort: abortDownload,
};
}
const { abort } = fetchWithProgress('下载模型资源的URL');
// 如果需要中断下载
abort();