示例系统与浏览器
示例系统由 Three.js 的 400+ 交互式演示集合以及用于浏览它们的基于 Web 的浏览器界面组成。该系统包括标准化的示例编写模式、基于 JSON 的组织结构(files.json 和 tags.json),以及带缩略图预览的可搜索浏览器 UI。浏览器在 iframe 中加载示例,维护基于 URL 的导航,并与文档系统共享 UI 架构(6.3)。
有关验证这些示例的测试基础设施的信息,请参阅测试基础设施(6.4)。有关同样使用示例的可视化编辑器,请参阅可视化编辑器(6.1)。
文件结构与数据源
示例浏览器由一个静态 HTML 文件组成,该文件从 JSON 配置文件动态加载示例元数据。
files.json 结构
files.json 清单将示例组织成类别。每个类别是一个对象键,包含示例文件名数组(不带 .html 扩展名):
{
"webgl": [
"webgl_animation_keyframes",
"webgl_buffergeometry"
],
"webgpu": [
"webgpu_compute_particles"
],
"misc": [
"misc_controls_orbit"
]
}tags.json 结构
tags.json 文件将示例名称映射到搜索标签数组和元数据标志:
{
"misc_exporter_gcode": ["community"],
"webgl_clipping": ["solid"],
"webgl_interactive_cubes": ["raycast", "highlight"]
}"community" 标签标识社区贡献的示例,这些示例会获得特殊视觉处理。
UI 架构
示例浏览器使用带卡片式导航的面板-查看器布局,与文档系统的架构匹配。
面板组件
面板在左侧占据固定的 300px 宽度(移动端全宽)。它包含:
- 头部(examples/index.html15-23):标题和章节标签
- 输入包装器(examples/index.html29-32):带清除按钮的搜索字段
- 内容区域(examples/index.html34-36):示例卡片的可滚动列表
- 预览切换器(examples/index.html35):在卡片视图和精简视图间切换的图标
面板使用 CSS 类管理状态:
.open- 在移动端展开面板.searchFocused- 搜索激活时调整 UI.minimal- 隐藏截图预览,显示紧凑列表
查看器 iframe
iframe 显示选中的示例,并具有 WebXR 的全屏权限:
<iframe id="viewer" name="viewer" allow="fullscreen; xr-spatial-tracking"></iframe>位置计算考虑面板宽度:padding-left: var(--panel-width)(files/main.css451)。
卡片生成
每个示例由 createLink() 创建的卡片元素表示:
卡片模板包括:
- 截图图像:懒加载,400px 宽,16:9 宽高比
- 社区标签:如果示例的标签数组中有
"community"则显示 - 格式化标题:由
getName()生成,去除前缀并将下划线转换为斜杠
导航与路由
浏览器使用基于哈希的路由来维护指向特定示例的深度链接。
链接注册
在导航构建期间,每个示例在两个数据结构中注册:
- links 对象(examples/index.html57):将文件名映射到 DOM 元素以进行选择高亮
- validRedirects Map(examples/index.html58):将文件名映射到带
.html扩展名的完整路径以确保安全
安全模式防止不受信任的 URL 重定向:
if ( validRedirects.has( file ) === true ) {
selectFile( file );
viewer.src = validRedirects.get( file );
}点击处理
卡片点击被拦截以防止在修改的点击(Ctrl+Click、中键点击)时导航:
link.querySelector( 'a[target="viewer"]' ).addEventListener( 'click', function ( event ) {
if ( event.button !== 0 || event.ctrlKey || event.altKey || event.metaKey ) return;
selectFile( file );
} );查看源码按钮
浮动“查看源码”按钮(examples/index.html43)在选中示例时动态配置:
viewSrcButton.style.display = '';
viewSrcButton.href = 'https://github.com/mrdoob/three.js/blob/master/examples/' + selected + '.html';
viewSrcButton.title = 'View source code for ' + getName( selected ) + ' on GitHub';搜索与过滤系统
浏览器实现带多词支持和类别感知过滤的客户端搜索。
多词搜索模式
搜索使用前瞻模式匹配任意顺序的所有单词:
function escapeRegExp( string ) {
string = string.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
return '(?=.*' + string.split( ' ' ).join( ')(?=.*' ) + ')';
}示例:"webgl animation" 变为 (?=.*webgl)(?=.*animation),匹配包含这两个单词的任何示例。
标签集成
filterExample() 函数将文件名与标签数组合并以进行全面匹配:
function filterExample( file, exp, tags ) {
const link = links[ file ];
if ( file in tags ) file += ' ' + tags[ file ].join( ' ' );
const res = file.replace( /_+/g, ' ' ).match( exp );
// ...
}这允许用户搜索像 "raycast"、"community" 或 "ambient occlusion" 这样的标签。
URL 状态持久化
搜索查询反映在 URL 中以便共享链接:
if ( v !== '' ) {
window.history.replaceState( {}, '', '?q=' + v + window.location.hash );
} else {
window.history.replaceState( {}, '', window.location.pathname + window.location.hash );
}页面加载时,提取查询并自动填充:
filterInput.value = extractQuery();
if ( filterInput.value !== '' ) {
panel.classList.add( 'searchFocused' );
updateFilter( files, tags );
}截图系统
每个示例都有对应的截图用作预览缩略图。
截图规格
| 属性 | 值 |
|---|---|
| 位置 | examples/screenshots/ |
| 命名 | {filename}.jpg(匹配示例名称) |
| 尺寸 | 400px 宽(16:9 宽高比) |
| 格式 | 带压缩的 JPEG |
| 加载 | 通过 loading="lazy" 属性懒加载 |
显示模式
浏览器通过预览图标支持两种显示模式:
卡片模式(默认):
- 完整截图预览,56.25% 底部内边距(16:9 比例)
- 截图使用
position: absolute居中填充容器 - 卡片背景色和内边距
精简模式(.minimal 类):
- 截图通过
display: none隐藏 - 纯文本列表视图
- 减少的内边距和外边距
截图生成
截图由测试基础设施(6.4)使用 Puppeteer 捕获渲染的示例生成。命名约定确保自动对应:
webgl_animation_keyframes.html → screenshots/webgl_animation_keyframes.jpg与文档系统的共享架构
示例浏览器和文档系统(6.3)共享大量 UI 基础设施,以提供一致的用户体验。
通用 UI 组件
两个系统使用相同的 HTML 结构和 CSS 选择器:
| 组件 | 选择器 | 用途 |
|---|---|---|
| 面板容器 | #panel | 带导航的固定侧边栏 |
| 头部 | #header | 标题和章节标签 |
| 搜索输入框 | #filterInput | 文本过滤字段 |
| 清除按钮 | #clearSearchButton | 重置搜索状态 |
| 内容区域 | #content | 可滚动导航 |
| 展开按钮 | #expandButton | 移动端菜单切换 |
| 面板遮罩 | #panelScrim | 移动端覆盖背景 |
| 查看器框架 | iframe[name="viewer"] | 内容显示区域 |
响应式行为
两个系统在 640px 处共享相同的移动端断点:
@media all and ( max-width: 640px ) {
#panel {
position: absolute;
width: 100%;
height: var(--header-height);
}
#panel.open {
height: 100%;
}
}在移动端:
- 面板折叠为仅头部
- 展开按钮变为可见
- 面板从右侧滑入
- 遮罩覆盖层使背景内容变暗
样式差异
虽然共享布局,但系统在内容样式上有所不同:
示例浏览器:
- 带缩略图的卡片网格(files/main.css559-604)
- 16:9 宽高比的封面图像容器
- 社区标签徽章
- 预览切换图标
文档系统:
- 带类别的分层文本列表
- 带语法高亮的搜索结果
- 成员函数表示法(例如
BoxHelper.update()) - 可折叠部分
示例文件组织
示例遵循严格的命名约定,编码其类别和功能重点。
命名模式
{category}_{topic}[_{variant}].html示例:
webgl_animation_keyframes.htmlwebgpu_compute_particles_snow.htmlmisc_controls_orbit.html
类别前缀
| 前缀 | 数量(约) | 描述 |
|---|---|---|
| webgl_ | 100+ | WebGL 渲染器示例 |
| webgpu_ | 40+ | WebGPU 渲染器示例 |
| webgl2_ | 5+ | WebGL 2 特定功能 |
| webxr_ | 10+ | WebXR 沉浸式体验 |
| css2d_, css3d_ | 5+ | CSS 渲染技术 |
| misc_ | 20+ | 控制器、导出器、实用工具 |
| physics_ | 10+ | 物理引擎集成 |
名称格式化
getName() 函数将文件名转换为显示名称:
function getName( file ) {
const name = file.split( '_' );
name.shift(); // 移除类别前缀
return name.join( ' / ' );
}示例:
webgl_animation_keyframes→animation / keyframeswebgpu_compute_particles→compute / particlesmisc_controls_orbit→controls / orbit
与测试基础设施的集成
虽然示例浏览器主要是导航工具,但它与测试基础设施(6.4)中描述的自动化测试系统共享基础设施:
- 截图验证:缩略图由捕获回归测试图像的相同 Puppeteer 进程生成
- 示例枚举:两者都使用
files.json作为示例存在的真实来源 - 命名约定:一致的命名支持为每个示例自动生成测试
- iframe 加载:测试系统使用相同的基于 iframe 的加载方法在隔离环境中渲染示例
浏览器作为可视化索引,开发者可以在运行自动化测试前手动验证示例行为。