Timeline
一个基于 React 的库,用于使用 canvas 渲染构建交互式时间线可视化。
@gravity-ui/timeline

一个基于 React 的库,用于构建具有 Canvas 渲染的交互式时间线可视化。
文档
详细信息请参阅 文档。
特性
- 基于 Canvas 的渲染,性能高
- 交互式时间线,支持缩放和平移
- 支持事件、标记、区域、轴和网格
- 背景区域,用于视觉组织和时间段高亮
- 智能标记分组,自动缩放到组 - 点击分组标记可缩放到其单独的组件
- 虚拟化渲染,以提高大型数据集的性能(仅在时间线内容超出视口时激活)
- 可自定义的外观和行为
- 支持 TypeScript,提供完整的类型定义
- React 集成,提供自定义 Hooks
安装
npm install @gravity-ui/timeline
用法
可以在 React 应用中使用时间线组件,基本设置如下:
import { TimelineCanvas, useTimeline } from '@gravity-ui/timeline/react';
const MyTimelineComponent = () => {
const { timeline, api, start, stop } = useTimeline({
settings: {
start: Date.now(),
end: Date.now() + 3600000, // 1 小时后
axes: [],
events: [],
markers: [],
sections: []
},
viewConfiguration: {
// 可选的视图配置
}
});
// timeline - Timeline 实例
// api - CanvasApi 实例 (与 timeline.api 相同)
// start - 用于初始化带 Canvas 的时间线的函数
// stop - 用于销毁时间线的函数
return (
<div style={{ width: '100%', height: '100%' }}>
<TimelineCanvas timeline={timeline} />
</div>
);
};
区域结构
每个区域需要以下结构:
type TimelineSection = {
id: string; // 唯一的区域标识符
from: number; // 开始时间戳
to?: number; // 可选的结束时间戳 (默认为时间线结束时间)
color: string; // 区域的背景颜色
hoverColor?: string; // 区域悬停时的可选颜色
renderer?: AbstractSectionRenderer; // 可选的自定义渲染器
};
区域为时间段提供背景着色,并有助于在视觉上组织时间线内容:
const MyTimelineComponent = () => {
const { timeline } = useTimeline({
settings: {
start: Date.now(),
end: Date.now() + 3600000,
axes: [],
events: [],
markers: [],
sections: [
{
id: 'morning',
from: Date.now(),
to: Date.now() + 1800000, // 30 分钟
color: 'rgba(255, 235, 59, 0.3)', // 半透明黄色
hoverColor: 'rgba(255, 235, 59, 0.4)'
},
{
id: 'afternoon',
from: Date.now() + 1800000,
// 未指定 'to' - 延伸至时间线结束
color: 'rgba(76, 175, 80, 0.2)', // 半透明绿色
hoverColor: 'rgba(76, 175, 80, 0.3)'
}
]
},
viewConfiguration: {
sections: {
hitboxPadding: 2 // 悬停检测的内边距
}
}
});
return <TimelineCanvas timeline={timeline} />;
};
标记结构
每个标记需要以下结构:
type TimelineMarker = {
time: number; // 标记位置的时间戳
color: string; // 标记线的颜色
activeColor: string; // 标记被选中时的颜色 (必需)
hoverColor: string; // 标记悬停时的颜色 (必需)
lineWidth?: number; // 可选的标记线宽度
label?: string; // 可选的标签文本
labelColor?: string; // 可选的标签颜色
renderer?: AbstractMarkerRenderer; // 可选的自定义渲染器
nonSelectable?: boolean;// 标记是否可被选中
group?: boolean; // 标记是否代表一个组
};
标记分组与缩放
时间线会自动将彼此靠近的标记进行分组,并提供缩放功能:
const MyTimelineComponent = () => {
const { timeline } = useTimeline({
settings: {
start: Date.now(),
end: Date.now() + 3600000,
axes: [],
events: [],
markers: [
// 这些标记将被分组在一起
{ time: Date.now(), color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: '事件 1' },
{ time: Date.now() + 1000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: '事件 2' },
{ time: Date.now() + 2000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: '事件 3' },
]
},
viewConfiguration: {
markers: {
collapseMinDistance: 8, // 将相距 8 像素内的标记分组
groupZoomEnabled: true, // 启用点击组进行缩放
groupZoomPadding: 0.3, // 组周围 30% 的内边距
groupZoomMaxFactor: 0.3, // 最大缩放因子
}
}
});
// 监听组缩放事件
useTimelineEvent(timeline, 'on-group-marker-click', (data) => {
console.log('组已缩放:', data);
});
return <TimelineCanvas timeline={timeline} />;
};
工作原理
时间线组件基于 React 构建,提供了一种灵活的方式来创建交互式时间线可视化。其工作原理如下:
组件架构
时间轴被实现为一个 React 组件,可以通过两个主要对象进行配置:
-
TimelineSettings: 控制核心时间轴行为和外观
start: 时间轴的开始时间end: 时间轴的结束时间axes: 轴配置数组events: 事件配置数组markers: 标记配置数组sections: 区段配置数组
-
ViewConfiguration: 管理视觉表示和交互设置
- 控制外观、缩放级别和交互行为
- 可以自定义或使用默认值
事件处理
时间轴组件支持多种交互事件:
on-click: 点击时间轴时触发on-context-click: 右键点击/上下文菜单时触发on-select-change: 选择发生变化时触发on-hover: 鼠标悬停在时间轴元素上时触发on-leave: 鼠标离开时间轴元素时触发
事件处理示例:
import { useTimelineEvent } from '@gravity-ui/timeline/react';
const MyTimelineComponent = () => {
const { timeline } = useTimeline({ /* ... */ });
useTimelineEvent(timeline, 'on-click', (data) => {
console.log('Timeline clicked:', data);
});
useTimelineEvent(timeline, 'on-select-change', (data) => {
console.log('Selection changed:', data);
});
return <TimelineCanvas timeline={timeline} />;
};
React 集成
该组件使用自定义钩子来管理时间轴:
-
useTimeline: 管理时间轴实例及其生命周期- 创建并初始化时间轴
- 处理组件卸载时的清理工作
- 提供对时间轴实例的访问
-
useTimelineEvent: 处理事件订阅和清理- 管理事件监听器的生命周期
- 在组件卸载时自动清理监听器
当组件卸载时,该组件会自动处理时间轴实例的清理和销毁。
事件结构
时间轴中的事件遵循以下结构:
type TimelineEvent = {
id: string; // 唯一标识符
from: number; // 开始时间戳
to?: number; // 结束时间戳(点事件可选)
axisId: string; // 事件所属的轴的 ID
trackIndex: number; // 轴轨道中的索引
renderer?: AbstractEventRenderer; // 可选的自定义渲染器
color?: string; // 可选的事件颜色
selectedColor?: string; // 可选的选中状态颜色
};
直接 TypeScript 使用
Timeline 类可以直接在 TypeScript 中使用,无需 React。这对于集成到其他框架或原生 JavaScript 应用程序非常有用:
import { Timeline } from '@gravity-ui/timeline';
const timestamp = Date.now();
// 创建一个时间轴实例
const timeline = new Timeline({
settings: {
start: timestamp,
end: timestamp + 3600000, // 1 小时后
axes: [
{
id: 'main',
label: '主轴',
color: '#000000'
}
],
events: [
{
id: 'event1',
from: timestamp + 1800000, // 30 分钟后
to: timestamp + 2400000, // 40 分钟后
label: '示例事件',
axisId: 'main'
}
],
markers: [
{
id: 'marker1',
time: timestamp + 1200000, // 20 分钟后
label: '重要点',
color: '#ff0000',
activeColor: '#ff5252',
hoverColor: '#ff1744'
}
],
sections: [
{
id: 'section1',
from: timestamp,
to: timestamp + 1800000, // 前 30 分钟
color: 'rgba(33, 150, 243, 0.2)', // 浅蓝色背景
hoverColor: 'rgba(33, 150, 243, 0.3)'
}
]
},
viewConfiguration: {
// 可选:自定义视图设置
zoomLevels: [1, 2, 4, 8, 16],
hideRuler: false,
showGrid: true
}
});
// 使用 canvas 元素初始化
const canvas = document.querySelector('canvas');
if (canvas instanceof HTMLCanvasElement) {
timeline.init(canvas);
}
// 添加事件监听器
timeline.on('on-click', (detail) => {
console.log('Timeline clicked:', detail);
});
timeline.on('on-select-change', (detail) => {
console.log('Selection changed:', detail);
});
// 完成后清理
timeline.destroy();
Timeline 类提供了丰富的 API 来管理时间轴:
-
事件管理:
// 添加事件监听器 timeline.on('eventClick', (detail) => { console.log('Event clicked:', detail); }); // 移除事件监听器 const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); timeline.off('eventClick', handler); // 触发自定义事件 timeline.emit('customEvent', { data: 'custom data' }); -
时间轴控制:
// 更新时间轴数据 timeline.api.setEvents([ { id: 'newEvent', from: Date.now(), to: Date.now() + 3600000, label: '新事件', axisId: 'main', trackIndex: 0 } ]); // 更新轴 timeline.api.setAxes([ { id: 'newAxis', label: '新轴', color: '#0000ff' } ]); // 更新标记 timeline.api.setMarkers([ { id: 'newMarker', time: Date.now(), label: '新标记', color: '#00ff00', activeColor: '#4caf50', hoverColor: '#2e7d32' } ]); // 更新区段 timeline.api.setSections([ { id: 'newSection', from: Date.now(), to: Date.now() + 1800000, color: 'rgba(255, 193, 7, 0.2)', // 浅琥珀色背景 hoverColor: 'rgba(255, 193, 7, 0.3)' } ]);
实时示例
在我们的 Storybook 中探索交互式示例:
开发
Storybook
本项目包含 Storybook,用于组件开发和文档。
运行 Storybook:
npm run storybook
这将启动 Storybook 开发服务器,端口为 6006。您可以通过 http://localhost:6006 访问它。
构建 Storybook 的静态版本以进行部署:
npm run build-storybook
许可证
MIT