npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

threejs-viewer

v0.1.2

Published

一个模块化的 Three.js 3D 模型查看器 React 组件库。支持 GLTF/GLB 模型加载、轨道控制、相机脚本动画、相机路径设计器等功能。

Readme

Three.js Viewer

一个模块化的 Three.js 3D 模型查看器 React 组件库。支持 GLTF/GLB 模型加载、轨道控制、相机脚本动画、相机路径设计器等功能。

特性

  • 🎨 React 组件 - 易用的 <ThreeViewer /><ModelViewer /> 组件,覆盖底层控制和开箱即用场景
  • 相机脚本控制器 - <CameraScriptController /> 组件,支持 Shot 动画和 View Preset 应用
  • �📦 GLTF/GLB 支持 - 加载 GLTF 和 GLB 格式的 3D 模型,含动画播放
  • 🎮 轨道控制 - 旋转、缩放和平移模型
  • 🎬 相机路径动画 - 支持多段相机路径动画,可配置插值和缓动
  • ✏️ 相机路径设计器 - 可视化设计相机运动路径
  • 网格辅助 - 可配置的 XY/XZ/YZ 平面网格和坐标轴
  • �🔌 插件架构 - 可扩展的设计,支持自定义功能
  • 📱 响应式 - 自动处理容器尺寸变化
  • 🧹 内存安全 - 正确清理 Three.js 资源
  • 🔗 实例访问 API - 通过 Hook 或 Ref 访问底层 Three.js 实例

安装

npm install threejs-viewer three react react-dom

快速开始

基础用法

import { ThreeViewer } from 'threejs-viewer';

function App() {
  return (
    <ThreeViewer
      modelUrl="https://example.com/model.glb"
      style={{ width: '100%', height: '500px' }}
      onLoad={(result) => console.log('模型加载完成:', result)}
      onError={(error) => console.error('错误:', error)}
    />
  );
}

推荐用法:ModelViewer

ModelViewer 是面向业务接入的高层组件,直接接收 model + cameraScript 两组参数,内部会自动处理本地文件解析、模型加载和相机脚本接入。

import { ModelViewer } from 'threejs-viewer';

function App() {
  return (
    <ModelViewer
      model={{ type: 'url', url: 'https://example.com/model.glb' }}
      cameraScript={{ mode: 'none' }}
      grid={{ visible: true, size: 20, divisions: 20, plane: 'XZ', showAxes: true }}
      style={{ width: '100%', height: '500px' }}
    />
  );
}

使用 CameraScriptController(高封装方式)

CameraScriptController 是一个无渲染组件,将相机 Shot 动画相机视角预设的完整逻辑封装在内部。只需传入 JSON 配置即可驱动相机行为,无需手动管理插件或订阅事件——适合需要快速集成相机控制的场景。

import { useRef, useState } from 'react';
import {
  ThreeViewer,
  CameraScriptController,
  type CameraScriptMode,
  type ThreeViewerHandle,
  type ModelLoadResult,
} from 'threejs-viewer';

function App() {
  const viewerRef = useRef<ThreeViewerHandle>(null);
  const [modelCenter, setModelCenter] = useState<THREE.Vector3 | undefined>();

  const handleLoad = (result: ModelLoadResult) => {
    setModelCenter(result.center);
  };

  // Shot JSON: 定义相机路径动画的关键帧
  const cameraShotJson = JSON.stringify({
    version: 1,
    defaults: { interpolation: 'catmullRom', easing: { type: 'easeInOut' } },
    keyframes: [
      { position: [5, 3, 5], target: [0, 0, 0], duration: 2 },
      { position: [-5, 3, 5], target: [0, 0, 0], duration: 2 },
    ],
  });

  // Preset JSON: 定义固定视角
  const cameraViewPresetJson = JSON.stringify({
    version: 1,
    position: { r: 2.5, theta: 45, phi: 45, mode: 'spherical' },
    target: { x: 0, y: 0, z: 0, relativeTo: 'world' },
  });

  return (
    <>
      <ThreeViewer
        ref={viewerRef}
        modelUrl="/model.glb"
        grid={{ visible: true, size: 20, divisions: 20, plane: 'XZ', showAxes: true }}
        onLoad={handleLoad}
        style={{ width: '100%', height: '500px' }}
      />
      <CameraScriptController
        viewerRef={viewerRef}
        mode="shot"                       // 'shot' | 'preset' | 'none'
        cameraShotJson={cameraShotJson}    // Shot 模式配置
        cameraViewPresetJson={cameraViewPresetJson}  // Preset 模式配置
        loop={true}
        autoPlay={true}
        modelCenter={modelCenter}
        onError={(error) => console.error(error)}
      />
    </>
  );
}

