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

@ridp/threejs

v1.5.2

Published

基于 Three.js 的 3D 可视化工具库,提供场景管理、模型加载、性能优化等开箱即用的功能。

Readme

@ridp/threejs

基于 Three.js 的 3D 可视化工具库,提供场景管理、模型加载、性能优化等开箱即用的功能。

📢 重要提示:

  • ⚠️ useThreeJs() Hook 已弃用,请使用 ThreeIns 类 (功能更强大,不限框架)
  • ⚠️ frameArea() 方法已弃用,请使用 setView() 方法 (支持多视角切换)

快速迁移:

// ❌ 旧用法 (已弃用)
const { scene, camera } = useThreeJs('#container', opts);
threeIns.frameArea(model, 1.0);

// ✅ 新用法 (推荐)
const threeIns = new ThreeIns('#container', opts);
threeIns.setView(model, ViewType.ISO, { scale: 1.0 });

特性

  • 🚀 开箱即用: 零配置快速搭建 Three.js 场景
  • 📦 智能缓存: 基于 IndexedDB 的模型缓存机制,大幅提升二次加载速度
  • 🎯 视角控制: 内置多种视角(俯视、侧视、等轴测)和平滑过渡动画
  • 🔧 性能优化: 模型优化、批量加载、渐进式渲染等性能优化工具
  • 🎨 辅助工具: 包围盒检测、射线拾取、碰撞检测等实用工具
  • 💡 TypeScript 支持: 完整的类型定义和 JSDoc 文档
  • 🌐 框架无关: 可在任何 JavaScript/TypeScript 项目中使用(Vue/React/Angular/原生JS)

安装

依赖要求

本库依赖以下 peer dependencies:

{
  "peerDependencies": {
    "dexie": ">=4.0.0 <5.0.0",
    "vue": ">=3.0.0 <4.0.0",
    "three": ">=0.178.0 <1.0.0"
  }
}

NPM 安装

npm install @ridp/threejs dexie@^4 vue@^3 three@^0.178

Yarn 安装

yarn add @ridp/threejs dexie@^4 vue@^3 three@^0.178

PNPM 安装

pnpm add @ridp/threejs dexie@^4 vue@^3 three@^0.178

vite.config.js 配置

因为涉及 worker,需要添加以下配置项:

import { defineConfig } from 'vite';

export default defineConfig(() => {
  return {
    // plugins,
    // build: { ... }
    optimizeDeps: {
      // ...
      exclude: ['@ridp/threejs']
    },
  }
})

快速开始

基础场景初始化

import { ThreeIns } from '@ridp/threejs';

// 创建 3D 场景实例
const threeJsIns = new ThreeIns('#container', {
  css3d: true,           // 启用 CSS3D 渲染器
  renderType: 'loop',     // 渲染循环类型
  initListener: true,     // 初始化事件监听
  initialFov: 75,         // 初始 FOV
  control: {              // OrbitControls 配置
    init: true,
    options: {
      maxDistance: Infinity,
      minDistance: 0,
      enablePan: true,
      enableDamping: true,
      dampingFactor: 0.25,
    },
  },
});

// 访问核心对象
console.log(threeJsIns.scene);    // THREE.Scene
console.log(threeJsIns.camera);   // THREE.PerspectiveCamera
console.log(threeJsIns.renderer); // THREE.WebGLRenderer
console.log(threeJsIns.control);  // THREE.OrbitControls

加载 GLTF 模型

import { useGLTFLoader, initEnvImage } from '@ridp/threejs';

const { asyncFetch, logCacheReport } = useGLTFLoader();

// 加载模型(带缓存)
async function loadModel() {
  const modelUrl = '/models/car.glb';
  const version = '1.0.0';

  const [err, model] = await asyncFetch(modelUrl, version);

  if (model) {
    threeJsIns.scene.add(model);
    threeJsIns.setView(model, ViewType.ISO, {
      scale: 1.0,
      showBox: true,
      animate: true,
      duration: 1000
    });
  }

  // 查看缓存性能报告
  logCacheReport();
}

// 加载环境贴图
await initEnvImage(threeJsIns.scene, '/hdr/studio.exr');
loadModel();

核心 API

ThreeIns 类

主要的 3D 场景管理类。

构造函数

new ThreeIns(selector: string, options: ThreeInsOptions)

