Autodesk Forge Viewer切换至First Object选择模式卡顿问题求助
Autodesk Forge Viewer切换回First Object模式卡顿加载缓慢的解决方案
问题分析
切换至Leaf Object模式时流畅,但切回First Object模式卡顿,核心原因是:
- First Object模式需要遍历模型层级树,找到点击位置最顶层的父节点,当模型层级复杂或使用AggregatedView加载多模型时,层级遍历的计算量会急剧上升,占用大量CPU资源。
- Leaf Object模式仅选择最底层几何节点,计算逻辑简单,因此性能表现更好。
现有代码的问题点
- 重复绑定事件:
loadManifest函数中每次加载模型都会绑定AGGREGATE_SELECTION_CHANGED_EVENT,导致多个重复监听叠加触发,加重性能负担。 - 事件绑定错误:
viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, switchSelectionMode(1))直接执行了函数,而非传递回调,导致模式切换逻辑错误触发。 - 模式切换优化不彻底:现有
switchSelectionMode仅禁用了请求,未处理选择状态清理和渲染暂停,无法完全缓解卡顿。
解决方案
1. 修复事件绑定
- 将
AGGREGATE_SELECTION_CHANGED_EVENT的监听移至Viewer初始化后,确保仅绑定一次。 - 修正
SELECTION_CHANGED_EVENT的绑定方式,使用箭头函数或bind传递参数。
2. 优化模式切换逻辑
- 切换前清理当前选择状态,减少层级遍历的计算量。
- 切换过程中暂停渲染,完成后恢复,避免界面卡顿。
3. 调整加载与配置策略
- 确保所有模型加载完成后再允许切换选择模式,避免加载过程中资源竞争。
修改后的完整代码
Autodesk.Viewing.Initializer(options, function() { const viewerDiv = document.getElementById('forgeViewer'); const viewer = new Autodesk.Viewing.GuiViewer3D(viewerDiv); viewer.start(); // 仅绑定一次事件 viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, onToolbarCreated); viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, Loadedevent); // 初始化AggregatedView const view = new Autodesk.Viewing.AggregatedView(); view.init(viewerDiv, options3d); const aggregatedViewer = view.viewer; // 设置自定义配置 const customProfileSettings = { settings: { reverseMouseZoomDir: true, reverseHorizontalLookDirection: true, selectionMode: 1 // 1 = First Object } }; const customProfile = new Autodesk.Viewing.Profile(customProfileSettings); viewer.setProfile(customProfile); // 修正事件绑定:使用箭头函数传递参数 viewer.addEventListener(Autodesk.Viewing.SELECTION_CHANGED_EVENT, () => switchSelectionMode(1)); // 优化Viewer性能设置 viewer.setQualityLevel(false, false); viewer.setGroundShadow(false); viewer.setGroundReflection(false); viewer.setProgressiveRendering(true); viewer.setNavigationLock(true); // 临时锁定导航,切换完成后解锁 // 加载所有模型,完成后再启用交互 const tasks = models.map(md => loadManifest(md.urn, aggregatedViewer)); Promise.all(tasks) .then(docs => Promise.resolve(docs.map(doc => { const bubbles = doc.getRoot().search({ type: 'geometry', role: '3d' }); return bubbles[0] || null; }))) .then(bubbles => { view.setNodes(bubbles); viewer.setNavigationLock(false); // 模型加载完成后解锁导航 // 绑定聚合选择事件(仅一次) aggregatedViewer.addEventListener(Autodesk.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, handleAggregateSelection); }); }); function loadManifest(documentId, viewer) { return new Promise((resolve, reject) => { const onDocumentLoadSuccess = (doc) => { doc.downloadAecModelData(() => resolve(doc)); }; Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, reject); }); } // 聚合选择事件处理(仅绑定一次) function handleAggregateSelection(event) { if (!event.selections || event.selections.length !== 1) return; const selSet = event.selections[0]; const dbId = selSet.dbIdArray[0]; const model = selSet.model; model.getProperties(dbId, function(data) { if (FromPage === '') { if (FromSelection === '') { if (Count === 0) { let instanceTree = model.getInstanceTree(); let parentId = instanceTree.getNodeParentId(dbId); var objectid = parentId + 1; if (objectid === PreviousParentId) { Count = 1; selectionchangecount = 0; } else { viewer.select(parentId, model, Autodesk.Viewing.SelectionType.OVERLAYED); Count = 1; selectionchangecount = 0; } PreviousParentId = parentId; } else { itemobject = dbId; selectionchangecount++; if (selectionchangecount === fileselectioncount) { Count = 0; selectionchangecount = 0; } } } else { selectionchangecount++; if (selectionchangecount === filecount) { FromSelection = ''; selectionchangecount = 0; } itemobject = dbId; } } else { FromPage = ''; itemobject = dbId; } }); } // 优化后的选择模式切换函数 function switchSelectionMode(newMode) { if (newMode === viewer.getSelectionMode()) return; // 清理当前选择状态 viewer.clearSelection(); // 暂停渲染和请求 viewer.impl.disableRendering(true); viewer.impl.disableRequests(true); // 切换模式 viewer.setSelectionMode(newMode); // 恢复渲染和请求,强制刷新视图 viewer.impl.disableRequests(false); viewer.impl.disableRendering(false); viewer.impl.invalidate(true); }
额外优化建议
- 如果模型层级固定,可以预缓存所有节点的父节点映射,避免每次选择时重复遍历instanceTree。
- 对于超大型模型,考虑使用
viewer.loadDocumentNode时指定加载层级,只加载必要的层级数据。
内容的提问来源于stack exchange,提问作者E.M Pradeep
相关产品推荐
相关产品推荐