ModelViewer 属性

| 属性 | 类型 | 描述 | |------|------|------| | model | ModelViewerModel \| null | 模型来源,支持 URL、本地单文件和本地文件夹 | | cameraScript | ModelViewerCameraScript | 相机脚本,支持 noneshotpreset | | grid | GridConfig | 网格和坐标轴辅助线配置 | | pivotPoint | { x, y, z } | 自定义旋转中心点 | | zoomLimits | { min?, max? } | 相机缩放距离限制 | | backgroundColor | number \| string | 场景背景色 | | className | string | 容器的 CSS 类名 | | style | CSSProperties | 容器的内联样式 | | onLoad | (result: ModelLoadResult) => void | 模型加载成功时的回调 | | onLoadingChange | (loading: boolean) => void | 模型源准备或加载状态变化时的回调 | | onError | (error: Error, context: { stage }) => void | 错误回调,stagemodel-sourcemodel-loadcamera-script | | ref | Ref<ThreeViewerHandle> | 可选,转发到底层 ThreeViewer 的命令式句柄 |

ThreeViewer 属性

| 属性 | 类型 | 描述 | |------|------|------| | modelUrl | string | 要加载的 GLTF/GLB 模型 URL | | pivotPoint | { x, y, z } | 自定义旋转中心点 | | zoomLimits | { min?, max? } | 相机缩放距离限制 | | grid | GridConfig | 网格和坐标轴辅助线配置 | | backgroundColor | number \| string | 场景背景色(十六进制数值或 CSS 颜色字符串) | | className | string | 容器的 CSS 类名 | | style | CSSProperties | 容器的内联样式 | | onLoad | (result: ModelLoadResult) => void | 模型加载成功时的回调 | | onError | (error: Error) => void | 发生错误时的回调 | | onLoadingChange | (isLoading: boolean) => void | 加载状态变化时的回调 | | onViewerReady | (viewerCore: ViewerCore) => void | ViewerCore 初始化完成后的回调 | | children | ReactNode | 子组件,可使用 useThreeInstance Hook | | ref | Ref<ThreeViewerHandle> | 用于命令式访问 Three.js 实例 |

GridConfig

interface GridConfig {
  visible?: boolean;    // 是否显示网格
  size?: number;        // 网格大小
  divisions?: number;   // 网格分割数
  plane?: 'XY' | 'XZ' | 'YZ';  // 网格所在平面
  showAxes?: boolean;   // 是否显示坐标轴(RGB = XYZ)
}

CameraScriptController 属性

CameraScriptController 是一个无渲染的 React 组件,用于声明式地驱动相机行为。它内部自动完成插件注册、JSON 解析和状态管理。

| 属性 | 类型 | 默认值 | 描述 | |------|------|--------|------| | viewerRef | RefObject<ThreeViewerHandle> | 必填 | ThreeViewer 的 ref 引用 | | mode | 'shot' \| 'preset' \| 'none' | 'none' | 相机脚本模式 | | cameraShotJson | string | - | Shot 模式:相机路径动画的 JSON 配置 | | cameraShot | CameraShot | - | Shot 模式:相机路径动画的对象配置(与 JSON 二选一) | | loop | boolean | - | Shot 模式:是否循环播放 | | autoPlay | boolean | true | Shot 模式:是否自动播放 | | cameraViewPresetJson | string | - | Preset 模式:视角预设的 JSON 配置 | | cameraViewPreset | CameraViewPreset | - | Preset 模式:视角预设的对象配置(与 JSON 二选一) | | applyViewWhen | 'immediate' \| 'afterModelLoaded' | 'afterModelLoaded' | Preset 模式:何时应用视角预设 | | modelCenter | THREE.Vector3 | - | 模型中心点(用于相对定位) | | modelRadius | number | - | 模型包围球半径(用于距离计算) | | onError | (error: Error) => void | - | 错误回调 |

控制方式

  • 旋转: 左键拖拽
  • 缩放: 滚轮或双指捏合
  • 平移: 右键拖拽

高级用法

自定义旋转中心

<ThreeViewer
  modelUrl="/model.glb"
  pivotPoint={{ x: 0, y: 1, z: 0 }}
