添加测试信息
This commit is contained in:
106
demo/index.html
106
demo/index.html
@@ -5,7 +5,8 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>BIM Engine SDK Demo</title>
|
<title>BIM Engine SDK Demo</title>
|
||||||
<script src="../dist/bim-engine-sdk.umd.js"></script>
|
<!-- 从本地 lib 目录加载 SDK 文件 -->
|
||||||
|
<script src="./lib/bim-engine-sdk.umd.js"></script>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -157,6 +158,18 @@
|
|||||||
<button onclick="setCustomTheme()">自定义 (Red)</button>
|
<button onclick="setCustomTheme()">自定义 (Red)</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 5. 3D 引擎 -->
|
||||||
|
<div class="control-group">
|
||||||
|
<h2>🎮 3D 引擎 (Engine3D)</h2>
|
||||||
|
<div class="btn-container">
|
||||||
|
<button class="primary" onclick="initEngine3D()">初始化引擎</button>
|
||||||
|
<button class="primary" onclick="loadModel()">加载模型</button>
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; font-size: 0.85rem; color: #666;">
|
||||||
|
<div>状态: <span id="engine-status">未初始化</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 右侧主区域 -->
|
<!-- 右侧主区域 -->
|
||||||
@@ -170,6 +183,7 @@
|
|||||||
let isLabelVisible = true;
|
let isLabelVisible = true;
|
||||||
let isLocationVisible = true;
|
let isLocationVisible = true;
|
||||||
let customGroupAdded = false;
|
let customGroupAdded = false;
|
||||||
|
let engine3DInitialized = false;
|
||||||
|
|
||||||
// 初始化引擎
|
// 初始化引擎
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
@@ -177,6 +191,7 @@
|
|||||||
const Engine = window.LyzBimEngineSDK.BimEngine;
|
const Engine = window.LyzBimEngineSDK.BimEngine;
|
||||||
try {
|
try {
|
||||||
engine = new Engine('app', { locale: 'zh-CN' });
|
engine = new Engine('app', { locale: 'zh-CN' });
|
||||||
|
// initEngine3D();
|
||||||
console.log('Engine initialized:', engine);
|
console.log('Engine initialized:', engine);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Init failed:', err);
|
console.error('Init failed:', err);
|
||||||
@@ -301,7 +316,94 @@
|
|||||||
componentActive: '#e57373'
|
componentActive: '#e57373'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 3D 引擎操作 ---
|
||||||
|
/**
|
||||||
|
* 初始化 3D 引擎
|
||||||
|
*/
|
||||||
|
function initEngine3D() {
|
||||||
|
if (!engine || !engine.engine) {
|
||||||
|
alert('引擎未创建,请先等待页面加载完成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.engine.isInitialized()) {
|
||||||
|
alert('3D 引擎已经初始化过了');
|
||||||
|
updateEngineStatus('已初始化');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 初始化引擎,使用默认配置
|
||||||
|
const success = engine.initEngine({
|
||||||
|
backgroundColor: 0x333333, // 深色背景
|
||||||
|
version: 'v1', // WebGL 版本
|
||||||
|
showStats: true, // 显示性能统计
|
||||||
|
showViewCube: true // 显示视图立方体
|
||||||
|
});
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
engine3DInitialized = true;
|
||||||
|
updateEngineStatus('已初始化');
|
||||||
|
console.log('✅ 3D 引擎初始化成功');
|
||||||
|
} else {
|
||||||
|
updateEngineStatus('初始化失败');
|
||||||
|
console.error('❌ 3D 引擎初始化失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
updateEngineStatus('初始化错误');
|
||||||
|
console.error('❌ 3D 引擎初始化错误:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 3D 模型
|
||||||
|
*/
|
||||||
|
function loadModel() {
|
||||||
|
if (!engine || !engine.engine) {
|
||||||
|
alert('引擎未创建,请先等待页面加载完成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!engine.engine.isInitialized()) {
|
||||||
|
alert('请先初始化 3D 引擎!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 加载模型文件(从 model 目录)
|
||||||
|
const modelUrl = './model/gujianzhu.glb';
|
||||||
|
|
||||||
|
engine.engine.loadModel(modelUrl, {
|
||||||
|
position: [0, 0, 0], // 初始位置
|
||||||
|
rotation: [0, 0, 0], // 初始旋转
|
||||||
|
scale: [1, 1, 1] // 初始缩放
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ 模型加载请求已发送:', modelUrl);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 模型加载错误:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新引擎状态显示
|
||||||
|
*/
|
||||||
|
function updateEngineStatus(status) {
|
||||||
|
const statusEl = document.getElementById('engine-status');
|
||||||
|
if (statusEl) {
|
||||||
|
statusEl.textContent = status;
|
||||||
|
// 根据状态设置颜色
|
||||||
|
if (status === '已初始化') {
|
||||||
|
statusEl.style.color = '#28a745';
|
||||||
|
} else if (status === '初始化失败' || status === '初始化错误') {
|
||||||
|
statusEl.style.color = '#dc3545';
|
||||||
|
} else {
|
||||||
|
statusEl.style.color = '#666';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
33257
dist/bim-engine-sdk.es.js
vendored
33257
dist/bim-engine-sdk.es.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.es.js.map
vendored
2
dist/bim-engine-sdk.es.js.map
vendored
File diff suppressed because one or more lines are too long
4892
dist/bim-engine-sdk.umd.js
vendored
4892
dist/bim-engine-sdk.umd.js
vendored
File diff suppressed because one or more lines are too long
2
dist/bim-engine-sdk.umd.js.map
vendored
2
dist/bim-engine-sdk.umd.js.map
vendored
File diff suppressed because one or more lines are too long
99
dist/index.d.ts
vendored
99
dist/index.d.ts
vendored
@@ -69,6 +69,7 @@ declare class BimDialog implements IBimComponent {
|
|||||||
private _isInitialized;
|
private _isInitialized;
|
||||||
private unsubscribeTheme;
|
private unsubscribeTheme;
|
||||||
private unsubscribeLocale;
|
private unsubscribeLocale;
|
||||||
|
private rafId;
|
||||||
/**
|
/**
|
||||||
* 构造函数
|
* 构造函数
|
||||||
* @param options 弹窗配置选项
|
* @param options 弹窗配置选项
|
||||||
@@ -97,11 +98,11 @@ declare class BimDialog implements IBimComponent {
|
|||||||
*/
|
*/
|
||||||
private initPosition;
|
private initPosition;
|
||||||
/**
|
/**
|
||||||
* 初始化拖拽功能
|
* 初始化拖拽功能 (性能优化 + 解决粘手)
|
||||||
*/
|
*/
|
||||||
private initDrag;
|
private initDrag;
|
||||||
/**
|
/**
|
||||||
* 初始化缩放功能
|
* 初始化缩放功能 (性能优化 + 解决粘手)
|
||||||
*/
|
*/
|
||||||
private initResize;
|
private initResize;
|
||||||
/**
|
/**
|
||||||
@@ -126,6 +127,7 @@ export declare class BimEngine {
|
|||||||
toolbar: ToolbarManager | null;
|
toolbar: ToolbarManager | null;
|
||||||
buttonGroup: ButtonGroupManager | null;
|
buttonGroup: ButtonGroupManager | null;
|
||||||
dialog: DialogManager | null;
|
dialog: DialogManager | null;
|
||||||
|
engine: EngineManager | null;
|
||||||
get localeManager(): LocaleManager;
|
get localeManager(): LocaleManager;
|
||||||
get themeManager(): ThemeManager;
|
get themeManager(): ThemeManager;
|
||||||
constructor(container: HTMLElement | string, options?: {
|
constructor(container: HTMLElement | string, options?: {
|
||||||
@@ -137,7 +139,12 @@ export declare class BimEngine {
|
|||||||
setTheme(theme: 'dark' | 'light'): void;
|
setTheme(theme: 'dark' | 'light'): void;
|
||||||
setCustomTheme(theme: ThemeConfig): void;
|
setCustomTheme(theme: ThemeConfig): void;
|
||||||
private init;
|
private init;
|
||||||
private createTopLeftGroup;
|
/**
|
||||||
|
* 初始化 3D 引擎组件
|
||||||
|
* 注意:只初始化引擎,不加载模型。模型加载在使用层(如 demo.html)进行
|
||||||
|
* @param options 引擎配置选项(可选)
|
||||||
|
*/
|
||||||
|
initEngine(options?: Omit<EngineOptions, 'container'>): boolean;
|
||||||
private updateTheme;
|
private updateTheme;
|
||||||
destroy(): void;
|
destroy(): void;
|
||||||
}
|
}
|
||||||
@@ -221,6 +228,18 @@ export declare interface ClickPayload {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export declare function createEngine(r) {
|
||||||
|
const e = r.version || "v1";
|
||||||
|
switch (e) {
|
||||||
|
case "v2":
|
||||||
|
return new Uc(r);
|
||||||
|
case "v1":
|
||||||
|
return new L_(r);
|
||||||
|
:
|
||||||
|
return console.warn(`Version '${e}' not found. Falling back to v2.`), new Uc(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 弹窗颜色配置
|
* 弹窗颜色配置
|
||||||
*/
|
*/
|
||||||
@@ -311,6 +330,65 @@ declare type DialogPosition = 'center' | 'top-left' | 'top-center' | 'top-right'
|
|||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3D 引擎管理器
|
||||||
|
* 负责连接 Engine 组件和 BimEngine,向外部暴露简化的 API
|
||||||
|
* 采用延迟初始化模式,用户需主动调用 initialize() 方法
|
||||||
|
*/
|
||||||
|
declare class EngineManager {
|
||||||
|
/** 3D 引擎挂载的父容器 */
|
||||||
|
private container;
|
||||||
|
/** 3D 引擎组件实例 */
|
||||||
|
private engine;
|
||||||
|
/**
|
||||||
|
* 构造函数
|
||||||
|
* @param container 3D 引擎挂载的目标容器
|
||||||
|
*/
|
||||||
|
constructor(container: HTMLElement);
|
||||||
|
/**
|
||||||
|
* 初始化 3D 引擎
|
||||||
|
* @param options 引擎配置选项(可选,如果不提供则使用默认配置)
|
||||||
|
* @returns 是否初始化成功
|
||||||
|
*/
|
||||||
|
initialize(options?: Omit<EngineOptions, 'container'>): boolean;
|
||||||
|
/**
|
||||||
|
* 检查 3D 引擎是否已初始化
|
||||||
|
*/
|
||||||
|
isInitialized(): boolean;
|
||||||
|
/**
|
||||||
|
* 加载 3D 模型
|
||||||
|
* @param url 模型文件 URL
|
||||||
|
* @param options 加载选项(位置、旋转、缩放)
|
||||||
|
*/
|
||||||
|
loadModel(url: string, options?: ModelLoadOptions): void;
|
||||||
|
/**
|
||||||
|
* 获取原始 3D 引擎实例
|
||||||
|
* 用于直接调用第三方引擎的其他 API
|
||||||
|
*/
|
||||||
|
getEngine(): any;
|
||||||
|
/**
|
||||||
|
* 销毁 3D 引擎实例
|
||||||
|
*/
|
||||||
|
destroy(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引擎配置选项
|
||||||
|
* 用于 Engine 组件的初始化
|
||||||
|
*/
|
||||||
|
export declare interface EngineOptions {
|
||||||
|
/** 容器元素 */
|
||||||
|
container: HTMLElement;
|
||||||
|
/** 背景颜色(十六进制数字,如 0x333333) */
|
||||||
|
backgroundColor?: number;
|
||||||
|
/** WebGL 版本 */
|
||||||
|
version?: 'v1' | 'v2';
|
||||||
|
/** 是否显示性能统计 */
|
||||||
|
showStats?: boolean;
|
||||||
|
/** 是否显示视图立方体 */
|
||||||
|
showViewCube?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
/** 二级菜单展开方向 */
|
/** 二级菜单展开方向 */
|
||||||
declare type ExpandDirection = 'up' | 'down' | 'left' | 'right';
|
declare type ExpandDirection = 'up' | 'down' | 'left' | 'right';
|
||||||
|
|
||||||
@@ -384,6 +462,21 @@ declare class LocaleManager {
|
|||||||
*/
|
*/
|
||||||
declare type LocaleType = 'zh-CN' | 'en-US';
|
declare type LocaleType = 'zh-CN' | 'en-US';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型加载选项
|
||||||
|
* 用于配置模型的位置、旋转和缩放
|
||||||
|
*/
|
||||||
|
export declare interface ModelLoadOptions {
|
||||||
|
/** 模型初始位置 [x, y, z] */
|
||||||
|
position?: [number, number, number];
|
||||||
|
/** 模型初始旋转 [x, y, z](弧度) */
|
||||||
|
rotation?: [number, number, number];
|
||||||
|
/** 模型初始缩放 [x, y, z] */
|
||||||
|
scale?: [number, number, number];
|
||||||
|
/** 模型 ID(可选,如果不提供则自动生成) */
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export declare interface OptButton extends ButtonConfig {
|
export declare interface OptButton extends ButtonConfig {
|
||||||
children?: OptButton[];
|
children?: OptButton[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,10 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build"
|
"build": "tsc && vite build",
|
||||||
|
"dev:all": "./dev.sh",
|
||||||
|
"dev:demo": "cd demo && npm run copy-sdk && npm run dev",
|
||||||
|
"dev:demo-vue": "cd demo-vue && npm run copy-sdk && npm run dev"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bim",
|
"bim",
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
.bim-engine-wrapper {
|
.bim-engine-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
/* 添加相对定位作为参照物 */
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
color: #333;
|
color: #bf1d1d;
|
||||||
padding: 20px;
|
|
||||||
background-color: #e16969;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
/* 确保 padding 不会撑大容器 */
|
overflow: hidden;
|
||||||
|
/* 防止内容溢出 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ... (中间代码不变) ... */
|
/* ... (中间代码不变) ... */
|
||||||
|
|
||||||
/* 操作按钮组容器 */
|
/* 操作按钮组容器 */
|
||||||
|
|||||||
@@ -2,11 +2,17 @@ import './bim-engine.css';
|
|||||||
import { ToolbarManager } from './managers/toolbar-manager';
|
import { ToolbarManager } from './managers/toolbar-manager';
|
||||||
import { ButtonGroupManager } from './managers/button-group-manager';
|
import { ButtonGroupManager } from './managers/button-group-manager';
|
||||||
import { DialogManager } from './managers/dialog-manager';
|
import { DialogManager } from './managers/dialog-manager';
|
||||||
|
import { EngineManager } from './managers/engine-manager';
|
||||||
|
import type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||||
import { localeManager } from './services/locale';
|
import { localeManager } from './services/locale';
|
||||||
import { themeManager } from './services/theme';
|
import { themeManager } from './services/theme';
|
||||||
import type { LocaleType } from './locales/types';
|
import type { LocaleType } from './locales/types';
|
||||||
import type { ThemeType, ThemeConfig } from './themes/types';
|
import type { ThemeType, ThemeConfig } from './themes/types';
|
||||||
|
|
||||||
|
export type { EngineOptions, ModelLoadOptions };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export class BimEngine {
|
export class BimEngine {
|
||||||
private container: HTMLElement;
|
private container: HTMLElement;
|
||||||
private wrapper: HTMLElement | null = null;
|
private wrapper: HTMLElement | null = null;
|
||||||
@@ -15,11 +21,18 @@ export class BimEngine {
|
|||||||
public toolbar: ToolbarManager | null = null; // 底部专用
|
public toolbar: ToolbarManager | null = null; // 底部专用
|
||||||
public buttonGroup: ButtonGroupManager | null = null; // 通用
|
public buttonGroup: ButtonGroupManager | null = null; // 通用
|
||||||
public dialog: DialogManager | null = null;
|
public dialog: DialogManager | null = null;
|
||||||
|
public engine: EngineManager | null = null; // 3D 引擎管理器
|
||||||
|
|
||||||
public get localeManager() { return localeManager; }
|
public get localeManager() { return localeManager; }
|
||||||
public get themeManager() { return themeManager; }
|
public get themeManager() { return themeManager; }
|
||||||
|
|
||||||
constructor(container: HTMLElement | string, options?: { locale?: LocaleType; theme?: ThemeType }) {
|
constructor(
|
||||||
|
container: HTMLElement | string,
|
||||||
|
options?: {
|
||||||
|
locale?: LocaleType;
|
||||||
|
theme?: ThemeType;
|
||||||
|
}
|
||||||
|
) {
|
||||||
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
const el = typeof container === 'string' ? document.getElementById(container) : container;
|
||||||
if (!el) throw new Error('Container not found');
|
if (!el) throw new Error('Container not found');
|
||||||
this.container = el;
|
this.container = el;
|
||||||
@@ -47,13 +60,14 @@ export class BimEngine {
|
|||||||
this.wrapper.className = 'bim-engine-wrapper';
|
this.wrapper.className = 'bim-engine-wrapper';
|
||||||
this.container.appendChild(this.wrapper);
|
this.container.appendChild(this.wrapper);
|
||||||
|
|
||||||
// 初始化管理器
|
// 创建 3D 引擎管理器
|
||||||
|
this.engine = new EngineManager(this.wrapper);
|
||||||
|
|
||||||
|
// 初始化其他管理器
|
||||||
this.dialog = new DialogManager(this.wrapper);
|
this.dialog = new DialogManager(this.wrapper);
|
||||||
this.toolbar = new ToolbarManager(this.wrapper);
|
this.toolbar = new ToolbarManager(this.wrapper);
|
||||||
this.buttonGroup = new ButtonGroupManager(this.wrapper);
|
this.buttonGroup = new ButtonGroupManager(this.wrapper);
|
||||||
|
|
||||||
// --- 创建左上角按钮组 (需求 1 & 2) ---
|
|
||||||
this.createTopLeftGroup();
|
|
||||||
|
|
||||||
// 初始主题
|
// 初始主题
|
||||||
this.updateTheme(themeManager.getTheme());
|
this.updateTheme(themeManager.getTheme());
|
||||||
@@ -69,34 +83,23 @@ export class BimEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTopLeftGroup() {
|
/**
|
||||||
if (!this.buttonGroup) return;
|
* 初始化 3D 引擎组件
|
||||||
|
* 注意:只初始化引擎,不加载模型。模型加载在使用层(如 demo.html)进行
|
||||||
|
* @param options 引擎配置选项(可选)
|
||||||
|
*/
|
||||||
|
public initEngine(options?: Omit<EngineOptions, 'container'>): boolean {
|
||||||
|
if (!this.engine) {
|
||||||
|
console.error('[BimEngine] Engine manager not available.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this.topLeftGroup = this.buttonGroup.create({
|
// 调用 manager 的 initialize 方法初始化引擎
|
||||||
position: 'top-left',
|
return this.engine.initialize(options);
|
||||||
direction: 'column',
|
|
||||||
align: 'vertical',
|
|
||||||
backgroundColor: '#ff00ff', // 自定义背景色,不会被主题覆盖
|
|
||||||
showLabel: false
|
|
||||||
});
|
|
||||||
|
|
||||||
this.topLeftGroup.addGroup('main');
|
|
||||||
this.topLeftGroup.addButton({
|
|
||||||
id: 'menu-btn',
|
|
||||||
groupId: 'main',
|
|
||||||
type: 'button',
|
|
||||||
label: 'Menu', // 应该用 translation key
|
|
||||||
icon: '<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>',
|
|
||||||
onClick: () => {
|
|
||||||
alert("点击按钮")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 手动 render 一次以显示
|
|
||||||
this.topLeftGroup.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTheme(theme: ThemeConfig) {
|
private updateTheme(theme: ThemeConfig) {
|
||||||
|
|
||||||
if (this.wrapper) {
|
if (this.wrapper) {
|
||||||
this.wrapper.style.backgroundColor = theme.background;
|
this.wrapper.style.backgroundColor = theme.background;
|
||||||
this.wrapper.style.color = theme.textPrimary;
|
this.wrapper.style.color = theme.textPrimary;
|
||||||
@@ -106,6 +109,7 @@ export class BimEngine {
|
|||||||
public destroy() {
|
public destroy() {
|
||||||
this.toolbar?.destroy();
|
this.toolbar?.destroy();
|
||||||
this.buttonGroup?.destroy();
|
this.buttonGroup?.destroy();
|
||||||
|
this.engine?.destroy();
|
||||||
this.dialog = null;
|
this.dialog = null;
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
background-color: var(--bim-btn-group-section-bg, rgba(17, 17, 17, 0.88));
|
background-color: var(--bim-btn-group-section-bg, rgba(17, 17, 17, 0.88));
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
/* 添加阴影效果,增强视觉层次感 */
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bim-btn-group-root.dir-row .bim-btn-group-section {
|
.bim-btn-group-root.dir-row .bim-btn-group-section {
|
||||||
|
|||||||
@@ -14,11 +14,12 @@
|
|||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
z-index: 1000;
|
z-index: 10001; /* 提高 z-index,确保在第三方 SDK (10000) 之上 */
|
||||||
color: var(--bim-dialog-title-color);
|
color: var(--bim-dialog-title-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
|
pointer-events: auto; /* 确保弹窗可以接收事件 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.bim-dialog-header {
|
.bim-dialog-header {
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export class BimDialog implements IBimComponent {
|
|||||||
private unsubscribeTheme: (() => void) | null = null;
|
private unsubscribeTheme: (() => void) | null = null;
|
||||||
private unsubscribeLocale: (() => void) | null = null;
|
private unsubscribeLocale: (() => void) | null = null;
|
||||||
|
|
||||||
|
// 性能优化:用于存储 requestAnimationFrame 的 ID
|
||||||
|
private rafId: number | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数
|
* 构造函数
|
||||||
* @param options 弹窗配置选项
|
* @param options 弹窗配置选项
|
||||||
@@ -116,7 +119,7 @@ export class BimDialog implements IBimComponent {
|
|||||||
|
|
||||||
if (this.options.id) el.id = this.options.id;
|
if (this.options.id) el.id = this.options.id;
|
||||||
|
|
||||||
// 应用颜色配置到 CSS 变量 (局部作用域)
|
// 应用颜色配置到 CSS 变量
|
||||||
const style = el.style;
|
const style = el.style;
|
||||||
if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);
|
if (this.options.backgroundColor) style.setProperty('--bim-dialog-bg', this.options.backgroundColor);
|
||||||
if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);
|
if (this.options.headerBackgroundColor) style.setProperty('--bim-dialog-header-bg', this.options.headerBackgroundColor);
|
||||||
@@ -139,7 +142,10 @@ export class BimDialog implements IBimComponent {
|
|||||||
const closeBtn = document.createElement('span');
|
const closeBtn = document.createElement('span');
|
||||||
closeBtn.className = 'bim-dialog-close';
|
closeBtn.className = 'bim-dialog-close';
|
||||||
closeBtn.innerHTML = '×';
|
closeBtn.innerHTML = '×';
|
||||||
closeBtn.onclick = () => this.close();
|
// 修复 TS 报错:去掉未使用的参数 e
|
||||||
|
closeBtn.onclick = () => {
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
|
||||||
header.appendChild(title);
|
header.appendChild(title);
|
||||||
header.appendChild(closeBtn);
|
header.appendChild(closeBtn);
|
||||||
@@ -163,6 +169,26 @@ export class BimDialog implements IBimComponent {
|
|||||||
el.appendChild(resizeHandle);
|
el.appendChild(resizeHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 事件拦截核心逻辑 ====================
|
||||||
|
// 定义阻断逻辑:只阻止冒泡,不阻止捕获,也不阻止默认行为(除非显式阻止)
|
||||||
|
const stopPropagation = (e: Event) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 现代浏览器和 3D 引擎 (Three.js/Cesium) 交互事件
|
||||||
|
const events = [
|
||||||
|
'click', 'dblclick', 'contextmenu', 'wheel',
|
||||||
|
'mousedown', 'mouseup', 'mousemove',
|
||||||
|
'touchstart', 'touchend', 'touchmove',
|
||||||
|
'pointerdown', 'pointerup', 'pointermove', 'pointerenter', 'pointerleave', 'pointerover', 'pointerout'
|
||||||
|
];
|
||||||
|
|
||||||
|
// 绑定监听器 (默认冒泡阶段)
|
||||||
|
// 这样内部元素(如关闭按钮)先触发,然后冒泡到这里被拦截,不再传给地图
|
||||||
|
events.forEach(eventType => {
|
||||||
|
el.addEventListener(eventType, stopPropagation, { passive: false });
|
||||||
|
});
|
||||||
|
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +209,6 @@ export class BimDialog implements IBimComponent {
|
|||||||
*/
|
*/
|
||||||
private initPosition() {
|
private initPosition() {
|
||||||
const pos = this.options.position;
|
const pos = this.options.position;
|
||||||
|
|
||||||
const elRect = this.element.getBoundingClientRect();
|
const elRect = this.element.getBoundingClientRect();
|
||||||
|
|
||||||
// 计算相对父容器的定位
|
// 计算相对父容器的定位
|
||||||
@@ -218,7 +243,6 @@ export class BimDialog implements IBimComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 简单的边界检查,防止初始位置溢出
|
|
||||||
left = Math.max(0, Math.min(left, pW - elW));
|
left = Math.max(0, Math.min(left, pW - elW));
|
||||||
top = Math.max(0, Math.min(top, pH - elH));
|
top = Math.max(0, Math.min(top, pH - elH));
|
||||||
|
|
||||||
@@ -227,53 +251,81 @@ export class BimDialog implements IBimComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化拖拽功能
|
* 初始化拖拽功能 (性能优化 + 解决粘手)
|
||||||
*/
|
*/
|
||||||
private initDrag() {
|
private initDrag() {
|
||||||
let startX = 0;
|
let startX = 0;
|
||||||
let startY = 0;
|
let startY = 0;
|
||||||
let startLeft = 0;
|
let startLeft = 0;
|
||||||
let startTop = 0;
|
let startTop = 0;
|
||||||
|
let containerW = 0;
|
||||||
|
let containerH = 0;
|
||||||
|
let elW = 0;
|
||||||
|
let elH = 0;
|
||||||
|
|
||||||
const onMouseDown = (e: MouseEvent) => {
|
const onMouseDown = (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault(); // 阻止默认行为(如选中文本),非常重要,防止卡顿
|
||||||
|
e.stopPropagation(); // 阻止传递给 Three.js
|
||||||
|
|
||||||
startX = e.clientX;
|
startX = e.clientX;
|
||||||
startY = e.clientY;
|
startY = e.clientY;
|
||||||
startLeft = this.element.offsetLeft;
|
startLeft = this.element.offsetLeft;
|
||||||
startTop = this.element.offsetTop;
|
startTop = this.element.offsetTop;
|
||||||
|
|
||||||
document.addEventListener('mousemove', onMouseMove);
|
// 缓存尺寸,减少 reflow
|
||||||
document.addEventListener('mouseup', onMouseUp);
|
containerW = this.container.clientWidth;
|
||||||
|
containerH = this.container.clientHeight;
|
||||||
|
elW = this.element.offsetWidth;
|
||||||
|
elH = this.element.offsetHeight;
|
||||||
|
|
||||||
|
// 关键:使用 capture: true
|
||||||
|
// 确保即使 createDom 阻止了冒泡,document 也能在捕获阶段收到事件
|
||||||
|
document.addEventListener('mousemove', onMouseMove, { capture: true });
|
||||||
|
document.addEventListener('mouseup', onMouseUp, { capture: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
const dx = e.clientX - startX;
|
e.preventDefault();
|
||||||
const dy = e.clientY - startY;
|
e.stopPropagation();
|
||||||
|
|
||||||
let newLeft = startLeft + dx;
|
// 节流优化:使用 requestAnimationFrame
|
||||||
let newTop = startTop + dy;
|
if (this.rafId) return;
|
||||||
|
|
||||||
// 边界限制,防止拖出容器
|
this.rafId = requestAnimationFrame(() => {
|
||||||
const maxLeft = this.container.clientWidth - this.element.offsetWidth;
|
const dx = e.clientX - startX;
|
||||||
const maxTop = this.container.clientHeight - this.element.offsetHeight;
|
const dy = e.clientY - startY;
|
||||||
|
|
||||||
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
let newLeft = startLeft + dx;
|
||||||
newTop = Math.max(0, Math.min(newTop, maxTop));
|
let newTop = startTop + dy;
|
||||||
|
|
||||||
this.element.style.left = `${newLeft}px`;
|
const maxLeft = containerW - elW;
|
||||||
this.element.style.top = `${newTop}px`;
|
const maxTop = containerH - elH;
|
||||||
|
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
|
||||||
|
newTop = Math.max(0, Math.min(newTop, maxTop));
|
||||||
|
|
||||||
|
this.element.style.left = `${newLeft}px`;
|
||||||
|
this.element.style.top = `${newTop}px`;
|
||||||
|
|
||||||
|
this.rafId = null;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onMouseUp = () => {
|
||||||
document.removeEventListener('mousemove', onMouseMove);
|
if (this.rafId) {
|
||||||
document.removeEventListener('mouseup', onMouseUp);
|
cancelAnimationFrame(this.rafId);
|
||||||
|
this.rafId = null;
|
||||||
|
}
|
||||||
|
// 移除监听
|
||||||
|
document.removeEventListener('mousemove', onMouseMove, { capture: true });
|
||||||
|
document.removeEventListener('mouseup', onMouseUp, { capture: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
this.header.addEventListener('mousedown', onMouseDown);
|
this.header.addEventListener('mousedown', onMouseDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化缩放功能
|
* 初始化缩放功能 (性能优化 + 解决粘手)
|
||||||
*/
|
*/
|
||||||
private initResize() {
|
private initResize() {
|
||||||
const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;
|
const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;
|
||||||
@@ -292,24 +344,38 @@ export class BimDialog implements IBimComponent {
|
|||||||
startW = this.element.offsetWidth;
|
startW = this.element.offsetWidth;
|
||||||
startH = this.element.offsetHeight;
|
startH = this.element.offsetHeight;
|
||||||
|
|
||||||
document.addEventListener('mousemove', onMouseMove);
|
// 关键:使用 capture: true
|
||||||
document.addEventListener('mouseup', onMouseUp);
|
document.addEventListener('mousemove', onMouseMove, { capture: true });
|
||||||
|
document.addEventListener('mouseup', onMouseUp, { capture: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseMove = (e: MouseEvent) => {
|
const onMouseMove = (e: MouseEvent) => {
|
||||||
const dx = e.clientX - startX;
|
e.preventDefault();
|
||||||
const dy = e.clientY - startY;
|
e.stopPropagation();
|
||||||
|
|
||||||
const newW = Math.max(this.options.minWidth || 100, startW + dx);
|
if (this.rafId) return;
|
||||||
const newH = Math.max(this.options.minHeight || 50, startH + dy);
|
|
||||||
|
|
||||||
this.element.style.width = `${newW}px`;
|
this.rafId = requestAnimationFrame(() => {
|
||||||
this.element.style.height = `${newH}px`;
|
const dx = e.clientX - startX;
|
||||||
|
const dy = e.clientY - startY;
|
||||||
|
|
||||||
|
const newW = Math.max(this.options.minWidth || 100, startW + dx);
|
||||||
|
const newH = Math.max(this.options.minHeight || 50, startH + dy);
|
||||||
|
|
||||||
|
this.element.style.width = `${newW}px`;
|
||||||
|
this.element.style.height = `${newH}px`;
|
||||||
|
|
||||||
|
this.rafId = null;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onMouseUp = () => {
|
||||||
document.removeEventListener('mousemove', onMouseMove);
|
if (this.rafId) {
|
||||||
document.removeEventListener('mouseup', onMouseUp);
|
cancelAnimationFrame(this.rafId);
|
||||||
|
this.rafId = null;
|
||||||
|
}
|
||||||
|
document.removeEventListener('mousemove', onMouseMove, { capture: true });
|
||||||
|
document.removeEventListener('mouseup', onMouseUp, { capture: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
handle.addEventListener('mousedown', onMouseDown);
|
handle.addEventListener('mousedown', onMouseDown);
|
||||||
@@ -333,6 +399,13 @@ export class BimDialog implements IBimComponent {
|
|||||||
*/
|
*/
|
||||||
public close() {
|
public close() {
|
||||||
if (this._isDestroyed) return;
|
if (this._isDestroyed) return;
|
||||||
|
|
||||||
|
// 清理可能存在的动画帧,防止报错
|
||||||
|
if (this.rafId) {
|
||||||
|
cancelAnimationFrame(this.rafId);
|
||||||
|
this.rafId = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.unsubscribeTheme) {
|
if (this.unsubscribeTheme) {
|
||||||
this.unsubscribeTheme();
|
this.unsubscribeTheme();
|
||||||
this.unsubscribeTheme = null;
|
this.unsubscribeTheme = null;
|
||||||
@@ -354,4 +427,4 @@ export class BimDialog implements IBimComponent {
|
|||||||
public destroy() {
|
public destroy() {
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
36
src/index.ts
36
src/index.ts
@@ -8,4 +8,38 @@ export { Toolbar } from './components/button-group/toolbar';
|
|||||||
export type { OptButton, ButtonGroup, ButtonGroupOptions, ClickPayload } from './components/button-group/index.type';
|
export type { OptButton, ButtonGroup, ButtonGroupOptions, ClickPayload } from './components/button-group/index.type';
|
||||||
|
|
||||||
// 导出主引擎类
|
// 导出主引擎类
|
||||||
export { BimEngine };
|
export { BimEngine };
|
||||||
|
|
||||||
|
// 导出 3D 引擎相关类型
|
||||||
|
export type { EngineOptions, ModelLoadOptions } from './components/engine';
|
||||||
|
|
||||||
|
// 导出 createEngine 函数(从第三方 SDK 重新导出)
|
||||||
|
// 注意:createEngine 的实际实现来自 bim-engine-sdk.es.js
|
||||||
|
//
|
||||||
|
// 使用方式:
|
||||||
|
// 1. 直接从 SDK 文件导入(推荐,如 Vue 示例):
|
||||||
|
// import { createEngine } from '/engine/bim-engine-sdk.es.js';
|
||||||
|
//
|
||||||
|
// 2. 从主入口导入(如果构建配置支持):
|
||||||
|
// import { createEngine } from 'bim-engine-sdk';
|
||||||
|
//
|
||||||
|
// 示例:
|
||||||
|
// ```javascript
|
||||||
|
// const engine = createEngine({
|
||||||
|
// containerId: 'vue2-container',
|
||||||
|
// backgroundColor: 0x333333,
|
||||||
|
// version: 'v1',
|
||||||
|
// showStats: true,
|
||||||
|
// showViewCube: true
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// engine.loader.loadModel(url, {
|
||||||
|
// position: [10, -5, 0],
|
||||||
|
// rotation: [0, 0, 0],
|
||||||
|
// scale: [1, 1, 1]
|
||||||
|
// });
|
||||||
|
// ```
|
||||||
|
|
||||||
|
// 重新导出 createEngine(从 SDK 文件)
|
||||||
|
// 注意:如果直接导入失败,用户应该直接从 bim-engine-sdk.es.js 文件导入
|
||||||
|
export { createEngine } from './bim-engine-sdk.es.js';
|
||||||
@@ -3,25 +3,30 @@
|
|||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"allowJs": true,
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
/* Library specifics */
|
/* Library specifics */
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"emitDeclarationOnly": true
|
"emitDeclarationOnly": true
|
||||||
},
|
},
|
||||||
"include": ["src", "playground"]
|
"include": [
|
||||||
}
|
"src",
|
||||||
|
"playground"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -9,10 +9,21 @@ export default defineConfig(({ command }) => {
|
|||||||
// 移除 Vue 插件
|
// 移除 Vue 插件
|
||||||
dts({
|
dts({
|
||||||
include: ['src'],
|
include: ['src'],
|
||||||
rollupTypes: true
|
exclude: [
|
||||||
|
'src/**/*.es.js',
|
||||||
|
'src/bim-engine-sdk.es.js',
|
||||||
|
'**/*.es.js'
|
||||||
|
], // 排除第三方 SDK 文件,避免类型分析错误
|
||||||
|
rollupTypes: true,
|
||||||
|
logLevel: 'warn', // 只显示警告和错误
|
||||||
}),
|
}),
|
||||||
cssInjectedByJs()
|
cssInjectedByJs()
|
||||||
],
|
],
|
||||||
|
// 开发服务器配置
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
open: '/demo/index.html', // 自动打开 demo 页面
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
entry: resolve(__dirname, 'src/index.ts'),
|
entry: resolve(__dirname, 'src/index.ts'),
|
||||||
|
|||||||
Reference in New Issue
Block a user