You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.5 KiB
6.5 KiB
ADR 003: 分镜看板基于 orderIndex 的性能优化
状态
提议中 (Proposed)
背景
当前分镜看板基于时间轴(startTime/endTime)布局,存在以下问题:
- 布局计算复杂(需要处理重叠、间隙)
- 跳转定位困难(需要计算累积时间)
- 不适合虚拟滚动(时间不连续)
决策
采用基于 orderIndex 序号排序 + 虚拟滚动分页的方案。
核心设计理念
抛开时间轴概念:
- 分镜按 orderIndex 顺序排列(紧密相连,无间隙)
- 宽度由 duration 决定,位置由 orderIndex 决定
- 使用虚拟滚动只渲染可见分镜
架构设计
1. 数据分层
Layer 1: 骨架数据(Skeleton)
├─ 用途:计算总宽度、跳转定位
├─ 数据:id, orderIndex, duration
└─ 大小:200 分镜 ≈ 5-10 KB
Layer 2: 分页数据(Paginated)
├─ 用途:虚拟滚动渲染
├─ 数据:按 orderIndex 范围加载(每页 50 个)
└─ 大小:50 分镜 ≈ 25-30 KB
2. API 设计
骨架数据接口(新增)
GET /api/v1/projects/{project_id}/storyboard-board/skeleton
响应:
{
"projectId": "xxx",
"storyboardCount": 200,
"tracks": [
{
"type": "storyboard",
"items": [
{
"id": "xxx",
"orderIndex": 0,
"duration": 3.0
},
{
"id": "yyy",
"orderIndex": 1,
"duration": 5.0
}
]
}
]
}
数据量:200 分镜 ≈ 5-10 KB
分页数据接口(新增)
GET /api/v1/projects/{project_id}/storyboard-board/paginated
?startIndex=0
&endIndex=49
&trackTypes=storyboard,resource
响应:
{
"items": [
{
"id": "xxx",
"type": "storyboard",
"orderIndex": 0,
"duration": 3.0,
"data": {
"title": "分镜 1",
"thumbnail_url": "...",
"resources": {...}
}
}
]
}
数据量:50 分镜 ≈ 25-30 KB
3. 前端架构
虚拟滚动实现
import { VariableSizeList } from 'react-window';
// 1. 加载骨架数据
const { data: skeleton } = useStoryboardBoardSkeleton(projectId);
// 2. 计算每个分镜的宽度
const getItemSize = (index: number) => {
const item = skeleton.items[index];
return item.duration * pps; // pps = pixels per second
};
// 3. 虚拟滚动渲染
<VariableSizeList
height={600}
itemCount={skeleton.items.length}
itemSize={getItemSize}
width="100%"
layout="horizontal"
>
{({ index, style }) => (
<StoryboardItem
index={index}
style={style}
data={getItemData(index)}
/>
)}
</VariableSizeList>
分页加载策略
// 计算可见范围的 orderIndex
const calculateVisibleRange = (scrollLeft: number, containerWidth: number) => {
let accumulatedWidth = 0;
let startIndex = 0;
let endIndex = 0;
for (let i = 0; i < skeleton.items.length; i++) {
const itemWidth = skeleton.items[i].duration * pps;
if (accumulatedWidth <= scrollLeft && startIndex === 0) {
startIndex = i;
}
if (accumulatedWidth <= scrollLeft + containerWidth) {
endIndex = i;
} else {
break;
}
accumulatedWidth += itemWidth;
}
return { startIndex, endIndex };
};
// 加载可见范围的数据
const { data: visibleItems } = useStoryboardBoardPaginated(
projectId,
visibleRange.startIndex,
visibleRange.endIndex
);
跳转定位实现
// 点击分镜列表时跳转到对应分镜
const scrollToStoryboard = (storyboardId: string) => {
// 1. 找到分镜的 orderIndex
const targetItem = skeleton.items.find(item => item.id === storyboardId);
if (!targetItem) return;
// 2. 计算前面所有分镜的宽度总和
let scrollLeft = 0;
for (let i = 0; i < targetItem.orderIndex; i++) {
scrollLeft += skeleton.items[i].duration * pps;
}
// 3. 滚动到目标位置
listRef.current?.scrollTo(scrollLeft);
};
4. 性能优化
预加载策略
// 预加载相邻页(前后各 25 个分镜)
const preloadRange = {
startIndex: Math.max(0, visibleRange.startIndex - 25),
endIndex: Math.min(skeleton.items.length - 1, visibleRange.endIndex + 25)
};
useStoryboardBoardPaginated(projectId, preloadRange.startIndex, preloadRange.endIndex);
缓存策略
// TanStack Query 缓存配置
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 分钟
cacheTime: 10 * 60 * 1000, // 10 分钟
keepPreviousData: true, // 保留上一页数据,避免闪烁
},
},
});
实施计划
阶段 1:后端 API(2-3 天)
- 新增骨架数据接口
/storyboard-board/skeleton - 新增分页数据接口
/storyboard-board/paginated - 添加响应压缩(gzip)
阶段 2:前端基础架构(2-3 天)
- 实现骨架数据加载
- 实现分页数据加载
- 实现数据缓存管理
阶段 3:虚拟滚动(3-4 天)
- 集成 react-window(水平虚拟滚动)
- 实现可见范围计算
- 实现跳转定位功能
阶段 4:优化和测试(2-3 天)
- 性能测试(1000+ 分镜)
- 跳转定位测试
- 用户体验优化
总计:9-13 天
性能目标
| 指标 | 当前 | 目标 |
|---|---|---|
| 首次加载时间 | 2-5s | <300ms |
| 骨架数据量 | 300-500 KB | <10 KB |
| 分页数据量 | - | <30 KB |
| 内存占用 | 随分镜数增长 | 恒定 ~50 MB |
| 滚动 FPS | 30-40 | 60 |
| 跳转定位时间 | - | <100ms |
| 支持分镜数 | <200 | 1000+ |
优势
- 架构简单:无需复杂的时间轴计算
- 性能卓越:虚拟滚动 + 分页加载
- 跳转快速:基于 orderIndex 直接计算位置
- 易于维护:逻辑清晰,代码简洁
- 可扩展性强:支持大规模数据
劣势
- 需要重构:现有时间轴逻辑需要调整
- 开发周期:预计 2 周
- 测试成本:需要大量测试
与 ADR 002 的对比
| 维度 | ADR 002(时间轴) | ADR 003(orderIndex) |
|---|---|---|
| 架构复杂度 | 高 | 低 |
| 实施难度 | 高 | 中 |
| 性能 | 优秀 | 卓越 |
| 跳转定位 | 复杂 | 简单 |
| 开发周期 | 2-3 周 | 1.5-2 周 |
推荐:ADR 003(orderIndex 方案)