Three.js 场景导出 SVG 文件实现指南
1. 功能概述
本文档详细介绍如何将 Three.js 3D 场景转换为 SVG 格式的文件,并实现对特定材质(LineMaterial)的过滤。这个功能通常用于:
-
场景快照保存
-
矢量图导出
-
2D 文档生成
2. 核心实现流程
2.1 基础环境准备
首先需要获取必要的 Three.js 组件和渲染尺寸:
// 获取必要的组件 const renderer = viewer.renderer; // Three.js 渲染器 const scene = viewer.threeScene; // Three.js 场景 const camera = viewer.camera; // Three.js 相机 // 获取实际渲染尺寸 const width = renderer.domElement.width; const height = renderer.domElement.height; // 保存相机原始状态 const originalAspect = camera.aspect; const originalMatrix = camera.matrix.clone(); const originalProjectionMatrix = camera.projectionMatrix.clone();
2.2 场景对象过滤
在导出之前,需要处理场景中的对象可见性:
// 保存和设置场景对象可见性 const originalVisibility = new Map(); scene.traverse((object) => { // 保存原始可见性状态 originalVisibility.set(object, object.visible); // 隐藏使用 LineMaterial 的线段 if (object.material && object.material.isLineMaterial) { object.visible = false; } });
2.3 SVG 渲染设置
使用 Three.js 的 SVGRenderer 进行渲染:
// 创建 SVG 渲染器 const svgRenderer = new SVGRenderer(); svgRenderer.setSize(width, height); // 更新相机参数以匹配渲染尺寸 camera.aspect = width / height; camera.updateProjectionMatrix();
2.4 生成 SVG 文件
将场景渲染为 SVG 并创建文件:
// 渲染场景到 SVG svgRenderer.render(scene, camera); // 获取 SVG 内容并添加必要的头信息 const svgContent = svgRenderer.domElement.outerHTML; const finalSvgContent = `<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> ${svgContent}`; // 创建文件对象 const blob = new Blob([finalSvgContent], { type: 'image/svg+xml' }); const file = new File([blob], 'plotLayer.svg', { type: 'image/svg+xml' });
2.5 清理和状态恢复
确保场景状态被正确恢复:
// 恢复场景中物体的可见性 originalVisibility.forEach((visible, object) => { object.visible = visible; }); // 恢复相机状态 camera.aspect = originalAspect; camera.matrix.copy(originalMatrix); camera.projectionMatrix.copy(originalProjectionMatrix); camera.updateProjectionMatrix(); // 清理 SVG 渲染器 if (svgRenderer.domElement && svgRenderer.domElement.parentNode) { svgRenderer.domElement.parentNode.removeChild(svgRenderer.domElement); }
3. 完整实现代码
export function getScreenPlotEntity() { return new Promise(async (resolve, reject) => { try { let viewer = useViewer(); if (!viewer) { throw new Error('Viewer not initialized'); } const renderer = viewer.renderer; const scene = viewer.threeScene; const camera = viewer.camera; if (!renderer || !scene || !camera) { throw new Error('Required Three.js components not initialized'); } // 获取实际渲染尺寸 const width = renderer.domElement.width; const height = renderer.domElement.height; // 保存相机状态 const originalAspect = camera.aspect; const originalMatrix = camera.matrix.clone(); const originalProjectionMatrix = camera.projectionMatrix.clone(); try { // SVG 渲染器设置 const svgRenderer = new SVGRenderer(); svgRenderer.setSize(width, height); // 更新相机参数 camera.aspect = width / height; camera.updateProjectionMatrix(); // 处理场景对象可见性 const originalVisibility = new Map(); scene.traverse((object) => { originalVisibility.set(object, object.visible); if (object.material && object.material.isLineMaterial) { object.visible = false; } }); // 渲染和创建文件 svgRenderer.render(scene, camera); const svgContent = svgRenderer.domElement.outerHTML; const finalSvgContent = `<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> ${svgContent}`; const blob = new Blob([finalSvgContent], { type: 'image/svg+xml' }); const file = new File([blob], 'plotLayer.svg', { type: 'image/svg+xml' }); resolve(file); } finally { // 恢复场景状态 camera.aspect = originalAspect; camera.matrix.copy(originalMatrix); camera.projectionMatrix.copy(originalProjectionMatrix); camera.updateProjectionMatrix(); // 恢复场景中物体的可见性 originalVisibility.forEach((visible, object) => { object.visible = visible; }); } } catch (error) { console.error('SVG export failed:', error); reject(error); } }); }
4. 使用方法
4.1 基本使用
// 导出 SVG 文件 getScreenPlotEntity() .then(file => { console.log('SVG file generated:', file); // 处理生成的文件 }) .catch(error => { console.error('Failed to generate SVG:', error); });
4.2 文件下载示例
getScreenPlotEntity() .then(file => { const url = URL.createObjectURL(file); const link = document.createElement('a'); link.href = url; link.download = file.name; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); });
代码地址