@xwink/ui

useHashAnchor

页内锚点的两件事。原生 hash 跳转只算一次落点,跳完上面的内容再长高(异步列表落地、图片撑开、 手动展开)目标就被推走了;hash 留在地址栏上,刷新又跳回一个早已离开的位置。

XScrollRail 内部就是它,页面用了 XScrollRail 就不必再调;一页只挂一处。

基础

跳转后内容长高
跳到下方目标(600ms 后上方内容长高)
间隔
目标:跳完仍贴在视口顶部;往回滚到看不见它时,地址栏的 hash 被清掉。

选项

选项类型说明
alignMaybeRefOrGetter<boolean>跳完纠偏,缺省开启
clearHashMaybeRefOrGetter<boolean>用户滚走后清掉过期 hash,缺省开启
settleMsnumber跳转后还认多久的高度变化,缺省 4000 毫秒

返回值

名称类型说明
follow()() => void按当前地址的 hash 开始纠偏与过期判断;路由改 hash 后调

判据

  • 纠偏:跳完逐帧比目标在文档里的位置,变了就瞬间贴齐(纠偏不是导航,平滑滚会跟正在进行的跳转 打架),超时或用户一动手(滚轮、触摸、指针按下、按键)就撒手。比位置而不是观察某个盒子的尺寸: 撑开它的可能是任意一层祖先。贴齐用 scrollIntoView,吸顶栏的让位交给目标的 scroll-margin-top。
  • 清 hash:目标先被看见、再整块滚出视口,才算用户离开了它。不按「当前章节不再是它」判—— 比视口矮的节永远占不到视口中部,那样会在刚跳到时就把 hash 抹掉,而 hash 指向的也未必是章节。 清理用 replaceState 并保留 history.state(路由的前进后退记录在里面),位置恢复交给浏览器 原生的 scrollRestoration。
  • 只认原生 hashchange(<a href="#x">)。经路由 pushState 改的 hash 不触发它,那种跳转 之后调一次 follow()。

判据里的纯逻辑单独导出:hashTargetId(hash)(解码、不经选择器,#1-intro 这类 id 也取得到)、 followDrift(options)、createStaleTracker()。