参数:

  • selector: DOM 元素选择器(如 #canvas-container)
  • options: 配置对象
    • css3d: 是否启用 CSS3D 渲染器(默认: false)
    • renderType: 渲染循环类型,'loop''ondemand'(默认: 'loop')
    • initListener: 是否自动监听窗口大小变化(默认: true)
    • initialFov: 初始垂直 FOV(默认: 50)
    • control: OrbitControls 配置
      • init: 是否初始化控制器(默认: true)
      • options: OrbitControls 配置对象

实例属性

  • scene: THREE.Scene - 场景对象
  • camera: THREE.PerspectiveCamera - 相机对象
  • renderer: THREE.WebGLRenderer - WebGL 渲染器
  • css3DRenderer: THREE.CSS3DRenderer - CSS3D 渲染器(如果启用)
  • control: THREE.OrbitControls - 轨道控制器
  • domElement: HTMLElement - 容器 DOM 元素

主要方法

setView()

设置模型视角。

threeJsIns.setView(
  model: Object3D,
  viewType: ViewType,
  options?: {
    scale?: number,        // 缩放比例,1=占满画布,0.5=50%,2=0%(默认: 1)
    showBox?: boolean,     // 是否显示包围盒(默认: false)
    animate?: boolean,     // 是否启用动画(默认: false)
    duration?: number,     // 动画时长(毫秒,默认: 1000)
    offset?: Vector3       // 中心点偏移
  }
): void

视角类型 (ViewType):

  • ViewType.TOP - 俯视(从上往下)
  • ViewType.RIGHT - 右视图(从右往左)
  • ViewType.LEFT - 左侧视(从左往右)
  • ViewType.ISO - 等轴测视角(对角线上方俯视)

示例:

// 设置等轴测视角,模型占满画布
threeJsIns.setView(model, ViewType.ISO, {
  scale: 1.0,
  showBox: true,
  animate: true,
  duration: 800
});

// 设置俯视,模型缩小到 50%
threeJsIns.setView(model, ViewType.TOP, {
  scale: 0.5,
  animate: true
});
frameArea()

⚠️ 已弃用 - 此方法仅为兼容旧版本保留,请使用 setView() 方法代替。

自动调整相机位置使模型完整显示在视口中(默认使用等轴测视角)。

threeJsIns.frameArea(
  model: Object3D,
  scale: number = 1,
  options?: {
    showBox?: boolean,    // 是否显示包围盒
    offset?: Vector3      // 中心点偏移
  }
): void

参数说明:

  • scale: 显示比例
    • 1.0 - 模型占满画布
    • 0.5 - 模型占画布 50%
    • 2.0 - 模型放大到 200%

示例:

// ❌ 旧用法(已弃用)
threeJsIns.frameArea(model, 1.0);

// ✅ 推荐用法 - 使用 setView
threeJsIns.setView(model, ViewType.ISO, { scale: 1.0 });

// ❌ 旧用法(已弃用)
threeJsIns.frameArea(model, 0.5, { showBox: true });

// ✅ 推荐用法 - 使用 setView
threeJsIns.setView(model, ViewType.ISO, {
  scale: 0.5,
  showBox: true,
  animate: true
});

迁移指南: | 旧用法 (frameArea) | 新用法 (setView) | | ----------------------- | ---------------------------------------------- | | frameArea(model, 1.0) | setView(model, ViewType.ISO, { scale: 1.0 }) | | frameArea(model, 0.5) | setView(model, ViewType.ISO, { scale: 0.5 }) | | frameArea(model, 2.0) | setView(model, ViewType.ISO, { scale: 2.0 }) |

updateCameraFOV()

根据画布宽高比动态调整相机 FOV。

threeJsIns.updateCameraFOV(): void

当窗口大小变化时自动调用,确保模型在不同宽高比下正确显示。

addAnimate()

添加自定义动画函数到渲染循环。

threeJsIns.addAnimate(fn: Function): void

示例:

let time = 0;
threeJsIns.addAnimate(() => {
  time += 0.01;
  model.rotation.y = time;
});
dispose()

销毁场景,释放所有资源。

threeJsIns.dispose(): void

清理内容:

  • 停止渲染循环
  • 移除事件监听器
  • 释放几何体、材质、纹理
  • 清理 Stats 性能监控
  • 销毁 OrbitControls

Hooks

useThreeJs()

⚠️ 已弃用 - 此 Hook 仅为兼容旧版本保留,请使用 ThreeIns 类 代替。

Vue 3 组合式 API 风格的 Three.js 场景初始化 Hook。

为什么要弃用?

  • ❌ 功能被 ThreeIns 类完全覆盖
  • ❌ 缺少视角切换功能 (setView)
  • ❌ 性能优化不如 ThreeIns 类
  • ❌ 仅限 Vue 项目使用
  • ❌ 资源清理不够完善

推荐迁移到 ThreeIns 类:

// ❌ 旧用法 (useThreeJs Hook - 已弃用)
import { useThreeJs } from '@ridp/threejs';
const { scene, camera, renderer, control } = useThreeJs('#container', { ... });

// ✅ 推荐用法 (ThreeIns 类)
import { ThreeIns, ViewType } from '@ridp/threejs';
const threeJsIns = new ThreeIns('#container', {
  css3d: true,
  renderType: 'loop',
  initListener: true,
  initialFov: 75,
  control: { init: true, options: { ... } }
});

// 访问核心对象
console.log(threeJsIns.scene);    // THREE.Scene
console.log(threeJsIns.camera);   // THREE.PerspectiveCamera
console.log(threeJsIns.renderer); // THREE.WebGLRenderer
console.log(threeJsIns.control);  // THREE.OrbitControls

// 使用强大的 setView 方法
threeJsIns.setView(model, ViewType.ISO, { scale: 1.0, animate: true });

迁移对照表:

| 功能 | useThreeJs (已弃用) | ThreeIns 类 (推荐) | | ----------- | ------------------------------------ | -------------------------------- | | 初始化场景 | useThreeJs('#el', opts) | new ThreeIns('#el', opts) | | 访问 scene | const { scene } = useThreeJs(...) | threeJsIns.scene | | 访问 camera | const { camera } = useThreeJs(...) | threeJsIns.camera | | 添加动画 | addAnimate(fn) | threeJsIns.addAnimate(fn) | | 视角切换 | ❌ 不支持 | ✅ setView(model, ViewType.ISO) | | 资源清理 | dispose() | ✅ threeJsIns.dispose() | | 适用范围 | 仅 Vue 3 | 所有 JS/TS 项目 |


useGLTFLoader()

GLTF 模型加载器,支持 IndexedDB 缓存、内存缓存、重试机制和调试模式。

const loader = useGLTFLoader({
  debug: false  // 是否开启调试日志(默认: false)
});

const {
  // 核心加载方法
  load,              // 基础加载方法
  asyncLoad,         // Promise 异步加载
  asyncFetch,        // 带缓存和重试的异步加载(推荐)
  asyncCacheLoad,    // [已弃用] 旧版缓存加载

  // 批量加载
  loadBatch,         // 批量加载模型
  loadBatchSequential, // 顺序批量加载

  // IndexedDB 缓存管理
  clearCache,        // 清空 IndexedDB 缓存
  invalidateCache,   // 使缓存失效
  getCacheStats,     // 获取缓存统计
  logCacheReport,    // 输出缓存性能报告
  getCache,          // 获取 IDBCache 实例
  resetCacheStats,   // 重置缓存统计

  // 内存缓存管理 (v1.4.2+)
  clearMemoryCache,      // 清空内存缓存
  getMemoryCacheInfo,    // 获取内存缓存统计
  deleteMemoryCache,     // 删除指定模型的内存缓存

  // 性能监控
  cacheMonitor       // 缓存监控器实例
} = loader;

新增功能 (v1.4.2):

  1. 内存缓存机制: 自动缓存已解析的 3D 模型对象,避免重复解析
  2. 调试模式控制: 通过初始化参数统一控制所有日志输出

初始化配置

// 生产环境 - 默认静默
const loader = useGLTFLoader();

// 开发环境 - 开启调试日志
const loader = useGLTFLoader({
  debug: true
});

// 所有操作都会输出详细日志
const model = await loader.asyncFetch('/models/car.glb', '1.0.0');
// 输出: [ asyncFetch ] ====> 缓存未命中
//       [ fetchArrayBuffer ] 加载模型耗时: 150ms
//       [ 解析模型耗时 ]: 80ms
//       [ 内存缓存 ] 存储模型 /models/car.glb (version: 1.0.0)

常用方法:

asyncFetch()

推荐的模型加载方法,集成 IndexedDB 缓存、内存缓存、重试机制、性能监控和模型优化。

const model = await asyncFetch(
  url: string,        // 模型 URL
  version?: string,   // 模型版本(用于缓存失效)
  onProgress?: (percent: number) => void,  // 进度回调
  options?: {
    // 重试配置
    maxRetries?: number,      // 最大重试次数(默认: 3)

    // 模型优化选项
    optimizeMaterials?: boolean,  // 是否自动优化材质,合并相同材质(默认: false)
    simplifyGeometry?: boolean,   // 是否简化模型几何体(默认: false)
    simplifyRatio?: number,       // 简化比例 0-1, 0.5=保留50%面(默认: 0.5)
    simplifyOptions?: {
      minFaceCount?: number,      // 最小面数阈值,低于此值不简化(默认: 100)
      preserveUVs?: boolean      // 是否保留UV坐标(默认: true)
    },

    // 缓存控制
    useMemoryCache?: boolean  // 是否使用内存缓存(默认: true, v1.4.2+)
  }
): Promise<THREE.Object3D | null>

缓存层级 (v1.4.2+):

  1. 内存缓存 (最快) - 已解析的 3D 对象,仅需克隆(~2ms)
  2. IndexedDB 缓存 (快) - ArrayBuffer 数据,需要解析(~200ms)
  3. 网络加载 (慢) - 完整下载 + 解析(~2s)

示例:

// 基础用法
const model = await asyncFetch('/models/car.glb', '1.0.0');
if (model) {
  scene.add(model);
}

// 带进度回调
const model = await asyncFetch('/models/car.glb', '1.0.0', (percent) => {
  console.log(`加载进度: ${percent}%`);
});

// 启用材质优化
const model = await asyncFetch('/models/car.glb', '1.0.0', null, {
  optimizeMaterials: true  // 自动合并相同材质
});

// 启用几何体简化(适合大型模型)
const model = await asyncFetch('/models/large-scene.glb', '1.0.0', null, {
  simplifyGeometry: true,      // 启用简化
  simplifyRatio: 0.5,          // 保留 50% 的面
  simplifyOptions: {
    minFaceCount: 500,         // 面数少于 500 的网格不简化
    preserveUVs: true          // 保留 UV 坐标
  }
});

// 禁用内存缓存
const model = await asyncFetch('/models/car.glb', '1.0.0', null, {
  useMemoryCache: false  // 跳过内存缓存
});

// 综合优化
const model = await asyncFetch('/models/complex.glb', '1.0.0', (p) => {
  console.log(`加载: ${p.toFixed(1)}%`);
}, {
  maxRetries: 5,             // 增加重试次数
  optimizeMaterials: true,   // 优化材质
  simplifyGeometry: true,    // 简化几何体
  simplifyRatio: 0.7,        // 保留 70% 的面
  useMemoryCache: true       // 使用内存缓存(默认)
});

性能对比:

// 首次加载
await asyncFetch('/models/car.glb', '1.0.0');
// 耗时: ~2s (网络 + 解析 + 优化)

// 二次加载 (内存缓存命中)
await asyncFetch('/models/car.glb', '1.0.0');
// 耗时: ~2ms (仅克隆对象)

// 二次加载 (仅 IndexedDB 命中,内存缓存被清空)
loader.clearMemoryCache();
await asyncFetch('/models/car.glb', '1.0.0');
// 耗时: ~200ms (从 IndexedDB 读取 + 解析)
logCacheReport()

输出 IndexedDB 缓存性能报告到控制台。

logCacheReport(): void

输出示例:

💾 [ 缓存性能报告 ] ====
  - 总加载次数: 5
  - 缓存命中次数: 3
  - 缓存未命中次数: 2
  - 缓存命中率: 60.00%
  - 总耗时: 2.5s
  - 平均耗时: 500ms
  - 缓存节省时间: ~1.5s
内存缓存管理 API (v1.4.2+)

getMemoryCacheInfo()

获取内存缓存统计信息。

const info = loader.getMemoryCacheInfo();

// 返回值:
{
  totalPaths: 2,      // 缓存的不同模型路径数量
  totalModels: 3,     // 缓存的总模型数量
  details: [
    {
      path: '/models/car.glb',
      versions: ['1.0.0', '1.1.0'],
      count: 2
    },
    {
      path: '/models/truck.glb',
      versions: ['1.0.0'],
      count: 1
    }
  ]
}

clearMemoryCache()

清空所有内存缓存,释放已缓存的 3D 模型对象内存。

const count = loader.clearMemoryCache();
console.log(`释放了 ${count} 个模型`);

// 使用场景: 内存不足时主动释放
if (info.totalModels > 100) {
  loader.clearMemoryCache();
}

deleteMemoryCache(path, version?)

删除指定模型的内存缓存。

// 删除特定版本
loader.deleteMemoryCache('/models/car.glb', '1.0.0');

// 删除所有版本
loader.deleteMemoryCache('/models/car.glb');

使用示例:

import { useGLTFLoader } from '@ridp/threejs/hooks';

const loader = useGLTFLoader({ debug: true });

// 首次加载 - 从网络加载并缓存
const model1 = await loader.asyncFetch('/models/car.glb', '1.0.0');
// [ asyncFetch ] ====> 缓存未命中
// [ 内存缓存 ] 存储模型 /models/car.glb (version: 1.0.0)

// 二次加载 - 从内存缓存获取(极快)
const model2 = await loader.asyncFetch('/models/car.glb', '1.0.0');
// [ 内存缓存命中 ] /models/car.glb (version: 1.0.0)
// [ 内存缓存克隆耗时 ]: 2ms

// 查看内存缓存信息
const info = loader.getMemoryCacheInfo();
console.log(`缓存了 ${info.totalModels} 个模型`);

// 清空内存缓存
loader.clearMemoryCache();

// 再次加载 - 从 IndexedDB 缓存获取
const model3 = await loader.asyncFetch('/models/car.glb', '1.0.0');
// [ asyncFetch ] ====> IndexedDB 缓存命中

useRaycaster()

射线拾取工具,用于鼠标交互和物体选择。

const { getIntersects, getPointer } = useRaycaster('app-id');

// 获取鼠标拾取的对象
const { intersects, pointer, x, y } = getIntersects(
  event,          // 鼠标事件
  renderer.domElement,  // 渲染器 DOM
  camera,         // 相机
  objects         // 可拾取对象数组
);

// intersects[0].object - 拾取到的 3D 对象
// pointer - 标准化设备坐标(-1 到 1)
// x, y - 鼠标在容器中的相对坐标

示例:

const canvas = document.getElementById('canvas');
canvas.addEventListener('click', (event) => {
  const { intersects } = getIntersects(event, canvas, camera, scene.children);

  if (intersects.length > 0) {
    const clickedObject = intersects[0].object;
    console.log('点击了:', clickedObject.name);

    // 高亮显示
    clickedObject.material.color.set(0xff0000);
  }
});

useObb()

有向包围盒(OBB)碰撞检测工具。

import { useObb, obbObjects, intersectColor } from '@ridp/threejs';

const {
  resetObbs,              // 重置所有 OBB
  initObb,                // 初始化模型的 OBB
  initSingleObbItem,      // 初始化单个对象的 OBB
  addObbFromArray,        // 批量添加 OBB
  getObbObjectByParentUid, // 根据 parentUid 获取 OBB 对象
  checkObbIntersection     // 检测碰撞
} = useObb();

使用示例:

// 1. 标记需要碰撞检测的模型
model.userData.needCheck = true;

// 2. 初始化 OBB
initObb('model-uid', model);

// 3. 碰撞检测
const hasCollision = checkObbIntersection(otherObject, obbObjects);

// 4. 高亮碰撞对象
if (hasCollision) {
  object.material.color.set(intersectColor);
}

useBatchGLTFLoader()

批量模型加载器,支持并发控制和进度追踪。

const { loadBatch, loadBatchSequential } = useBatchGLTFLoader();

// 并发加载(推荐)
const results = await loadBatch([
  { url: '/models/part1.glb', version: '1.0.0' },
  { url: '/models/part2.glb', version: '1.0.0' },
  { url: '/models/part3.glb', version: '1.0.0' }
], {
  concurrency: 3,        // 最大并发数
  onProgress: (progress) => {
    console.log(`进度: ${progress.percent.toFixed(2)}%`);
  }
});

// 顺序加载
const results = await loadBatchSequential([
  { url: '/models/part1.glb', version: '1.0.0' },
  { url: '/models/part2.glb', version: '1.0.0' }
]);

工具函数

场景辅助工具

import {
  createCameraHelper,    // 创建相机辅助器
  createGridHelper,      // 创建网格辅助器
  createBox3Helper,      // 创建包围盒辅助器
  createOrbitControl,    // 创建轨道控制器
  createMapControls,     // 创建地图控制器
  createRaycaster,       // 创建射线投射器
  createAxesHelper,      // 创建坐标轴辅助器
  createArrowHelper,     // 创建箭头辅助器
  createStats            // 创建性能监控器
} from '@ridp/threejs';

// 示例
const gridHelper = createGridHelper(100, 10, 0x888888, 0xcccccc);
scene.add(gridHelper);

const stats = createStats();
document.body.appendChild(stats.dom);

功能说明:

  • createCameraHelper(camera) - 创建相机辅助线,显示相机视锥体
  • createGridHelper(size, divisions, color1, color2) - 创建网格辅助线
    • size: 网格大小,默认 150
    • divisions: 分段数
    • color1/color2: 中心线和其他线的颜色
  • createBox3Helper(model) - 创建包围盒辅助线
  • createOrbitControl(camera, dom) - 创建轨道控制器
  • createMapControls(camera, dom) - 创建地图控制器
  • createRaycaster() - 创建射线投射器,用于鼠标拾取
  • createAxesHelper(size) - 创建坐标轴辅助线,默认大小 10
  • createArrowHelper(dir, origin, length, color) - 创建箭头辅助线
  • createStats() - 创建性能监控器(基于 stats.js)

initEnvImage()

初始化场景环境贴图。

import { initEnvImage } from '@ridp/threejs';

// EXR 格式
await initEnvImage(scene, '/hdr/studio.exr');

// HDR 格式
await initEnvImage(scene, '/hdr/outdoor.hdr');

// PNG/JPG 格式(作为 CubeTexture)
await initEnvImage(scene, '/textures/env.jpg');

getCommonParent()

查找多个网格对象的公共父节点。

import { getCommonParent } from '@ridp/threejs';

// 查找多个选中对象的公共父节点
const meshes = [mesh1, mesh2, mesh3];
const commonParent = getCommonParent(meshes, scene);

console.log('公共父节点:', commonParent);

功能说明:

  • 遍历每个网格的所有父节点,统计出现频率最高的父节点
  • 返回的公共父节点可作为分组、操作的目标对象
  • 常用于批量操作、场景组织等场景

使用场景:

  • 批量选择对象时查找共同的容器
  • 场景层级分析
  • 分组操作前的父节点确定

modelOptimizer

模型优化工具,用于减少多边形数量和优化材质。

import { modelOptimizer } from '@ridp/threejs';

// 获取模型统计信息
const stats = modelOptimizer.getModelStats(model);
console.log('网格数量:', stats.meshCount);
console.log('三角形数:', stats.triangleCount);
console.log('顶点数:', stats.vertexCount);
console.log('材质数:', stats.materialCount);

// 简化模型几何体
const optimized = modelOptimizer.simplifyModel(model, {
  ratio: 0.5,        // 保留 50% 的面
  mergeMaterials: true,  // 合并相同材质
  removeUnused: true     // 移除未使用的顶点
});

// 优化材质
modelOptimizer.optimizeMaterials(model);

disposeThreeObject()

深度释放 3D 对象及其子对象的资源。

import { disposeThreeObject } from '@ridp/threejs';

// 释放模型资源
disposeThreeObject(model);

// 释放整个场景
disposeThreeObject(scene);

清理内容:

  • 几何体 (geometry.dispose())
  • 材质 (material.dispose())
  • 材质中的所有纹理 (texture.dispose())
  • 子对象递归清理

注意: 此函数会递归遍历对象的所有子对象,确保所有资源都被正确释放,避免内存泄漏。

sceneRebuilder

渐进式场景重建工具,用于大型模型的分批渲染。

import { ProgressiveSceneBuilder } from '@ridp/threejs';

const builder = new ProgressiveSceneBuilder(scene, camera, {
  batchSize: 1000,        // 每批添加的网格数
  delay: 16,              // 批次间延迟(毫秒)
  onProgress: (progress) => {
    console.log(`构建进度: ${progress.percent}%`);
  }
});

await builder.build(model);

模型序列化工具

用于 Three.js 对象的序列化和反序列化,支持将 3D 对象转换为可存储的数据格式。

import {
  object3DToData,
  object3DToDataSync,
  dataToObject3D,
  dataToObject3DSync
} from '@ridp/threejs';

// 异步序列化(推荐,避免阻塞主线程)
const data = await object3DToData(model, 50);

// 同步序列化
const data = object3DToDataSync(model);

// 异步反序列化(推荐,避免阻塞主线程)
const restoredModel = await dataToObject3D(data, 50);

// 同步反序列化
const restoredModel = dataToObject3DSync(data);

功能说明:

  • object3DToData(object, chunkSize) - 异步序列化对象,使用分块处理避免阻塞
    • chunkSize: 每帧处理的对象数量,默认 50
  • object3DToDataSync(object) - 同步序列化对象
  • dataToObject3D(data, chunkSize) - 异步反序列化数据为 3D 对象
  • dataToObject3DSync(data) - 同步反序列化数据为 3D 对象

序列化内容:

  • 对象类型、名称、位置、旋转、缩放
  • 几何体数据(顶点、索引、UV 等)
  • 材质数据(颜色、纹理、属性等)
  • userData 自定义数据
  • 子对象层级关系

使用场景:

  • 场景状态保存和恢复
  • 模型数据的本地存储
  • 跨页面/跨会话的模型传输

对象查询工具

基于 userData 的对象查找工具。

import { getObjectByUserData, getRootObj } from '@ridp/threejs';

// 向下遍历查找首个匹配 userData 的对象
const target = getObjectByUserData(mesh, 'type', 'wheel');

// 向上查找根节点(根据 userData 判定)
const root = getRootObj(mesh, 'isRoot', true);

功能说明:

  • getObjectByUserData(obj, key, value) - 从指定对象开始向下遍历,查找首个 userData[key]=value 的对象(包括自身)
  • getRootObj(obj, rootKey, value) - 从指定对象开始向上查找父级,直到找到 userData[rootKey]=value 的根对象

CSS3D 辅助工具

import { createInfoPlane, createTagPlane } from '@ridp/threejs';

// 从 DOM ID 创建 CSS3D 信息面板
const infoPlane = createInfoPlane('my-info-panel', [0.3, 0.3, 0.3]);
infoPlane.position.set(0, 10, 0);
scene.add(infoPlane);

// 创建 CSS3D 标签面板
const tagPlane = createTagPlane('设备名称', 0.01);
tagPlane.position.set(5, 5, 5);
scene.add(tagPlane);

功能说明:

  • createInfoPlane(id, scale) - 从页面中已存在的 DOM 元素创建 CSS3D 精灵
    • id: DOM 元素的 ID
    • scale: 缩放比例数组 [x, y, z],默认 [0.3, 0.3, 0.3]
    • ⚠️ 注意: HTML 元素不能设置为绝对定位
  • createTagPlane(text, scale) - 创建带文本的 CSS3D 标签
    • text: 标签文本内容
    • scale: 统一缩放比例(三个维度相同)

CacheMonitor

缓存性能监控工具。

import { cacheMonitor } from '@ridp/threejs';

// 获取缓存统计
const stats = cacheMonitor.getStats();
console.log('命中率:', stats.hitRate);
console.log('平均耗时:', stats.avgLoadTime);

// 监听缓存事件
cacheMonitor.on('hit', (key) => {
  console.log('缓存命中:', key);
});

cacheMonitor.on('miss', (key) => {
  console.log('缓存未命中:', key);
});

RetryHelper

重试机制工具,用于处理不稳定的网络请求。

import { RetryHelper, ModelLoadError } from '@ridp/threejs';

const retryHelper = new RetryHelper({
  maxRetries: 3,        // 最大重试次数
  baseDelay: 1000,      // 基础延迟(毫秒)
  maxDelay: 10000,      // 最大延迟(毫秒)
  backoffMultiplier: 2  // 退避乘数
});

try {
  const result = await retryHelper.execute(async () => {
    const response = await fetch('/models/car.glb');
    if (!response.ok) {
      throw new ModelLoadError('加载失败', response.status);
    }
    return response.json();
  });
} catch (error) {
  console.error('重试失败:', error);
}

PredictiveLoader

预测性加载工具,根据用户行为预加载可能需要的资源。

import { PredictiveLoader } from '@ridp/threejs';

const loader = new PredictiveLoader({
  maxPrefetch: 3,       // 最大预加载数
  priority: 'recent'    // 优先级策略
});

// 预加载模型
loader.prefetch('/models/next-part.glb', '1.0.0');

// 根据用户行为预测
loader.predict(userBehavior, availableModels);

实例类

IDBCache

IndexedDB 缓存管理类。

import { IDBCache } from '@ridp/threejs';

const cache = new IDBCache('my-cache-db', 1);

// 保存模型到缓存
await cache.saveModel('model-key', modelData, {
  version: '1.0.0',
  timestamp: Date.now()
});

// 从缓存加载模型
const data = await cache.loadCachedModel('model-key', '1.0.0');

// 清除特定模型缓存
await cache.deleteModel('model-key');

// 清空所有缓存
await cache.clear();

// 获取缓存统计
const stats = await cache.getStats();

完整示例

Vue 3 组件示例

<template>
  <div class="canvas-container" ref="containerRef"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ThreeIns, useGLTFLoader, initEnvImage, ViewType } from '@ridp/threejs';
