修复
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
yinsx
2026-01-05 16:09:36 +08:00
parent ebbd21916e
commit b9cbb58a9d
14 changed files with 460 additions and 321 deletions

63
src/event/bridge.ts Normal file
View File

@ -0,0 +1,63 @@
import { emit, on, once, off, Emitter } from './bus';
import {
ModelClickPayload,
ModelLoadedPayload,
ModelLoadErrorPayload,
ModelLoadProgressPayload,
SceneReadyPayload
} from './types';
/**
* Centralized event helpers to avoid spreading raw event strings.
*/
export class EventBridge {
// Emits
static modelLoadProgress(payload: ModelLoadProgressPayload): Emitter {
return emit("model:load:progress", payload);
}
static modelLoadError(payload: ModelLoadErrorPayload): Emitter {
return emit("model:load:error", payload);
}
static modelLoaded(payload: ModelLoadedPayload): Emitter {
return emit("model:loaded", payload);
}
static modelClick(payload: ModelClickPayload): Emitter {
return emit("model:click", payload);
}
static sceneReady(payload: SceneReadyPayload): Emitter {
return emit("scene:ready", payload);
}
// Listeners
static onModelLoadProgress(callback: (payload: ModelLoadProgressPayload) => void, context?: unknown): Emitter {
return on("model:load:progress", callback, context);
}
static onModelLoadError(callback: (payload: ModelLoadErrorPayload) => void, context?: unknown): Emitter {
return on("model:load:error", callback, context);
}
static onModelLoaded(callback: (payload: ModelLoadedPayload) => void, context?: unknown): Emitter {
return on("model:loaded", callback, context);
}
static onModelClick(callback: (payload: ModelClickPayload) => void, context?: unknown): Emitter {
return on("model:click", callback, context);
}
static onSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
return on("scene:ready", callback, context);
}
static onceSceneReady(callback: (payload: SceneReadyPayload) => void, context?: unknown): Emitter {
return once("scene:ready", callback, context);
}
static off(eventName?: string, callback?: (...args: unknown[]) => void): Emitter {
return off(eventName, callback);
}
}

82
src/event/bus.ts Normal file
View File

@ -0,0 +1,82 @@
type Listener = {
callback: (...args: unknown[]) => void;
context?: unknown;
};
export class Emitter {
private _events: Record<string, Listener[]> = {};
on(name: string, callback: (...args: unknown[]) => void, context?: unknown): this {
if (!this._events[name]) {
this._events[name] = [];
}
this._events[name].push({ callback, context });
return this;
}
once(name: string, callback: (...args: unknown[]) => void, context?: unknown): this {
const onceWrapper = (...args: unknown[]) => {
this.off(name, onceWrapper);
callback.apply(context, args);
};
return this.on(name, onceWrapper, context);
}
off(name?: string, callback?: (...args: unknown[]) => void): this {
if (!name) {
this._events = {};
return this;
}
if (!this._events[name]) return this;
if (!callback) {
delete this._events[name];
return this;
}
this._events[name] = this._events[name].filter(
listener => listener.callback !== callback
);
return this;
}
removeAllListeners(): this {
this._events = {};
return this;
}
emit(name: string, ...args: unknown[]): this {
if (!this._events[name]) return this;
this._events[name].forEach(listener => {
listener.callback.apply(listener.context, args);
});
return this;
}
listenerCount(name: string): number {
return this._events[name]?.length ?? 0;
}
}
export class EventBus extends Emitter { }
export const eventBus = new EventBus();
export const on = (eventName: string, callback: (...args: unknown[]) => void, context?: unknown): Emitter => {
return eventBus.on(eventName, callback, context);
};
export const off = (eventName?: string, callback?: (...args: unknown[]) => void): Emitter => {
return eventBus.off(eventName, callback);
};
export const once = (eventName: string, callback: (...args: unknown[]) => void, context?: unknown): Emitter => {
return eventBus.once(eventName, callback, context);
};
export const emit = (eventName: string, ...args: unknown[]): Emitter => {
return eventBus.emit(eventName, ...args);
};
export const removeAllListeners = (eventName?: string): Emitter => {
if (eventName) return eventBus.off(eventName);
return eventBus.removeAllListeners();
};

37
src/event/types.ts Normal file
View File

@ -0,0 +1,37 @@
import { Scene } from '@babylonjs/core/scene';
export type ModelLoadProgressDetail = {
url?: string;
lengthComputable?: boolean;
loadedBytes?: number;
totalBytes?: number;
};
export type ModelLoadProgressPayload = {
loaded: number;
total: number;
url?: string;
urls?: string[];
success?: boolean;
progress?: number;
percentage?: number;
detail?: ModelLoadProgressDetail;
};
export type ModelLoadErrorPayload = {
url: string;
error?: unknown;
};
export type ModelLoadedPayload = {
urls: string[];
};
export type ModelClickPayload = {
meshName?: string;
};
export type SceneReadyPayload = {
scene: Scene | null;
};