如何在单网页加载2D及3D Forge查看器并实现双向元素联动
现有初始化代码问题修正
你的初始化逻辑存在两个明显问题:
- 变量名不匹配:你声明的查看器实例变量是
firstModel和secondModel,后续回调里用的是未定义的viewer1、viewer2,运行时会直接报错 - 重复调用初始化方法:
Autodesk.Viewing.Initializer只需调用一次即可,不需要为两个查看器分别初始化,只要用同一套授权token配置初始化一次,在回调中依次初始化两个查看器就行
正确的初始化代码示例:
let viewer2D, viewer3D; let isSyncing = false; // 防止联动时事件循环触发的锁变量 const options = { env: 'AutodeskProduction', accessToken: '你的访问token' }; // 全局只需初始化一次 Autodesk.Viewing.Initializer(options, async function() { // 初始化2D图纸查看器 viewer2D = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv1')); viewer2D.start(); await viewer2D.loadDocumentNode(/* 你的2D文档节点参数 */); // 初始化3D模型查看器 viewer3D = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('MyViewerDiv2')); viewer3D.start(); await viewer3D.loadDocumentNode(/* 你的3D文档节点参数 */); // 两个模型都加载完成后绑定联动事件、构建ID映射 buildIdMapping(); bindSyncEvents(); });
双向联动功能实现
要实现2D、3D的双向同步,核心是先建立两端构件的ID映射关系,再监听两端的操作事件做同步处理,具体实现步骤如下:
1. 构建2D-3D构件ID映射表
同一套Revit文件导出的2D图纸和3D模型中,同一个构件的externalId是全局一致的,可以以此为桥梁构建两端dbId的映射:
let dbId2DTo3D = new Map(); let dbId3DTo2D = new Map(); async function buildIdMapping() { // 批量获取2D所有构件的externalId const 2dProps = await new Promise(resolve => { viewer2D.model.getBulkProperties(viewer2D.model.getInstanceTree().getRootId(), ['externalId'], resolve); }); // 批量获取3D所有构件的externalId const 3dProps = await new Promise(resolve => { viewer3D.model.getBulkProperties(viewer3D.model.getInstanceTree().getRootId(), ['externalId'], resolve); }); // 建立双向映射关系 const extIdTo3dDbId = new Map(3dProps.map(p => [p.externalId, p.dbId])); 2dProps.forEach(p => { const matched3dDbId = extIdTo3dDbId.get(p.externalId); if (matched3dDbId) { dbId2DTo3D.set(p.dbId, matched3dDbId); dbId3DTo2D.set(matched3dDbId, p.dbId); } }); }
2. 选中与隔离联动
监听两个查看器的选中事件,同步选中和隔离对应构件,通过锁变量避免事件循环触发重复同步:
function bindSyncEvents() { // 监听2D侧选中事件 viewer2D.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, (e) => { if (isSyncing) return; isSyncing = true; const selected2dDbIds = e.selections.map(s => s.dbIdArray).flat(); const mapped3dDbIds = selected2dDbIds.map(id => dbId2DTo3D.get(id)).filter(Boolean); // 同步到3D侧:选中+隔离 viewer3D.select(mapped3dDbIds); viewer3D.isolate(mapped3dDbIds); isSyncing = false; }); // 监听3D侧选中事件 viewer3D.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, (e) => { if (isSyncing) return; isSyncing = true; const selected3dDbIds = e.selections.map(s => s.dbIdArray).flat(); const mapped2dDbIds = selected3dDbIds.map(id => dbId3DTo2D.get(id)).filter(Boolean); // 同步到2D侧:选中+隔离 viewer2D.select(mapped2dDbIds); viewer2D.isolate(mapped2dDbIds); isSyncing = false; }); }
3. 颜色修改同步
你可以封装统一的颜色修改方法,调用时两端同时设置主题色即可:
function syncElementColor(dbId, color, isFrom2D = true) { const rgbColor = new THREE.Vector4(color.r/255, color.g/255, color.b/255, color.a || 1); if (isFrom2D) { const target3dDbId = dbId2DTo3D.get(dbId); viewer2D.setThemingColor(dbId, rgbColor); target3dDbId && viewer3D.setThemingColor(target3dDbId, rgbColor); } else { const target2dDbId = dbId3DTo2D.get(dbId); viewer3D.setThemingColor(dbId, rgbColor); target2dDbId && viewer2D.setThemingColor(target2dDbId, rgbColor); } }
如果需要监听用户通过查看器界面修改颜色的操作,绑定对应的颜色修改事件触发上述同步逻辑即可。
内容的提问来源于stack exchange,提问作者Abed Aarabi
相关产品推荐
相关产品推荐