import * as THREE from 'three';

const containerRef = ref(null);
let threeJsIns = null;
const { asyncFetch, logCacheReport } = useGLTFLoader();

onMounted(async () => {
  // 1. 初始化场景
  threeJsIns = new ThreeIns(containerRef.value, {
    css3d: false,
    renderType: 'loop',
    initListener: true,
    initialFov: 75,
    control: {
      init: true,
      options: {
        maxDistance: Infinity,
        minDistance: 0,
        enablePan: true,
        enableDamping: true,
        dampingFactor: 0.25,
      },
    },
  });

  // 2. 加载环境贴图
  await initEnvImage(threeJsIns.scene, '/hdr/studio.exr');

  // 3. 加载模型
  const [err, model] = await asyncFetch('/models/car.glb', '1.0.0', {
    onProgress: (progress) => {
      const percent = (progress.loaded / progress.total * 100).toFixed(2);
      console.log(`加载进度: ${percent}%`);
    }
  });

  if (model) {
    threeJsIns.scene.add(model);

    // 4. 设置视角
    threeJsIns.setView(model, ViewType.ISO, {
      scale: 1.0,
      showBox: false,
      animate: true,
      duration: 1000
    });
  }

  // 5. 查看缓存性能
  logCacheReport();
});

onUnmounted(() => {
  // 清理资源
  if (threeJsIns) {
    threeJsIns.dispose();
  }
});
</script>

