iter(g289): 气泡跟随滚动重算翻转
This commit is contained in:
@@ -57,6 +57,31 @@ export function tooltip(props: TooltipProps, trigger: HTMLElement): HTMLElement
|
||||
);
|
||||
const root = h('span', { class: 'ua-tip-wrap' }, trigger, bubble);
|
||||
|
||||
// —— 出场期间的位置跟随:气泡本身是 wrap 内的绝对定位(滚动时跟着走),但翻转判定看的是视口,
|
||||
// 页面或任意滚动容器一滚就可能变成「朝上放不下」——所以出场时挂监听,收起时摘掉(不在关闭态空转) ——
|
||||
let bound = false; // 监听是否已挂:重复出场不重复绑定
|
||||
let raf = 0; // rAF 节流句柄:滚动事件高频,一帧最多重算一次
|
||||
const reflow = (): void => place(flipSide(trigger, bubble, props.placement ?? 'top'));
|
||||
const onShift = (): void => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
reflow();
|
||||
});
|
||||
};
|
||||
const bind = (): void => {
|
||||
if (bound) return;
|
||||
bound = true;
|
||||
window.addEventListener('scroll', onShift, true); // 捕获阶段:滚动容器的 scroll 不冒泡,只有捕获能收到
|
||||
window.addEventListener('resize', onShift);
|
||||
};
|
||||
const unbind = (): void => {
|
||||
if (!bound) return;
|
||||
bound = false;
|
||||
window.removeEventListener('scroll', onShift, true);
|
||||
window.removeEventListener('resize', onShift);
|
||||
};
|
||||
|
||||
root.addEventListener('pointerenter', (e: PointerEvent) => { if (e.pointerType !== 'touch') show(); }); // 鼠标进入(触摸交给下面的点按)
|
||||
root.addEventListener('pointerleave', (e: PointerEvent) => { if (e.pointerType !== 'touch') hide(); }); // 鼠标离开
|
||||
root.addEventListener('focusin', show); // 键盘聚焦(focusin 会冒泡)
|
||||
@@ -64,7 +89,11 @@ export function tooltip(props: TooltipProps, trigger: HTMLElement): HTMLElement
|
||||
// —— 触摸屏:没有 hover,改点按。按下即出(不等 150ms),抬手留 1.2s 再收,否则手指正挡着气泡就没了 ——
|
||||
root.addEventListener('pointerdown', (e: PointerEvent) => { if (e.pointerType === 'touch') show(); });
|
||||
root.addEventListener('pointerup', (e: PointerEvent) => { if (e.pointerType === 'touch') hideLater(); });
|
||||
effect(() => { if (open()) place(flipSide(trigger, bubble, props.placement ?? 'top')); }); // 每次出场重算方位:空间不足自动翻转
|
||||
effect(() => {
|
||||
if (!open()) return unbind(); // 收起即摘监听:气泡不在,重算方位没有意义
|
||||
place(flipSide(trigger, bubble, props.placement ?? 'top')); // 出场先算一次:空间不足自动翻转
|
||||
bind(); // 出场期间跟着滚动 / 缩放重算:wrap 定位不会飘,但「视口里还放得下吗」会过期
|
||||
});
|
||||
root.addEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') { e.stopPropagation(); hide(); } // Esc 立即收起且不影响外层弹层
|
||||
});
|
||||
|
||||
@@ -1194,7 +1194,8 @@
|
||||
"鼠标悬停或键盘聚焦后延迟 delay 毫秒出场,移出 / 失焦 / Esc 收起;空间不足时自动翻到另一侧",
|
||||
"触摸屏没有 hover:手指按下即出场,抬手后停留 1.2 秒再收起,免得手指正挡着就消失",
|
||||
"气泡设了 pointer-events: none,不会挡住下面的点击;aria-describedby 已关联,读屏能念出提示文案",
|
||||
"方位支持上下左右四向:贴边或工具栏竖排时用 left / right,出场时按该方向剩余空间自动翻到对侧"
|
||||
"方位支持上下左右四向:贴边或工具栏竖排时用 left / right,出场时按该方向剩余空间自动翻到对侧",
|
||||
"出场期间滚动页面或任意滚动容器,气泡会按视口重算上下翻转(rAF 节流);收起后监听立即摘掉,不在关闭态空转"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user