feat(viewer,ui04): E04 interactions — pause reason set, click lock/drag cancel, manual scrub, keyboard semantics, destroy
- pauseReasons Set (hover/focus/pointer/offscreen/hidden/manual + userPaused via Space, no auto-resume); 500ms transient resume with re-evaluation - pointerdown target lock; >8px drag suppresses click and scrubs via nudgeOffset (window-clamped); removed photos clear pending target - keyboard: Space pause toggle, Escape collapse, ArrowUp/Down scrub; input fields untouched - aria-live status announcements (zh/en); viewport touch-action none - e2e: hover freeze/resume, popup deep link hit, drag cancel, keyboard, slot disable destroy Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
advanceStream,
|
||||
anchorRebuild,
|
||||
createStreamState,
|
||||
MAX_DOM_INSTANCES,
|
||||
photoDisplayHeight,
|
||||
PHOTO_GAP_PX,
|
||||
removePhoto,
|
||||
@@ -161,6 +162,38 @@ export function useVerticalPhotoStream(options: UseVerticalPhotoStreamOptions) {
|
||||
if (!scrollable()) stopLoop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动滚动(§4.3):外部(滚轮/拖动/键盘)直接平移当前窗口内容。夹取在
|
||||
* 已渲染窗口范围内(有限真实列表语义——不重放全部历史);自动恢复时从
|
||||
* 当前 offset 锚点继续(不回顶)。paused 状态下亦可用(reducedMotion 手动
|
||||
* 查看路径)。超出窗口尾部时按需补窗(anchor 语义由下次 reanchor 收敛)。
|
||||
*/
|
||||
function nudgeOffset(deltaPx: number): void {
|
||||
const s = state.value;
|
||||
if (s === null || s.items.length === 0) return;
|
||||
let total = 0;
|
||||
for (const item of s.items) {
|
||||
total += photoDisplayHeight(s.sizes[item.queueIdx], innerWidth) + PHOTO_GAP_PX;
|
||||
}
|
||||
const maxOffset = Math.max(0, total - viewportHeight);
|
||||
// 头部上界:负向钳 0(已到最新);尾部下界钳 maxOffset(到窗口尾)。
|
||||
s.offset = Math.max(0, Math.min(s.offset + deltaPx, maxOffset));
|
||||
// 手动平移可能越过退役边界:复用 advance 的退役循环(速度 0 的纯夹取)。
|
||||
let guard = 0;
|
||||
while (
|
||||
guard < MAX_DOM_INSTANCES &&
|
||||
s.offset >= photoDisplayHeight(s.sizes[s.headIdx], innerWidth) + PHOTO_GAP_PX
|
||||
) {
|
||||
const headH =
|
||||
photoDisplayHeight(s.sizes[s.headIdx], innerWidth) + PHOTO_GAP_PX;
|
||||
s.offset -= headH;
|
||||
s.headIdx = (s.headIdx + 1) % s.queue.length;
|
||||
if (s.headIdx === 0) s.gen += 1;
|
||||
guard += 1;
|
||||
}
|
||||
syncRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前与视口相交的照片(可见定义 §5.2:交集面积>0 且高度≥20px)——
|
||||
* E05 气泡归属依据。纯计算,不触 DOM。
|
||||
@@ -215,6 +248,7 @@ export function useVerticalPhotoStream(options: UseVerticalPhotoStreamOptions) {
|
||||
photoById,
|
||||
offsetView,
|
||||
dropPhoto,
|
||||
nudgeOffset,
|
||||
visiblePhotoIds,
|
||||
/** 测试接口(§11.4 受控时间):绕过 rAF 按给定 dt 推进一帧并同步渲染。
|
||||
* 仅单测使用;生产路径不经过本入口。单张/零张静态(不推进,§3.2.8/9)。 */
|
||||
|
||||
Reference in New Issue
Block a user