<style scoped>
.canvas-container {
  width: 100%;
  height: 100vh;
}
</style>

多模型加载示例

import { ThreeIns, useBatchGLTFLoader, ViewType } from '@ridp/threejs';

const threeJsIns = new ThreeIns('#container', { /* ... */ });
const { loadBatch } = useBatchGLTFLoader();

// 批量加载多个模型
const models = [
  { url: '/models/floor.glb', version: '1.0.0' },
  { url: '/models/wall.glb', version: '1.0.0' },
  { url: '/models/roof.glb', version: '1.0.0' },
  { url: '/models/furniture.glb', version: '1.0.0' }
];

const results = await loadBatch(models, {
  concurrency: 2,
  onProgress: (progress) => {
    console.log(`加载进度: ${progress.percent}%`);
    console.log(`已完成: ${progress.loaded}/${progress.total}`);
  }
});

// 添加所有模型到场景
results.forEach(result => {
  if (result.model) {
    threeJsIns.scene.add(result.model);
  }
});

// 自动适配视角
const group = new THREE.Group();
results.forEach(r => r.model && group.add(r.model));
threeJsIns.setView(group, ViewType.TOP, { scale: 0.8 });

交互式场景示例

import { ThreeIns, useRaycaster, useObb } from '@ridp/threejs';

