prophetk-3d-viewer
Version:
A Three.js r128 based GLB/GLTF model viewer for WeChat Mini Program and Web.
313 lines (291 loc) • 11.6 kB
TypeScript
export interface ModelConfig {
/** Parent model ID; TRS is relative to the parent. Missing parents defer display. */
parent?: string | null;
id?: string;
name?: string;
url: string;
position?: [number, number, number];
/** Euler XYZ degrees. */
rotation?: [number, number, number];
scale?: number | [number, number, number];
normalize?: boolean;
/** Opt in to selection and dragging on a horizontal plane; default false. */
isMovable?: boolean;
/** Hide this model and descendants when rendering, preserving interaction; default false. */
invisible?: boolean;
/** Non-empty event name enables clicks, independently of isMovable. */
clickEvent?: string;
}
export type ModelPosition = [number, number, number];
export interface ModelTransform {
/** Omitted for scene-root models. */
parent?: string;
id: string;
/** Current parent-local position, matching ModelConfig.position. */
position: ModelPosition;
/** Euler XYZ degrees, regardless of the object's internal Euler order. */
rotation: [number, number, number];
scale: [number, number, number];
}
export type ModelEventReason = 'pointer' | 'release' | 'multitouch' | 'touchcancel' |
'blur' | 'resize' | 'low-angle' | 'modelremoved' | 'scenechange' | 'selectioncleared' | 'dispose' | 'parentchange';
export type ModelEvent = {
type: 'click';
modelId: string;
event: string;
} | {
type: 'selectionchange';
modelId: string | null;
previousModelId: string | null;
reason: ModelEventReason;
} | {
type: 'dragstart' | 'drag' | 'dragend' | 'dragcancel' | 'dragblocked';
modelId: string;
/** Parent-local coordinates, matching ModelConfig.position. */
startPosition: ModelPosition;
position: ModelPosition;
reason?: ModelEventReason;
message?: string;
};
export interface ModelTouchEvent {
touches: ArrayLike<{ clientX: number; clientY: number; identifier?: number }>;
changedTouches?: ArrayLike<{ clientX: number; clientY: number; identifier?: number }>;
pointerType?: string;
}
export interface ModelWheelEvent {
/** Negative zooms in; positive zooms out. */
deltaY: number;
/** 0: pixels (default), 1: lines, 2: pages. */
deltaMode?: 0 | 1 | 2;
ctrlKey?: boolean;
}
export interface DecalOptions {
projectionMode?: 'cylindrical' | 'projector' | 'orthographic';
mode?: 'cylindrical' | 'projector' | 'orthographic';
angle?: number;
height?: number;
scaleX?: number;
scaleY?: number;
arcWidth?: number;
heightRange?: number;
opacity?: number;
position?: [number, number, number];
direction?: [number, number, number];
targetPoint?: [number, number, number];
rotation?: [number, number, number];
roll?: number;
rollDeg?: number;
up?: [number, number, number];
width?: number;
projectorPosition?: [number, number, number];
projectorDirection?: [number, number, number];
projectorTarget?: [number, number, number];
projectorRotation?: [number, number, number];
projectorRoll?: number;
projectorRollDeg?: number;
projectorUp?: [number, number, number];
projectorWidth?: number;
projectorHeight?: number;
projectorNormalThreshold?: number;
normalThreshold?: number;
}
export interface SceneProjectorDecalConfig extends Omit<DecalOptions, 'target'> {
target?: string;
url?: string;
debug?: boolean;
debugColor?: number | string;
debugLength?: number;
}
export interface SceneButtonUiConfig {
type: 'button';
id?: string;
/** Normalized screen-space position, origin at the canvas bottom-left corner. Usually 0..1. */
position?: [number, number];
/** Normalized button width and height relative to the canvas. Usually 0..1. */
scale?: [number, number];
text?: string;
img?: string;
alpha?: number;
event?: string;
backgroundColor?: string;
borderColor?: string;
color?: string;
fontSize?: number;
}
export interface SceneSliderUiConfig {
type: 'slider' | 'slide';
id?: string;
/** Normalized screen-space position, origin at the canvas bottom-left corner. Usually 0..1. */
position?: [number, number];
/** Normalized slider width and height relative to the canvas. Usually 0..1. */
scale?: [number, number];
text?: string;
min?: number;
max?: number;
value?: number;
step?: number;
alpha?: number;
event?: string;
/** Built-in target such as 'decal.scaleX' or 'decal.scaleY'. */
target?: string;
trackColor?: string;
fillColor?: string;
thumbColor?: string;
thumbBorderColor?: string;
color?: string;
fontSize?: number;
}
export interface SceneLabelUiConfig {
type: 'label';
/** Unique id used by setUiLabelText(id, text). */
id: string;
/** Normalized screen-space position, origin at the canvas bottom-left corner. Usually 0..1. */
position?: [number, number];
/** Normalized label width and height relative to the canvas. Usually 0..1. */
scale?: [number, number];
text?: string;
alpha?: number;
backgroundColor?: string;
borderColor?: string;
borderWidth?: number;
radius?: number;
padding?: number;
color?: string;
fontSize?: number;
align?: 'left' | 'center' | 'right';
}
export type SceneUiConfig = SceneButtonUiConfig | SceneSliderUiConfig | SceneLabelUiConfig;
export type CameraControlMode = 'orbit' | 'pan';
export interface SceneCameraConfig {
/** Single-finger / left-mouse drag on empty space. Defaults to orbit. */
controlMode?: CameraControlMode;
position?: [number, number, number];
lookAt?: [number, number, number];
fov?: number;
near?: number;
far?: number;
/** Spherical angles in radians; radius in world units. */
orbit?: { theta?: number; phi?: number; radius?: number };
[key: string]: unknown;
}
export interface SceneEnvironmentConfig {
url?: string;
showSkybox?: boolean;
/** Show the horizontal reference grid while dragging. Defaults to true. */
showDragGrid?: boolean;
/** Show the selected model's bounds. Defaults to true. */
showBounds?: boolean;
[key: string]: unknown;
}
export interface SceneConfig {
renderer?: Record<string, unknown>;
environment?: SceneEnvironmentConfig;
camera?: SceneCameraConfig;
lights?: Array<Record<string, unknown>>;
models?: ModelConfig[];
ui?: SceneUiConfig[];
decal?: DecalOptions & {
target?: string;
url?: string;
decalAngleDeg?: number;
decalHeight?: number;
};
decal_projector?: SceneProjectorDecalConfig;
decalProjector?: SceneProjectorDecalConfig;
}
export interface PlatformAdapter {
getWindowInfo(): { pixelRatio?: number; windowWidth?: number; windowHeight?: number };
getPixelRatio(): number;
getContext(canvas: unknown, type: string): unknown;
requestJson(url: string): Promise<unknown>;
fetchFile(url: string, options?: Record<string, unknown>): Promise<string>;
fetchArrayBuffer(url: string, options?: Record<string, unknown>): Promise<ArrayBuffer>;
readFileBase64?(path: string): Promise<string>;
createImage(canvas?: unknown): unknown;
createCanvas?(width: number, height: number): unknown;
requestAnimationFrame(canvas: unknown, callback: FrameRequestCallback): number;
cancelAnimationFrame(canvas: unknown, id: number): void;
}
export class ModelRenderer {
constructor(canvas: unknown, options?: {
platform?: PlatformAdapter;
bgColor?: string;
width?: number;
height?: number;
renderMode?: 'on-demand' | 'continuous';
onUiEvent?: (detail: { type: string; event: string; text?: string; x?: number; y?: number }) => void;
onModelEvent?: (detail: ModelEvent) => void;
});
setUiEventHandler(handler?: (detail: { type: string; event: string; text?: string; x?: number; y?: number }) => void): void;
setUiLabelText(id: string, text: string): boolean;
resize(width: number, height: number, rect?: { left?: number; top?: number }): void;
loadModel(url: string | ModelConfig, options?: Partial<ModelConfig>): Promise<unknown>;
switchModel(url: string | ModelConfig, options?: Partial<ModelConfig>): Promise<unknown>;
loadScene(config?: SceneConfig | string): Promise<void>;
clearModel(target: string | { id?: string; url?: string }): number;
clearAllModels(): number;
/** Preserve local TRS; null detaches to the scene root. */
setParent(modelId: string, parentId: string | null): void;
SetParent(modelId: string, parentId: string | null): void;
getSelectedModelId(): string | null;
/** JSON-encoded ModelTransform[] for all loaded models, including waiting children; '[]' when empty. */
getModelTransforms(): string;
clearSelection(): void;
getCameraControlMode(): CameraControlMode;
setCameraControlMode(mode: CameraControlMode): void;
onTouchStart(event: ModelTouchEvent): void;
onTouchMove(event: ModelTouchEvent): void;
onTouchEnd(event: ModelTouchEvent): void;
onTouchCancel(event?: Partial<ModelTouchEvent>, reason?: ModelEventReason): void;
/** Returns whether the camera distance changed; both camera modes support wheel zoom. */
onWheel(event: ModelWheelEvent): boolean;
applyDecal(url: string, options?: DecalOptions): Promise<void>;
updateDecal(options: DecalOptions): void;
updateDecal(angle: number, height: number, scaleX?: number, scaleY?: number): void;
updateProjectorDecal(options?: DecalOptions): void;
removeDecal(): void;
dispose(): void;
}
export class WechatPlatformAdapter implements PlatformAdapter {
constructor(options?: { wx?: unknown; canvas?: unknown });
}
export class WebPlatformAdapter implements PlatformAdapter {
constructor(options?: { window?: Window; document?: Document });
}
export class WebModelViewer {
constructor(options: {
container: string | HTMLElement;
canvas?: HTMLCanvasElement;
bgColor?: string;
width?: number;
height?: number;
renderMode?: 'on-demand' | 'continuous';
onUiEvent?: (detail: { type: string; event: string; text?: string; x?: number; y?: number }) => void;
onModelEvent?: (detail: ModelEvent) => void;
});
loadModel(url: string | ModelConfig, options?: Partial<ModelConfig>): Promise<unknown>;
switchModel(url: string | ModelConfig, options?: Partial<ModelConfig>): Promise<unknown>;
loadScene(config?: SceneConfig | string): Promise<void>;
clearModel(target: string | { id?: string; url?: string }): number;
clearAllModels(): number;
/** Preserve local TRS; null detaches to the scene root. */
setParent(modelId: string, parentId: string | null): void;
SetParent(modelId: string, parentId: string | null): void;
getSelectedModelId(): string | null;
/** JSON-encoded ModelTransform[] for all loaded models, including waiting children; '[]' when empty. */
getModelTransforms(): string;
clearSelection(): void;
getCameraControlMode(): CameraControlMode;
setCameraControlMode(mode: CameraControlMode): void;
applyDecal(url: string, options?: DecalOptions): Promise<void>;
updateDecal(options: DecalOptions): void;
updateDecal(angle: number, height: number, scaleX?: number, scaleY?: number): void;
updateProjectorDecal(options?: DecalOptions): void;
removeDecal(): void;
setUiLabelText(id: string, text: string): boolean;
resize(): void;
dispose(): void;
}
export function createWebModelViewer(options: ConstructorParameters<typeof WebModelViewer>[0]): WebModelViewer;
export const THREE: unknown;
export const DEFAULT_SCENE_CONFIG: SceneConfig;