扇形锚点导航测试页
这一页专门用来试 TocFanNav:下面的每个 ## 都会出现在右侧的扇形容器里,点击即可跳转。
(测试用文件,不需要了直接删掉即可。)
一 · 准备工作
先确认右侧导轨里同时出现了两个东西:滚动指示条和扇形锚点导航。
扇形导航用的是 FanVirtualList 的默认 driver="self",也就是它自己带一条滚动轨道;
滚动指示条则继续跟随窗口滚动。两者互不干扰,这一点在下面几节里会反复验证。
二 · 几何关系
扇形把「轨道距离」换算成圆心角:theta = (progress - index) * itemHeight / radius。
圆心落在锚点右侧 radius 处,所以 theta = 0 的那一条正好压在锚点上,
其余条目沿弧向两端张开,横向会轻微外凸。半径越小,弧越弯、条目倾角越大;
半径越大,越接近一条直列。
三 · 前位与淡出
距离前位越远,条目越淡、倾角越大,超出 maxAngle 后就不再渲染。
maxAngle 同时决定虚拟窗口的 overscan:窗口必须比可见角度范围更宽,
否则弧上的条目会被提前卸载,滚动时会出现"边缘条目闪一下"的现象。
四 · 点击跳转
点击任意一条,正文会平滑滚动到对应标题,同时把这条转到扇形的前位。
跳转做两件事:页面 scrollIntoView({ behavior: 'smooth' }) 到 heading,
轨道 scrollTo({ behavior: 'smooth' }) 到 index * itemHeight。
和 window 模式不同,这里不需要任何窗口进度的参与 —— 前位完全由轨道决定。
五 · 手写滚动
扇形自己是一条可滚动的轨道,把鼠标放在它上面滚动只会动扇形,不会动页面。
这也意味着:用户可以用滚轮浏览锚点列表,而正文保持不动; 真要读某一节,再点一下跳过去。
六 · 末尾留白
容器末尾补了一段留白(end-padding="container"),否则最后一条永远到不了前位。
原因很直接:轨道的 scrollTop 上限是「内容总高 − 容器高」,
不加留白的话,最后「容器高 / itemHeight」条根本没有机会滚到锚点位置。
七 · 键盘
当前只做了点击;如果要加方向键,在 TocFanNav 里接 reader.jumpBy(±1) 就行。
reader 里已经把 jumpBy 写好了,它按 activeIndex 前后移动并复用同一条跳转逻辑。
八 · 与文档列表的差别
RightTabWithNav 用 driver="window" 跟随页面滚动;这里用默认的 driver="self"。
还有一个区别是数据源:文档列表来自 content store 的 summaries,
这里来自 reader store 的 toc(当前文档的 page.body.toc)。
九 · 结束
滚到最后一条,它会稳稳停在扇形的前位上。