const threeJsIns = new ThreeIns('#container', { /* ... */ });
const { getIntersects } = useRaycaster('app');
const { initObb, checkObbIntersection } = useObb();

// 加载模型后初始化碰撞检测
model.userData.needCheck = true;
initObb('model-1', model);

// 点击事件
threeJsIns.renderer.domElement.addEventListener('click', (event) => {
  const { intersects } = getIntersects(
    event,
    threeJsIns.renderer.domElement,
    threeJsIns.camera,
    [model]
  );

  if (intersects.length > 0) {
    const object = intersects[0].object;
    console.log('点击了:', object.name);

    // 检测碰撞
    const hasCollision = checkObbIntersection(object, obbObjects);
    if (hasCollision) {
      object.material.color.set(0xff0000);
    }
  }
});

性能优化建议

1. 使用模型缓存和自动优化

对于重复访问的模型,启用 IndexedDB 缓存可以显著提升加载速度。同时可以启用材质优化和几何体简化来提升渲染性能:

// 首次加载: ~2s (含优化)
await asyncFetch('/models/large-scene.glb', '1.0.0', {
  optimizeMaterials: true,   // 合并相同材质,减少 draw calls
  simplifyGeometry: true,    // 简化几何体,减少三角形数
  simplifyRatio: 0.5         // 保留 50% 的面
});