/>

自定义缩放限制

<ThreeViewer
  modelUrl="/model.glb"
  zoomLimits={{ min: 1, max: 50 }}
/>

网格和坐标轴

<ThreeViewer
  modelUrl="/model.glb"
  grid={{
    visible: true,
    size: 20,
    divisions: 20,
    plane: 'XZ',
    showAxes: true,
  }}
/>

自定义背景色

<ThreeViewer
  modelUrl="/model.glb"
  backgroundColor={0x1a1a2e}
/>

加载状态

function App() {
  const [loading, setLoading] = useState(false);

  return (
    <>
      {loading && <div>加载中...</div>}
      <ThreeViewer
        modelUrl="/model.glb"
        onLoadingChange={setLoading}
      />
    </>
  );
}

实例访问 API

提供两种方式访问底层 Three.js 实例:Hook API 和 Ref API。

useThreeInstance Hook

ThreeViewer 子组件中使用,获取响应式的 Three.js 实例访问。

import { ThreeViewer, useThreeInstance } from 'threejs-viewer';

function SceneModifier() {
  const { scene, camera, isReady } = useThreeInstance();

  useEffect(() => {
    if (isReady && scene) {
      const light = new THREE.PointLight(0xffffff, 1);
      light.position.set(5, 5, 5);
      scene.add(light);

      return () => {
        scene.remove(light);
        light.dispose();
      };
    }
  }, [scene, isReady]);

  return null;
}

function App() {
  return (
    <ThreeViewer modelUrl="/model.glb">
      <SceneModifier />
    </ThreeViewer>
  );
}

Ref API

使用 ref 进行命令式访问,不触发重渲染。

import { useRef } from 'react';
import { ThreeViewer, ThreeViewerHandle } from 'threejs-viewer';

function App() {
  const viewerRef = useRef<ThreeViewerHandle>(null);

  const handleScreenshot = () => {
    if (viewerRef.current?.isReady()) {
      const { renderer } = viewerRef.current.getInstances();
      if (renderer) {
        const dataUrl = renderer.domElement.toDataURL('image/png');
        console.log('截图:', dataUrl);
      }
    }
  };

  const handleGetViewerCore = () => {
    const viewerCore = viewerRef.current?.getViewerCore();
    if (viewerCore) {
      console.log('插件系统:', viewerCore.plugins);
    }
  };

  return (
    <>
      <button onClick={handleScreenshot}>截图</button>
      <button onClick={handleGetViewerCore}>获取 ViewerCore</button>
      <ThreeViewer ref={viewerRef} modelUrl="/model.glb" />
    </>
  );
}

初始化回调(插件注册推荐)

在 ViewerCore 初始化完成、内置插件注册完成、render loop 启动前触发,可用于可靠地注册自定义插件:

import { ThreeViewer, CameraMovementPlugin } from 'threejs-viewer';

function App() {
  return (
    <ThreeViewer
      modelUrl="/model.glb"
      onViewerReady={(viewerCore) => {
        if (!viewerCore.plugins.has('CameraMovementPlugin')) {
          viewerCore.plugins.register(new CameraMovementPlugin());
        }
      }}
    />
  );
}

ThreeInstanceContextValue

Hook 返回的上下文值类型:

interface ThreeInstanceContextValue {
  scene: THREE.Scene | null;              // Three.js 场景
  camera: THREE.PerspectiveCamera | null;  // 透视相机
  renderer: THREE.WebGLRenderer | null;    // WebGL 渲染器
  container: HTMLElement | null;           // 容器 DOM 元素
  isReady: boolean;                        // 是否已初始化
  isDisposed: boolean;                     // 是否已销毁
}

ThreeViewerHandle

Ref 暴露的方法:

interface ThreeViewerHandle {
  getInstances(): ThreeInstanceContextValue;  // 获取所有实例
  getViewerCore(): ViewerCore | null;         // 获取 ViewerCore
  isReady(): boolean;                         // 检查是否就绪
  isDisposed(): boolean;                      // 检查是否已销毁
}

使用核心类(高级)

如需更多控制,可以直接使用核心类:

import { ViewerCore, ModelLoaderPlugin, OrbitControlsPlugin } from 'threejs-viewer';

const viewer = new ViewerCore();
viewer.initialize({ container: document.getElementById('viewer')! });

const modelLoader = new ModelLoaderPlugin();
const orbitControls = new OrbitControlsPlugin();

