iter(g106): 滚动监听改为卸载自清理
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { h } from '@ui-arch/frame';
|
||||
import type { AffixProps } from './types.js';
|
||||
import { watchViewport } from './watch.js';
|
||||
|
||||
/*** 固钉:内容滚动越过阈值后转 fixed 贴住视口顶部,外层保留占位高度避免布局跳动 ***/
|
||||
export function affix(props: AffixProps, ...children: unknown[]): HTMLElement {
|
||||
@@ -36,8 +37,6 @@ export function affix(props: AffixProps, ...children: unknown[]): HTMLElement {
|
||||
}
|
||||
};
|
||||
|
||||
scroller.addEventListener('scroll', sync, { passive: true }); // 被动监听,不阻塞滚动
|
||||
window.addEventListener('resize', sync); // 视口变化会改判定与宽度
|
||||
sync(); // 首屏同步一次:刷新时若已滚动到位,直接进入固定态
|
||||
watchViewport(wrap, scroller, sync); // 滚动 / 缩放后重算,节点脱离文档即自摘监听
|
||||
return wrap;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { h } from '@ui-arch/frame';
|
||||
import type { AnchorItem, AnchorProps } from './types.js';
|
||||
import { watchViewport } from './watch.js';
|
||||
|
||||
/*** 锚点导航:竖排区块链接,滚动时高亮当前区块,点击平滑跳转(尊重减少动效偏好) ***/
|
||||
export function anchor(props: AnchorProps): HTMLElement {
|
||||
@@ -70,8 +71,6 @@ export function anchor(props: AnchorProps): HTMLElement {
|
||||
}
|
||||
|
||||
const host: HTMLElement | Window = scroller ?? window;
|
||||
host.addEventListener('scroll', sync, { passive: true }); // 被动监听:不阻塞滚动
|
||||
window.addEventListener('resize', sync); // 视口变化会改变判定线
|
||||
sync(); // 首屏同步一次,刷新后停在中间位置也能高亮正确项
|
||||
watchViewport(root, host, sync); // 滚动 / 缩放后同步高亮,节点脱离文档即自摘监听
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { h } from '@ui-arch/frame';
|
||||
import type { BackTopProps } from './types.js';
|
||||
import { watchViewport } from './watch.js';
|
||||
|
||||
/*** 返回顶部:滚动越过阈值浮现,环形进度显示已滚比例,点击平滑回顶(尊重减少动效偏好) ***/
|
||||
export function backTop(props: BackTopProps = {}): HTMLElement {
|
||||
@@ -39,8 +40,6 @@ export function backTop(props: BackTopProps = {}): HTMLElement {
|
||||
props.onClick?.();
|
||||
};
|
||||
|
||||
scroller.addEventListener('scroll', sync, { passive: true }); // 被动监听:不阻塞滚动
|
||||
window.addEventListener('resize', sync); // 视口变化会改可滚距离
|
||||
sync(); // 首屏同步一次,避免刷新后停在中间位置却未浮现
|
||||
watchViewport(btn, scroller, sync); // 滚动 / 缩放后同步浮现态,节点脱离文档即自摘监听
|
||||
return btn;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Signal } from '@ui-arch/frame';
|
||||
import { pushOverlay } from './overlay.js';
|
||||
import { button } from './button.js';
|
||||
import type { TourHandle, TourProps } from './types.js';
|
||||
import { watchViewport } from './watch.js';
|
||||
|
||||
/*** 引导漫游:聚光框 + 说明卡逐步指引,聚光用超大 box-shadow 挖洞省掉遮罩层,Esc / 跳过可退出 ***/
|
||||
export function tour(props: TourProps): TourHandle {
|
||||
@@ -84,8 +85,7 @@ export function tour(props: TourProps): TourHandle {
|
||||
};
|
||||
|
||||
const onViewport = (): void => { if (idx() >= 0) place(); }; // 滚动/缩放后重算位置
|
||||
window.addEventListener('resize', onViewport);
|
||||
window.addEventListener('scroll', onViewport, { passive: true });
|
||||
watchViewport(root, window, onViewport); // 节点脱离文档即自摘监听:引导层反复挂载也不累积
|
||||
|
||||
return {
|
||||
el: root,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*** 滚动 / 视口监听:根节点被移出文档后自动摘净监听,避免单页应用里反复挂载累积监听 ***/
|
||||
export function watchViewport(root: HTMLElement, host: HTMLElement | Window, handler: () => void): void {
|
||||
// —— 状态集中声明 ——
|
||||
const run = (): void => {
|
||||
if (!root.isConnected) { // 已卸载:一次性摘掉 scroll 与 resize,后续不再进 handler
|
||||
host.removeEventListener('scroll', run);
|
||||
window.removeEventListener('resize', run);
|
||||
return;
|
||||
}
|
||||
handler();
|
||||
};
|
||||
host.addEventListener('scroll', run, { passive: true }); // 被动监听:不阻塞滚动
|
||||
window.addEventListener('resize', run); // 视口变化会改判定线与可滚距离
|
||||
handler(); // 首帧同步一次:刷新时停在页面中段也能得到正确状态
|
||||
}
|
||||
Reference in New Issue
Block a user