// 二次加载: ~200ms (从缓存读取,仍需优化)
await asyncFetch('/models/large-scene.glb', '1.0.0', {
  optimizeMaterials: true,
  simplifyGeometry: true,
  simplifyRatio: 0.5
});

优化选项说明:

  • optimizeMaterials (材质优化)

    • 自动合并相同属性的材质
    • 减少材质切换次数
    • 降低 GPU draw calls
    • 适用场景: 模型中有很多相似材质的对象
  • simplifyGeometry (几何体简化)

    • 减少模型的三角形数量
    • 显著提升渲染性能
    • 适合大型场景或低性能设备
    • 可配置简化比例和最小面数阈值
  • simplifyRatio (简化比例)

    • 0.8 - 保留 80% 的面(轻度简化)
    • 0.5 - 保留 50% 的面(中度简化,推荐)
    • 0.3 - 保留 30% 的面(重度简化,适合远景模型)

性能对比:

未优化模型: 50万三角形, 120个材质
优化后:    25万三角形 (50%减少), 45个材质 (62%减少)
FPS提升:   30 FPS -> 55 FPS (83%提升)

2. 批量加载优化

使用批量加载时,合理控制并发数:

// 推荐: 根据设备性能动态调整
const concurrency = navigator.hardwareConcurrency || 4;
await loadBatch(models, { concurrency });