viewer.plugins.register(modelLoader);
viewer.plugins.register(orbitControls);

viewer.start();

// 加载模型
const result = await modelLoader.load('/model.glb');
orbitControls.setTarget(result.center);

// 完成后清理
viewer.dispose();

插件系统

库提供以下内置插件,均可独立注册和使用:

| 插件 | 描述 | |------|------| | ModelLoaderPlugin | GLTF/GLB 模型加载与管理 | | ModelAnimationPlugin | 模型骨骼动画播放控制 | | OrbitControlsPlugin | 相机轨道控制(旋转、缩放、平移) | | GridHelperPlugin | 网格辅助线和坐标轴显示 | | CameraMovementPlugin | 键盘驱动的自由相机移动(FPS 风格) | | CameraPathAnimationPlugin | 多段相机路径动画(支持插值、缓动配置) | | CameraPathDesignerPlugin | 可视化相机路径设计器(编辑模式、实时预览) |

自定义插件示例

import { type Plugin, type PluginContext } from 'threejs-viewer';

class MyCustomPlugin implements Plugin {
  name = 'MyCustomPlugin';

  initialize(context: PluginContext): void {
    // 可访问 scene, camera, renderer
    console.log('插件已初始化', context.scene);
  }

  dispose(): void {
    // 清理资源
  }
}

相机工具函数

CameraShotIO

相机 Shot 的序列化/反序列化工具:

import { parseCameraShot, toCameraPathAnimationConfig } from 'threejs-viewer';

// 解析 JSON 字符串为 CameraShot 对象
const shot = parseCameraShot(jsonString);

// 转换为 CameraPathAnimationPlugin 配置
const config = toCameraPathAnimationConfig(shot, { loop: true });

CameraViewPreset

相机视角预设的导入/导出/应用:

import {
  parseCameraViewPreset,
  exportCameraViewPreset,
  applyCameraViewPreset,
} from 'threejs-viewer';

// 从当前相机导出视角预设
const preset = exportCameraViewPreset(
  { camera, orbitControls },
  { modelRadius, radiusMode: 'absolute', targetMode: 'world' }
);

// 解析 JSON 字符串为 CameraViewPreset 对象
const parsed = parseCameraViewPreset(jsonString);

// 将预设应用到相机
applyCameraViewPreset(
  { camera, orbitControls },
  parsed,
  { modelCenter, modelRadius }
);

开发

安装依赖

npm install

运行演示

npm run dev:demo

在 http://localhost:3000 打开演示应用。

