前阵子有些 Autodesk Forge 圈的朋友们都在询问同一个问题『要怎么在 Viewer 的自带右键菜单上添加自定义项目或是只显示自订义项目』~ 以下将针对『在自带右键菜单上添加自定义项目』和『只显示自订义项目的右键菜单』进行说明。
一、 在自带右键菜单上添加自定义项目:
在自带右键菜单上添加自定义项目是非常容易的,Forge Viewer 提供了一个 API 让使用者可以非常轻易的在自带菜单上添加自个的项目,而你需要做的就是像下面这段代码一样做一个简单的 API 调用。下面这个例子会在右键菜单上添加两个新项目,一个是『改变已选构件的颜色成红色(Override color of selected elements to red)』,另一个是『回复颜色变更(Clear overridden corlor)』:
viewer.registerContextMenuCallback( 'MyChangingColorMenuItems', ( menu, status ) => {
if( status.hasSelected ) {
menu.push({
title: 'Override color of selected elements to red',
target: () => {
const selSet = this.viewer.getSelection();
this.viewer.clearSelection();
const color = new THREE.Vector4( 255 / 255, 0, 0, 1 );
for( let i = 0; i < selSet.length; i++ ) {
this.viewer.setThemingColor( selSet[i], color );
}
}
});
} else {
menu.push({
title: 'Clear overridden corlor',
target: () => {
this.viewer.clearThemingColors();
}
});
}
});
在执行完上面的代码后就在右键菜单上看到这两个项目:
- 『改变已选构件的颜色成红色(Override color of selected elements to red)』项目将会在有构件被选中时在菜单上显示:
- 『回复颜色变更(Clear overridden corlor)』项目会在没有选中任何构件时出现:
但一般情况下,我们会将上面的代码放到一个自定义括展里头,让我们可以灵活的使用:
class MyMenuItemExtension extends Autodesk.Viewing.Extension {
constructor( viewer, options ) {
super( viewer, options );
this.onBuildingContextMenuItem = this.onBuildingContextMenuItem.bind( this );
}
get menuId() {
return 'MyColorContextMenu';
}
onBuildingContextMenuItem( menu, status ) {
if( status.hasSelected ) {
menu.push({
title: 'Override color of selected elements to red',
target: () => {
const selSet = this.viewer.getSelection();
this.viewer.clearSelection();
// Change color of selected elements to the red
const color = new THREE.Vector4( 255 / 255, 0, 0, 1 );
for( let i = 0; i < selSet.length; i++ ) {
this.viewer.setThemingColor( selSet[i], color );
}
}
});
} else {
menu.push({
title: 'Clear overridden corlor',
target: () => {
this.viewer.clearThemingColors();
}
});
}
}
load() {
// Add my owned menu items
this.viewer.registerContextMenuCallback(
this.menuId,
this.onBuildingContextMenuItem
);
return true;
}
unload() {
// Remove all menu items added from this extension
this.viewer.unregisterContextMenuCallback( this.menuId );
return true;
}
}
Autodesk.Viewing.theExtensionManager.registerExtension( 'DemoMenuExtension', MyMenuItemExtension );
二、 只显示自订义项目的右键菜单:
如果上头的代码与你的需求不符合,你可以考虑编写一自订义的右键菜单,一样的他也不会太困难。现在举个例子来说明,像现在如果我想让自带右键菜单上除了自带项目外,还会在点击到不同构件时显示不同的项目;我需要做的就是通过继承 Autodesk.Viewing.Extensions.ViewerObjectContextMenu
和加入 hitTest
相关的逻辑到自定义右键菜单的 buildMenu
函数,就像下面这样:
class MyContextMenu extends Autodesk.Viewing.Extensions.ViewerObjectContextMenu {
constructor( viewer ) {
super( viewer );
}
isWall( dbId ) {
//Logics for determining if selected element is wall or not.
return new Promise( ( resolve, reject ) => {
$.get(
'/api/walls/' + dbId,
( response ) => {
if( response && response.id != 0 ) {
return resolve( true );
}
return resolve( false );
}
)
.error( ( error ) => reject( error ) );
});
}
async buildMenu( event, status ) {
// Get defulat menu items from the super class
const menu = super.buildMenu( event, status );
// Do hitTest to get dbIds
const viewport = this.viewer.container.getBoundingClientRect();
const canvasX = event.clientX - viewport.left;
const canvasY = event.clientY - viewport.top;
const result = this.viewer.impl.hitTest( canvasX, canvasY, false );
if( !result || !result.dbId ) return menu;
let isWall = false;
try {
isWall = await this.isWall( result.dbId );
} catch ( error ) {
isWall = false;
}
if( status.hasSelected && isWall ) {
menu.push({
title: 'Show current surface temperature map',
target: () => {
$.post(
'/api/walls/temperature',
( response ) => {
ViewerUtil.showWallTemperatureMap( response.values );
}
);
}
});
}
return menu;
}
/**
* @override
*/
async show( event ) {
const numSelected = this.viewer.getSelectionCount();
const visibility = this.viewer.getSelectionVisibility();
const status = {
numSelected: numSelected,
hasSelected: ( numSelected > 0 ),
hasVisible: visibility.hasVisible,
hasHidden: visibility.hasHidden
};
const menu = await this.buildMenu( event, status );
this.viewer.runContextMenuCallbacks( menu, status );
if( menu && menu.length > 0 ) {
this.contextMenu.show( event, menu );
}
}
}
class MyContextMenuExtension extends Autodesk.Viewing.Extension {
constructor( viewer, options ) {
super( viewer, options );
}
load() {
// Use my owned context menu.
this.viewer.setContextMenu( new MyContextMenu( this.viewer ) );
return true;
}
unload() {
// Restore default context menu
this.viewer.setContextMenu( new Autodesk.Viewing.Extensions.ViewerObjectContextMenu( this.viewer ) );
return true;
}
}
Autodesk.Viewing.theExtensionManager.registerExtension( 'DemoWallMenuExtension', MyContextMenuExtension );
这样子就会在点击到墙构件显示这个项目『Show current surface temperature map』:
相反的,如果你不想显示菜单上自带的项目,你可以改成继承 Autodesk.Viewing.UI.ObjectContextMenu
。但你点击到墙构件的时候就只会显示『Show current surface temperature map』这个项目,就像下面这个样子:
class MyContextMenu extends Autodesk.Viewing.Extensions.ObjectContextMenu {
constructor( viewer ) {
super( viewer );
}
isWall( dbId ) {
//Logics for determining if selected element is wall or not.
return new Promise( ( resolve, reject ) => {
$.get(
'/api/walls/' + dbId,
( response ) => {
if( response && response.id != 0 ) {
return resolve( true );
}
return resolve( false );
}
)
.error( ( error ) => reject( error ) );
});
}
async buildMenu( event, status ) {
// Get defulat menu items from the super class
let menu = super.buildMenu( event, status );
if( !menu )
menu = [];
// Do hitTest to get dbIds
const viewport = this.viewer.container.getBoundingClientRect();
const canvasX = event.clientX - viewport.left;
const canvasY = event.clientY - viewport.top;
const result = this.viewer.impl.hitTest( canvasX, canvasY, false );
if( !result || !result.dbId ) return menu;
let isWall = false;
try {
isWall = await this.isWall( result.dbId );
} catch ( error ) {
isWall = false;
}
if( status.hasSelected && isWall ) {
menu.push({
title: 'Show current surface temperature map',
target: () => {
$.post(
'/api/walls/temperature',
( response ) => {
ViewerUtil.showWallTemperatureMap( response.values );
}
);
}
});
}
return menu;
}
/**
* @override
*/
async show( event ) {
const numSelected = this.viewer.getSelectionCount();
const visibility = this.viewer.getSelectionVisibility();
const status = {
numSelected: numSelected,
hasSelected: ( numSelected > 0 ),
hasVisible: visibility.hasVisible,
hasHidden: visibility.hasHidden
};
const menu = await this.buildMenu( event, status );
this.viewer.runContextMenuCallbacks( menu, status );
if( menu && menu.length > 0 ) {
this.contextMenu.show( event, menu );
}
}
}
class MyContextMenuExtension extends Autodesk.Viewing.Extension {
constructor( viewer, options ) {
super( viewer, options );
}
load() {
// Use my owned context menu.
this.viewer.setContextMenu( new MyContextMenu( this.viewer ) );
return true;
}
unload() {
// Restore default context menu
this.viewer.setContextMenu( new Autodesk.Viewing.Extensions.ViewerObjectContextMenu( this.viewer ) );
return true;
}
}
Autodesk.Viewing.theExtensionManager.registerExtension( 'DemoWallMenuExtension', MyContextMenuExtension );
希望上面的说明对各位使用 Autodesk Forge 的朋友们有些帮助~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。