3. 手动模型优化

如果需要在加载后手动优化模型:

import { modelOptimizer } from '@ridp/threejs';

// 材质优化
modelOptimizer.optimizeMaterials(model);

// 几何体简化
modelOptimizer.simplifyModel(model, 0.5, {
  minFaceCount: 500,
  preserveUVs: true
});

// 查看优化效果
const stats = modelOptimizer.getModelStats(model);
console.log('优化后统计:', stats);

4. 渐进式渲染

对于超大场景,使用渐进式渲染避免长时间卡顿:

import { ProgressiveSceneBuilder } from '@ridp/threejs';

const builder = new ProgressiveSceneBuilder(scene, camera, {
  batchSize: 1000,
  delay: 16
});
await builder.build(model);

5. 合理使用阴影

阴影是性能杀手,仅在必要时启用:

renderer.shadowMap.enabled = true;  // 启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;  // 软阴影

// 仅重要对象投射阴影
importantObject.castShadow = true;
otherObjects.castShadow = false;

常见问题

Q: 如何处理模型加载失败?

A: 使用 asyncFetch 的重试机制:

const [err, model] = await asyncFetch(url, version, {
  retryCount: 5,
  timeout: 60000
});

if (err || !model) {
  console.error('模型加载失败:', err);
  // 显示错误提示或加载备用模型
}