演示包含两个页面(通过 URL hash 切换):

  • Demo1(默认,#/)— 完整功能展示,包含所有控制面板(模型加载、旋转中心、缩放限制、网格控制、模型动画、相机动画/路径设计器、视角预设弹窗等)
  • Demo2#/demo2)— 高层 ModelViewer 示例页,展示如何用 model + cameraScript 组合 URL、本地文件、本地文件夹和相机脚本

运行测试

npm run test:run

构建库

npm run build:lib

输出到 dist/ 目录:

  • threejs-viewer.mjs (ESM)
  • threejs-viewer.cjs (CommonJS)
  • 类型声明文件 (.d.ts)

项目结构

src/                              # 库源码
├── components/
│   ├── ThreeViewer.tsx            # 主 React 组件
│   └── CameraScriptController.tsx # 相机脚本控制器组件
├── context/
│   ├── ThreeInstanceContext.ts    # 实例访问 Context
│   └── ThreeInstanceProvider.tsx  # Context Provider
├── core/
│   ├── ViewerCore.ts             # 核心引擎
│   ├── SceneManager.ts           # 场景管理
│   ├── CameraManager.ts          # 相机管理
│   ├── RenderManager.ts          # 渲染器管理
│   └── PluginSystem.ts           # 插件系统
├── hooks/
│   └── useThreeInstance.ts       # 实例访问 Hook
├── plugins/
│   ├── ModelLoaderPlugin.ts      # GLTF 模型加载
│   ├── ModelAnimationPlugin.ts   # 模型动画播放
│   ├── OrbitControlsPlugin.ts    # 轨道控制
│   ├── GridHelperPlugin.ts       # 网格辅助线
│   ├── CameraMovementPlugin.ts   # 键盘自由相机移动
│   ├── CameraPathAnimationPlugin.ts  # 相机路径动画
│   └── CameraPathDesignerPlugin.ts   # 相机路径设计器
├── camera/
│   ├── CameraShotIO.ts           # Shot 序列化/反序列化
│   └── CameraViewPreset.ts      # 视角预设工具
├── types/
│   └── instance.ts               # 实例访问类型定义
├── utils/                        # 通用工具函数
└── index.ts                      # 库导出入口

demo/                             # 演示应用
├── main.tsx                      # 演示入口
├── App.refactored.tsx            # 主应用(含 Demo1 和路由)
├── pages/
│   └── Demo2.tsx                 # 高封装演示页面
├── components/
│   ├── DemoLayout.tsx            # 布局框架
│   ├── DemoHeader.tsx            # 顶部栏
│   ├── DemoSidebar.tsx           # 侧边栏容器
│   ├── DemoViewer.tsx            # Viewer 包装组件
│   ├── index.ts                  # 组件导出
│   ├── controls/                 # 控制面板组件
│   │   ├── ModelUrlControl.tsx
│   │   ├── PivotPointControl.tsx
│   │   ├── ZoomLimitsControl.tsx
│   │   ├── GridControl.tsx
│   │   ├── ModelAnimationControl.tsx
│   │   ├── CameraModeControl.tsx
│   │   ├── CameraMovementControl.tsx
│   │   ├── CameraAnimationControl.tsx
│   │   ├── CameraPathDesignerControl.tsx
│   │   ├── CameraViewPresetControl.tsx
│   │   ├── ControlSection.tsx
│   │   ├── StatusDisplay.tsx
│   │   └── ControlsInstructions.tsx
│   └── panels/                   # 面板组件
│       ├── CameraPathEditorPanel.tsx
│       ├── CameraPathEditorFloatingWindow.tsx
│       ├── CameraViewPresetModal.tsx
│       ├── TimelineEditor.tsx
│       ├── SegmentInspector.tsx
│       ├── TransportBar.tsx
│       ├── ShotJsonModal.tsx
│       └── Tabs.tsx
├── hooks/                        # 演示自定义 Hooks
│   ├── useModelLoader.ts
│   ├── useModelAnimation.ts
│   ├── usePivotControl.ts
│   ├── useZoomControl.ts
│   ├── useGridControl.ts
│   ├── useCameraMovement.ts
│   ├── useCameraAnimation.ts
│   ├── useCameraPathDesigner.ts
│   ├── useDockablePanelState.ts
│   └── index.ts
└── styles/
    └── theme.ts                  # 主题样式

API 参考

ThreeViewer 组件

用于显示 3D 模型的主 React 组件。详见属性表

CameraScriptController 组件

无渲染相机脚本控制器。详见属性表

ViewerCore

协调所有子系统的核心引擎。

interface ViewerCore {
  scene: SceneManager;
  camera: CameraManager;
  renderer: RenderManager;
  plugins: PluginSystem;
  
  initialize(options: ViewerCoreOptions): void;
  start(): void;
  stop(): void;
  dispose(): void;
  resize(width: number, height: number): void;
}

ModelLoaderPlugin

处理 GLTF/GLB 模型加载。

interface ModelLoaderPlugin {
  loadingState: LoadingState;
  
  load(url: string): Promise<ModelLoadResult>;
  cancel(): void;
  unload(): void;
  getCenter(): Vector3 | null;
  getBoundingBox(): Box3 | null;
}

OrbitControlsPlugin

提供相机轨道控制。

interface OrbitControlsPlugin {
  controls: OrbitControls;
  
  configure(config: OrbitControlsConfig): void;
  setTarget(target: Vector3): void;
  setZoomLimits(min: number, max: number): void;
  reset(): void;
}

CameraPathAnimationPlugin

多段相机路径动画控制。

interface CameraPathAnimationPlugin {
  configure(config: CameraPathAnimationConfig): void;
  play(): void;
  stop(): void;
  setOrbitControlsPlugin(plugin: IOrbitControlsPlugin): void;
}

useThreeInstance

获取 Three.js 实例的 React Hook。

function useThreeInstance(): ThreeInstanceContextValue;
// 必须在 ThreeViewer 子组件中使用,否则抛出错误

ThreeViewerHandle

通过 ref 暴露的命令式 API。

interface ThreeViewerHandle {
  getInstances(): ThreeInstanceContextValue;
  getViewerCore(): ViewerCore | null;
  isReady(): boolean;
  isDisposed(): boolean;
}

对等依赖

  • react >= 18.0.0
  • react-dom >= 18.0.0
  • three >= 0.150.0

许可证

MIT