添加测试信息

This commit is contained in:
yuding
2025-12-04 18:39:07 +08:00
parent c45cdc9f7d
commit 8a727c4485
15 changed files with 38203 additions and 441 deletions

View File

@@ -1,18 +1,15 @@
.bim-engine-wrapper {
position: relative;
/* 添加相对定位作为参照物 */
width: 100%;
height: 100%;
font-family: sans-serif;
color: #333;
padding: 20px;
background-color: #e16969;
border-radius: 8px;
border: 1px solid #e0e0e0;
color: #bf1d1d;
box-sizing: border-box;
/* 确保 padding 不会撑大容器 */
overflow: hidden;
/* 防止内容溢出 */
}
/* ... (中间代码不变) ... */
/* 操作按钮组容器 */

View File

@@ -2,11 +2,17 @@ import './bim-engine.css';
import { ToolbarManager } from './managers/toolbar-manager';
import { ButtonGroupManager } from './managers/button-group-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 { themeManager } from './services/theme';
import type { LocaleType } from './locales/types';
import type { ThemeType, ThemeConfig } from './themes/types';
export type { EngineOptions, ModelLoadOptions };
export class BimEngine {
private container: HTMLElement;
private wrapper: HTMLElement | null = null;
@@ -15,11 +21,18 @@ export class BimEngine {
public toolbar: ToolbarManager | null = null; // 底部专用
public buttonGroup: ButtonGroupManager | null = null; // 通用
public dialog: DialogManager | null = null;
public engine: EngineManager | null = null; // 3D 引擎管理器
public get localeManager() { return localeManager; }
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;
if (!el) throw new Error('Container not found');
this.container = el;
@@ -47,13 +60,14 @@ export class BimEngine {
this.wrapper.className = 'bim-engine-wrapper';
this.container.appendChild(this.wrapper);
// 初始化管理器
// 创建 3D 引擎管理器
this.engine = new EngineManager(this.wrapper);
// 初始化其他管理器
this.dialog = new DialogManager(this.wrapper);
this.toolbar = new ToolbarManager(this.wrapper);
this.buttonGroup = new ButtonGroupManager(this.wrapper);
// --- 创建左上角按钮组 (需求 1 & 2) ---
this.createTopLeftGroup();
// 初始主题
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({
position: 'top-left',
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();
// 调用 manager 的 initialize 方法初始化引擎
return this.engine.initialize(options);
}
private updateTheme(theme: ThemeConfig) {
if (this.wrapper) {
this.wrapper.style.backgroundColor = theme.background;
this.wrapper.style.color = theme.textPrimary;
@@ -106,6 +109,7 @@ export class BimEngine {
public destroy() {
this.toolbar?.destroy();
this.buttonGroup?.destroy();
this.engine?.destroy();
this.dialog = null;
this.container.innerHTML = '';
}

View File

@@ -33,6 +33,8 @@
background-color: var(--bim-btn-group-section-bg, rgba(17, 17, 17, 0.88));
border-radius: 6px;
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 {

View File

@@ -14,11 +14,12 @@
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
z-index: 1000;
z-index: 10001; /* 提高 z-index确保在第三方 SDK (10000) 之上 */
color: var(--bim-dialog-title-color);
overflow: hidden;
min-width: 200px;
min-height: 100px;
pointer-events: auto; /* 确保弹窗可以接收事件 */
}
.bim-dialog-header {

View File

@@ -20,6 +20,9 @@ export class BimDialog implements IBimComponent {
private unsubscribeTheme: (() => void) | null = null;
private unsubscribeLocale: (() => void) | null = null;
// 性能优化:用于存储 requestAnimationFrame 的 ID
private rafId: number | null = null;
/**
* 构造函数
* @param options 弹窗配置选项
@@ -116,7 +119,7 @@ export class BimDialog implements IBimComponent {
if (this.options.id) el.id = this.options.id;
// 应用颜色配置到 CSS 变量 (局部作用域)
// 应用颜色配置到 CSS 变量
const style = el.style;
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);
@@ -139,7 +142,10 @@ export class BimDialog implements IBimComponent {
const closeBtn = document.createElement('span');
closeBtn.className = 'bim-dialog-close';
closeBtn.innerHTML = '&times;';
closeBtn.onclick = () => this.close();
// 修复 TS 报错:去掉未使用的参数 e
closeBtn.onclick = () => {
this.close();
};
header.appendChild(title);
header.appendChild(closeBtn);
@@ -163,6 +169,26 @@ export class BimDialog implements IBimComponent {
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;
}
@@ -183,7 +209,6 @@ export class BimDialog implements IBimComponent {
*/
private initPosition() {
const pos = this.options.position;
const elRect = this.element.getBoundingClientRect();
// 计算相对父容器的定位
@@ -218,7 +243,6 @@ export class BimDialog implements IBimComponent {
}
}
// 简单的边界检查,防止初始位置溢出
left = Math.max(0, Math.min(left, pW - elW));
top = Math.max(0, Math.min(top, pH - elH));
@@ -227,53 +251,81 @@ export class BimDialog implements IBimComponent {
}
/**
* 初始化拖拽功能
* 初始化拖拽功能 (性能优化 + 解决粘手)
*/
private initDrag() {
let startX = 0;
let startY = 0;
let startLeft = 0;
let startTop = 0;
let containerW = 0;
let containerH = 0;
let elW = 0;
let elH = 0;
const onMouseDown = (e: MouseEvent) => {
e.preventDefault();
e.preventDefault(); // 阻止默认行为(如选中文本),非常重要,防止卡顿
e.stopPropagation(); // 阻止传递给 Three.js
startX = e.clientX;
startY = e.clientY;
startLeft = this.element.offsetLeft;
startTop = this.element.offsetTop;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
// 缓存尺寸,减少 reflow
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 dx = e.clientX - startX;
const dy = e.clientY - startY;
e.preventDefault();
e.stopPropagation();
let newLeft = startLeft + dx;
let newTop = startTop + dy;
// 节流优化:使用 requestAnimationFrame
if (this.rafId) return;
// 边界限制,防止拖出容器
const maxLeft = this.container.clientWidth - this.element.offsetWidth;
const maxTop = this.container.clientHeight - this.element.offsetHeight;
this.rafId = requestAnimationFrame(() => {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
newTop = Math.max(0, Math.min(newTop, maxTop));
let newLeft = startLeft + dx;
let newTop = startTop + dy;
this.element.style.left = `${newLeft}px`;
this.element.style.top = `${newTop}px`;
const maxLeft = containerW - elW;
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 = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
// 移除监听
document.removeEventListener('mousemove', onMouseMove, { capture: true });
document.removeEventListener('mouseup', onMouseUp, { capture: true });
};
this.header.addEventListener('mousedown', onMouseDown);
}
/**
* 初始化缩放功能
* 初始化缩放功能 (性能优化 + 解决粘手)
*/
private initResize() {
const handle = this.element.querySelector('.bim-dialog-resize-handle') as HTMLElement;
@@ -292,24 +344,38 @@ export class BimDialog implements IBimComponent {
startW = this.element.offsetWidth;
startH = this.element.offsetHeight;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
// 关键:使用 capture: true
document.addEventListener('mousemove', onMouseMove, { capture: true });
document.addEventListener('mouseup', onMouseUp, { capture: true });
};
const onMouseMove = (e: MouseEvent) => {
const dx = e.clientX - startX;
const dy = e.clientY - startY;
e.preventDefault();
e.stopPropagation();
const newW = Math.max(this.options.minWidth || 100, startW + dx);
const newH = Math.max(this.options.minHeight || 50, startH + dy);
if (this.rafId) return;
this.element.style.width = `${newW}px`;
this.element.style.height = `${newH}px`;
this.rafId = requestAnimationFrame(() => {
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 = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
document.removeEventListener('mousemove', onMouseMove, { capture: true });
document.removeEventListener('mouseup', onMouseUp, { capture: true });
};
handle.addEventListener('mousedown', onMouseDown);
@@ -333,6 +399,13 @@ export class BimDialog implements IBimComponent {
*/
public close() {
if (this._isDestroyed) return;
// 清理可能存在的动画帧,防止报错
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
if (this.unsubscribeTheme) {
this.unsubscribeTheme();
this.unsubscribeTheme = null;
@@ -354,4 +427,4 @@ export class BimDialog implements IBimComponent {
public destroy() {
this.close();
}
}
}

View File

@@ -8,4 +8,38 @@ export { Toolbar } from './components/button-group/toolbar';
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';