Q: 如何清理模型缓存?

A: 使用 clearCache 方法:

const { clearCache } = useGLTFLoader();
await clearCache();  // 清空所有缓存

// 或使用 IDBCache 实例
import { IDBCache } from '@ridp/threejs';
const cache = new IDBCache();
await cache.clear();

Q: 模型显示太小或太大?

A: 使用 setViewscale 参数:

// 模型太小 - 放大
threeJsIns.setView(model, ViewType.ISO, { scale: 2.0 });

// 模型太大 - 缩小
threeJsIns.setView(model, ViewType.ISO, { scale: 0.5 });

注意: 旧方法 frameArea() 已弃用,请使用 setView() 代替。

Q: 如何监听窗口大小变化?

A: 如果 initListener: true,会自动监听。否则手动监听:

window.addEventListener('resize', () => {
  threeJsIns.updateCameraFOV();
  threeJsIns.camera.updateProjectionMatrix();
  threeJsIns.renderer.setSize(
    threeJsIns.domElement.clientWidth,
    threeJsIns.domElement.clientHeight
  );
});

Q: 如何导出场景截图?

A: 使用 renderer.domElement.toDataURL():

function captureScreenshot() {
  threeJsIns.render();  // 确保最新帧已渲染
  const dataURL = threeJsIns.renderer.domElement.toDataURL('image/png');
  const link = document.createElement('a');
  link.download = 'screenshot.png';
  link.href = dataURL;
  link.click();
}

更新日志

v1.4.2 (2026-01-07)

🎉 重大更新:

  • 新增内存缓存机制: 自动缓存已解析的 3D 模型对象,避免重复解析

    • 首次加载后,二次加载仅需 ~2ms(对象克隆)
    • 相比完整加载流程,速度提升可达 100倍以上
    • 新增 clearMemoryCache() - 清空内存缓存
    • 新增 getMemoryCacheInfo() - 获取内存缓存统计
    • 新增 deleteMemoryCache(path, version?) - 删除指定缓存
    • 支持 useMemoryCache 选项控制是否使用内存缓存
  • 新增调试模式控制:

    • useGLTFLoader 现在支持初始化配置 { debug: boolean }
    • 统一控制所有内部函数的日志输出
    • 生产环境默认静默,开发环境可开启详细日志
    • 错误日志始终输出,不受 debug 配置影响
  • 🐛 修复 Web Worker 在 npm 包中无法加载的问题:

    • 使用 ?worker 语法正确导入 Worker
    • 配置 base: './' 使用相对路径解析
    • Worker 文件正确打包到 dist/assets/ 目录
    • 修复了之前 404 错误和 MIME 类型错误的问题

API 变更:

// 旧 API (v1.4.1 及之前)
const loader = useGLTFLoader();
const model = await loader.asyncFetch(url, version, null, { debug: true });

// 新 API (v1.4.2+)
const loader = useGLTFLoader({ debug: true });
const model = await loader.asyncFetch(url, version);

// 新增内存缓存管理
const info = loader.getMemoryCacheInfo();
loader.clearMemoryCache();
loader.deleteMemoryCache(url, version);

性能提升:

加载场景 (v1.4.1):
- 首次: ~2s (网络 + 解析)
- 二次: ~200ms (IndexedDB 读取 + 解析)

加载场景 (v1.4.2+):
- 首次: ~2s (网络 + 解析)
- 二次: ~2ms (内存缓存克隆)
- 提升: 100倍

v1.4.1 (2025-XX-XX)

  • 🎯 优化 setView 方法的 scale 参数计算逻辑
  • 🔧 修复水平 FOV 超过 180° 导致的负数距离问题
  • ✨ 移除 FRONT/BACK/BOTTOM 视角,保留 TOP/RIGHT/LEFT/ISO
  • ⚡ 优化 OrbitControls 距离限制的自动处理
  • 🐛 修复相机 near/far 平面裁剪问题
  • ⚠️ 弃用: useThreeJs() Hook - 推荐使用 ThreeIns 类 (功能更完整,不限框架)
  • ⚠️ 弃用: frameArea() 方法 - 推荐使用 setView() 方法 (支持多视角)
  • 新增: asyncFetch 支持自动材质优化 (optimizeMaterials)
  • 新增: asyncFetch 支持几何体简化 (simplifyGeometry)
  • 新增: modelOptimizer.simplifyModel() 方法 - 整体模型简化
  • 📦 增强: 完整的模型优化工作流,支持加载时自动优化

v1.3.2 (2025-01-05)

  • ✨ 新增 Web Worker 支持,优化 GLTF 解析性能
  • 📦 增强模型缓存机制
  • 🎨 新增 modelOptimizer 工具
  • ⚡ 性能优化:减少重复计算

v1.3.0 (2024-12-XX)

  • ✨ 新增 useBatchGLTFLoader 批量加载 Hook
  • 🎯 新增 setView 方法,支持多视角切换
  • 📊 新增 CacheMonitor 缓存监控工具
  • 🔧 新增 RetryHelper 重试机制

完整更新日志请查看 CHANGELOG.md


许可证

MIT License


支持


相关资源