Repository navigation
[Feature Request] 优化 Web 播放器底部 EPG 时间轴刻度与交互体验 #770
Copy link
Copy link
Closed
Description
期望行为(Expected Behavior)及实现细节
1. 拖拽时间指示线精准定位(Drag Playhead for Time-Shift)
交互逻辑:
- 鼠标悬停在红色指示线上方时,光标变为
grab;按下时变为grabbing。 - 鼠标水平拖动时,指示线跟随鼠标 X 坐标实时移动,同时播放器内部
currentTime更新为对应时间点。 - 拖动过程中,在指示线顶部显示一个悬浮气泡(Tooltip),内容为当前拖拽到的精确时间(格式
HH:mm:ss)以及该时间点对应的节目名称。 - 松开鼠标后,播放器正式执行 seek 操作,若该时间点属于历史时段则加载时移流,若属于未来时段则忽略或停留在当前直播。
前端实现参考:
- 监听
mousedown/touchstart事件在指示线元素上。 - 在
mousemove中通过clientX与时间轴总宽度比例计算出对应的时间偏移量。 - 利用
requestAnimationFrame更新指示线位置和 Tooltip 内容,避免频繁 DOM 操作导致性能问题。
2. 节目块悬停详情浮窗(Hover Tooltip on Program Block)
交互逻辑:
- 鼠标悬停在任意节目块(矩形区域)上超过 300ms 后,弹出一个半透明浮窗。
- 浮窗内容包含:节目完整名称、开始时间 – 结束时间、节目简介(若有)、节目类别标签(如“新闻”“电视剧”)。
- 浮窗位置跟随鼠标指针,超出屏幕边界时自动翻转方向。
- 鼠标移出节目块后延迟 200ms 关闭浮窗,防止闪烁。
前端实现参考:
- 使用
mouseenter/mouseleave事件,配合setTimeout实现延迟显示。 - 浮窗采用绝对定位,动态计算
left/top,必要时使用getBoundingClientRect()判断边界。 - 数据来源:EPG JSON 中的
description、category等字段。
3. 视觉状态区分(Visual State Differentiation)
设计建议:
状态 | 颜色/样式
-- | --
历史节目(已播完) | 灰色或浅蓝色,透明度 0.7
当前正在播放的节目 | 高亮色(如蓝色边框 + 白色背景)
未来节目(未开始) | 半透明虚线边框,或淡绿色
- 当前时间指示线(红色竖线)加粗至 2px,并在底部增加一个小三角形或圆点标识。
- 指示线与当前节目块交叉处增加发光阴影效果。
实现逻辑:
- 根据 EPG 数据的
start_time和end_time与服务器当前时间比较,动态赋予 CSS class。 - 例如:
.program.past { background: #e0e0e0; }.program.live { border: 2px solid #ff6600; }
4. 节目块点击跳转时移(Click to Jump to Program Start)
交互逻辑:
- 点击历史节目块(非直播、非未来)时,立即将播放器 seek 到该节目的起始时间点。
- 点击当前直播节目块时,不做跳转或跳转到直播最新位置(即清除时移状态)。
- 点击未来节目块时,弹出提示“该节目尚未播出”。
实现细节:
- 在节目块上绑定
click事件,读取该节目块的data-start-time属性(Unix 时间戳)。 - 调用播放器 API(如
video.currentTime = targetTimestamp - liveEdgeOffset)或通过 rtp2httpd 提供的时移 URL 参数实现。
5. 文本溢出与自适应(Text Overflow Handling)
需求细分:
- 对于宽度大于 80px 的节目块,正常显示节目名称,超出部分用省略号
…表示。 - 对于宽度小于 80px 的节目块,完全隐藏文字,仅在悬停时显示。
- 当用户鼠标悬停在该节目块上时,启动跑马灯滚动效果(从右向左循环滚动完整名称),移出后停止。
CSS/JS 实现:
- 默认使用
text-overflow: ellipsis; white-space: nowrap; overflow: hidden;。 - 跑马灯使用
@keyframes marquee+animation-play-state: paused/running控制,通过 JS 监听 hover 事件切换。
6. 平滑滚动与虚拟列表(Smooth Scrolling & Virtualization)
问题:当前时间轴平移可能因一次性渲染全部节目块(如 48 小时 EPG)导致 DOM 节点过多,造成滚动卡顿。
解决方案:
- 虚拟列表:只渲染视口内可见的节目块(加上前后各 20% 的缓冲区)。根据时间轴容器的
scrollLeft或translateX值动态计算需要显示的节目索引。 - 平滑动画:左右箭头点击时,使用
transform: translateX()+transition: transform 0.3s ease代替直接修改scrollLeft,避免重排。 - 当用户手动拖拽时间轴空白区域(或使用鼠标滚轮)时,同样应用
requestAnimationFrame驱动平移。
补充建议(Nice to Have)
- 时间轴底部增加“当前时间”的固定标签,始终显示
YYYY-MM-DD HH:mm:ss,每秒更新。 - 支持键盘快捷键:左右方向键微调时间(±15秒),上下方向键切换频道(需联动 EPG)。
- 若 EPG 数据为空,显示友好占位文字“暂无节目信息”。
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
CGG888 commentedon Sep 16, 2026
在现有最新代码的基础上做了一些调整,增加了epg刻度! @
CGG888 commentedon Sep 17, 2026
已在 #771 实现。本改动纯前端(Web 播放器),不含服务端改动、不需要新增配置项,合入后随固件/发布生效。
需求对照
HH:mm:ss+ 该时刻节目名;拖到直播边缘显示「返回直播」;松手才 seek;目标一律钳制到now,不会去拉未来流。拖动全程用requestAnimationFrame直写 DOM,不触发组件重渲染。<desc>/<category>目前在 XMLTV 解析白名单里被丢弃,EPGProgram也只有id/title/start/end,补简介/类别需要改数据模型与 worker 传输,本次刻意限定在前端。配色沿用播放器既有调色板(进度条同款蓝渐变 + 直播徽标的玫红),未采用红/橙/浅灰(那套是浅色主题写法,播放器是深色玻璃拟态,且需同时适配「简洁」主题)。
➕ 额外:时移时「播放位置线」与「当前时刻线」分离,两线之间就是还能回看的区间。
➕ 历史节目点击会先弹确认框:M3U 里没有
catchup-days,客户端无法知道上游回看窗口大小,直接请求可能拉不到流。prefers-reduced-motion与「简洁」主题下不滚动。原因:单频道 48 小时只有 50–100 个块,DOM 从不是瓶颈,真正的成本是每秒/每帧重渲染——所以细网格改用 CSS 渐变绘制(288 条线 0 个 DOM 节点)、只渲染与窗口相交的块、1Hz 的播放指示线隔离到独立子组件、拖动/悬停/浮窗全部 rAF 直写 DOM。
补充建议(Nice to have)
←/→是 ±5 秒、↑/↓换台(原本就有)。改成 ±15 秒还是加长按加速,想听维护者意见。顺带修掉/优化的几点
◀▶前面固定显示「今天 9/17」;跨天显示「昨天 9/16 – 今天 9/17」。原先把日期拼在 0 点刻度标签里,窗口边缘时会被挤出刻度带。ResizeObserver+document.fonts.ready)——刻度标签是像素定位、刻度线是百分比定位,缺了这一步就会错位。验证
pnpm web-ui:test:98 条通过,其中新增 50 条(44 条纯逻辑:时间窗/刻度/轴标签/点击决策表/跨天日期换算;6 条渲染级)pnpm type-check:tsc、biome check、vite build均干净试用方式
拉到 PR 分支
feat/epg-timeline后pnpm install && pnpm web-ui:dev,或在发布版上直接用(无需改配置)。上机建议重点看:开关侧边栏/进全屏后刻度是否仍然对齐、跨 0 点时日期是否正确、长节目名是否自动滚动、拖拽气泡与松手跳转是否跟手。feat(web-ui): add an EPG timeline band to the web player (#771)