tz-stl-design
v6.10.2609
Published
beta: x.x.0-beta.x, x.x.x
Readme
TuZhi
TuZhi 是一个基于 TypeScript、Vite 和 Babylon.js 的 3D 刀具预览工具。它提供场景初始化、STL 模型导入、刀具参数 JSON 解析、尺寸标注和点击事件,也可以通过 Tool3d 单独使用 Babylon.js 基础工具。
本文档以“如何接入和调用”为主,适用于直接运行本项目或在 Vue 页面中集成构建后的库。
运行项目
环境要求:Node.js 18 或更高版本。
页面容器与初始化
创建 App 之前,页面必须已经有一个 ID 为 web3d 的 DOM 元素。
<div id="web3d"></div>容器必须有明确的宽高,否则 canvas 没有可用的显示区域。项目内页面使用全屏布局:
html,
body,
#web3d {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
position: relative;
}初始化会创建 Babylon.js Engine、Scene、相机、默认灯光、方向立方体以及持续渲染循环:
import { App } from 'tz-stl-design';
const app = new App();在非 Vue 页面中,应在 DOM 创建完成后初始化,例如在入口模块执行 new App()。如果容器不存在,初始化会抛出 Div with id web3d not found。
方向立方体根据主输入设备自动适配样式:PC 保持原有的 84px 立方体;无悬停能力、使用粗指针的触屏设备(手机、平板)采用 64px 紧凑立方体,调整右上角间距并避让屏幕安全区域。移动端保留方向切换、斜视图和锁定视角功能,正面点击区域及锁定按钮高度为 44px。仅缩小 PC 浏览器窗口不会触发移动端样式。
方向切换动画结束后会恢复相机控制,触屏仍使用单指旋转、双指平移或缩放。恢复控制时不会重复绑定指针输入;视角已锁定时,继续保持指针控制禁用。
App 是应用主实例,常用管理器可通过以下属性访问:
| 属性 | 用途 |
| --- | --- |
| app.apiMgr | 高层刀具导入 API。 |
| app.eventMgr | 应用事件中心,使用 Node.js EventEmitter 的 on、off 等方法。 |
Vue 中使用
必须等 Vue 将容器挂载到 DOM 后再创建 App。保留固定的 id="web3d";库内部通过这个 ID 查找容器,ref 仅用于 Vue 中检查挂载状态。请为容器设置明确高度。
<template>
<div id="web3d" ref="viewer"></div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue';
import { App } from 'tz-stl-design';
const viewer = ref<HTMLDivElement | null>(null);
let app: App | undefined;
const onDimensionLabelClick = (payload: DimensionLabelClickPayload) => {
console.log('点击尺寸标注:', payload.label, payload.category, payload.dimensionValue);
};
onMounted(async () => {
if (!viewer.value) {
throw new Error('3D 容器尚未挂载');
}
app = new App();
app.eventMgr.center.on('dimension-label-click', onDimensionLabelClick);
try {
await app.apiMgr.import('xxx/models/tool.stl', 'xxx/models/tool.json');
} catch (error) {
console.error('刀具模型加载失败:', error);
}
});
onBeforeUnmount(() => {
if (!app) return;
app.eventMgr.center.off('dimension-label-click', onDimensionLabelClick);
app.clean();
app.sceneMgr.engine.stopRenderLoop();
app.sceneMgr.scene.dispose();
app.sceneMgr.engine.dispose();
app = undefined;
});
</script>
<style scoped>
#web3d {
width: 100%;
height: 600px;
overflow: hidden;
position: relative;
}
</style>将示例路径替换为项目可访问的模型地址。跨域托管 STL/JSON 时,服务器需要允许页面来源访问。组件销毁时示例会移除业务事件监听并释放场景资源;目前场景初始化注册的 window.resize 监听器没有对应的移除 API。如果 Vue 路由会反复创建/销毁此组件,建议将 3D 视图放在生命周期稳定的父级中,避免反复创建 App。
ApiManager 接口说明
app.apiMgr 是面向前端的高层接口。它将 STL 导入、模型变换、相机适配和刀具尺寸标注组合为完整流程;业务页面通常应优先调用这里的方法,而不是自行拼接底层 Tool3d 工具。
| 方法 | 签名 | 返回值 | 用途 |
| --- | --- | --- | --- |
| import | import(stlUrl?: string, jsonUrl?: string) | Promise<void> | 导入匹配的 STL 和刀具 JSON,并自动生成尺寸标注。 |
| clear | clear() | void | 清除所有由 STL 工具导入的网格和所有尺寸标注。 |
调试模型面板
在应用中按 Ctrl+D 可打开或关闭调试模型面板,也可以使用面板中的“退出”按钮关闭。选择钻头、铣刀、铰刀或丝锥及其具体型号后,点击“加载模型”会先清空当前模型和尺寸标注,再从测试资源目录导入对应的 STL 与 JSON 文件。面板显示时,可按 ↑ 或 ↓ 在当前产品目录的上一个或下一个型号间循环切换,并立即加载所选模型;加载期间不会响应方向键。
import(stlUrl?, jsonUrl?)
导入 STL、应用默认变换、适配相机,再读取 JSON 并根据刀具参数生成尺寸标注。
await app.apiMgr.import('xxx/models/tool.stl', 'xxx/models/tool.json');| 参数 | 类型 | 说明 |
| --- | --- | --- |
| stlUrl | string,可选 | STL 文件 URL。 |
| jsonUrl | string,可选 | 与 STL 对应的刀具参数 JSON URL。 |
两个参数省略时会使用代码内置的示例 STL/JSON 地址。实际项目建议明确传入一对匹配的 URL。import() 返回 Promise<void>,不返回网格对象;STL 或 JSON 加载失败时 Promise 会 reject,应使用 try/catch 处理。JSON 需符合 ToolData 数据结构。
该高层导入流程假设 STL 模型的尺寸单位为毫米,会将网格缩放 0.001 后按米制坐标显示;JSON 刀具尺寸按毫米读取。不要将已按米建模的 STL 不经换算传给此流程。
调用时不会自动移除之前导入的模型或标注。加载另一把刀具前应先调用 app.apiMgr.clear(),再等待新的 import() 完成:
async function loadTool(stlUrl: string, jsonUrl: string) {
app.apiMgr.clear();
try {
await app.apiMgr.import(stlUrl, jsonUrl);
} catch (error) {
console.error('刀具模型加载失败:', error);
}
}clear()
释放 Tool3d.stl 已登记的 STL 网格,并释放 Tool3d.dimension 已创建的全部尺寸标注:
app.apiMgr.clear();该方法不会销毁场景、相机、默认灯光或渲染引擎;清空刀具后仍可继续调用 import()。它也不会释放通过 mesh、material、texture 等其他工具单独创建的资源。
事件:dimension-label-click
当用户在 Babylon.js 场景中按下尺寸标注的文字标签时,应用会通过 app.eventMgr.center 同步触发 dimension-label-click。只有由 app.sceneMgr.tool3d.dimension.create() 创建的标注文字会触发该事件;尺寸线、箭头、STL 模型及其他网格不会触发它。
事件名常量为 DIMENSION_LABEL_CLICK_EVENT,其值为 'dimension-label-click'。未引入常量时可直接使用该字符串:
import { DIMENSION_LABEL_CLICK_EVENT } from './src/core/managers/eventManager';
const onDimensionLabelClick = (payload: DimensionLabelClickPayload) => {
console.log(payload.label, payload.category);
};
app.eventMgr.center.on(DIMENSION_LABEL_CLICK_EVENT, onDimensionLabelClick);事件负载为单个对象:
interface DimensionLabelClickPayload {
/** 尺寸标签当前显示的文本,例如 "10"、"∅6" 或 "118°"。 */
label: string;
/** 创建标注时传入的 category;未传入时为 "default"。 */
category: string;
/**
* 与标注关联的刀具尺寸参数。由 ApiManager 根据 ToolData 创建的参数标注会提供该值;
* 静态自定义标注则可能为 undefined。
*/
dimensionValue?: DimensionValue;
/** 根据当前尺寸参数仅刷新标签文字和公差,不重建尺寸线、箭头或标注位置。 */
updateDisplayContent(): void;
}category 用于让前端区分业务尺寸。由 ApiManager.import() 自动生成的刀具参数标注会传出对应的 dimensionValue。监听器可以直接修改该对象,再调用 updateDisplayContent() 立即刷新标签:
const onDimensionLabelClick = ({ category, dimensionValue, updateDisplayContent }: DimensionLabelClickPayload) => {
if (category !== 'L' || !dimensionValue) return;
dimensionValue.value = 100;
dimensionValue.tol.top = 0.02;
dimensionValue.tol.low = -0.02;
updateDisplayContent();
};updateDisplayContent() 只会重绘标签文字与公差;它不会根据新的尺寸值调整标注几何、引出线或箭头位置。如需重新计算标注位置,应清除并重新导入模型。
创建自定义标注时应为需要交互的尺寸传入稳定的业务标识。若需要支持参数编辑,则同时传入 dimensionValue 和 getDisplayContent:
import { Vector3 } from 'babylonjs';
const dimensionValue: DimensionValue = {
value: 50,
tol: { type: 0, top: 0, low: 0 },
};
app.sceneMgr.tool3d.dimension.create({
kind: 'linear',
start: new Vector3(0, 0, 0),
end: new Vector3(0.05, 0, 0),
info: String(dimensionValue.value),
category: 'overall-length',
dimensionValue,
getDisplayContent: () => ({
label: String(dimensionValue.value),
}),
direction: '-z',
});const onDimensionLabelClick = ({ category, dimensionValue, updateDisplayContent }: DimensionLabelClickPayload) => {
if (category !== 'overall-length' || !dimensionValue) return;
dimensionValue.value = 60;
updateDisplayContent();
};
app.eventMgr.center.on('dimension-label-click', onDimensionLabelClick);
// 解绑时必须传入与 on() 相同的函数引用。
app.eventMgr.center.off('dimension-label-click', onDimensionLabelClick);事件由 Node.js EventEmitter 分发,同一个标签按下时会依次执行所有已注册监听器;监听器中的异常会影响当前事件调用链。因此监听器应只处理 UI 状态或业务逻辑,异步操作应自行处理错误。Vue 组件应在 onMounted 后注册、在 onBeforeUnmount 中使用相同的函数引用调用 off(),避免组件销毁后仍响应事件。
刀具资源约定
刀具资源位于 资料/3D材料/3D材料/材料/,按铰刀、丝锥、铣刀、钻头等类型分层。资源通常包括二维 CAD 图纸(DWG)、刀具参数/模型描述(JSON)和三维模型(STL)。新增资源时,应保持同一刀具名称在各类型目录间对应,并沿用所在类别已有的目录大小写及命名方式。
