设计背景
在桌面端浏览时,文章目录(TOC)固定在侧边栏,用户可以随时查看和跳转。但在移动端(≤1024px),侧边栏默认收起,用户失去了快速定位文章章节的能力。对于动辄数千字的技术文章来说,缺少目录导航会严重影响移动端阅读效率。
解决思路
为移动端添加一个浮动操作按钮(FAB,Floating Action Button),固定在右下角。点击后从底部弹出抽屉面板,展示完整的文章目录。点击目录中的任一章节标题,页面平滑滚动到对应位置,同时自动关闭抽屉。
JavaScript 核心逻辑
1class MobileToc {
2 constructor() {
3 this.fab = document.getElementById('mobile-toc-fab');
4 this.drawer = document.getElementById('mobile-toc-drawer');
5 this.overlay = document.getElementById('mobile-toc-overlay');
6 this.isOpen = false;
7 this.init();
8 }
9
10 init() {
11 if (!this.fab || !this.drawer) return;
12
13 this.fab.addEventListener('click', () => this.toggle());
14 this.overlay.addEventListener('click', () => this.close());
15
16 this.drawer.querySelectorAll('a').forEach(link => {
17 link.addEventListener('click', (e) => {
18 e.preventDefault();
19 const target = document.querySelector(link.getAttribute('href'));
20 if (target) {
21 target.scrollIntoView({ behavior: 'smooth', block: 'start' });
22 this.close();
23 }
24 });
25 });
26 }
27
28 toggle() {
29 this.isOpen ? this.close() : this.open();
30 }
31
32 open() {
33 this.isOpen = true;
34 this.drawer.classList.add('open');
35 this.overlay.classList.add('visible');
36 this.fab.classList.add('active');
37 }
38
39 close() {
40 this.isOpen = false;
41 this.drawer.classList.remove('open');
42 this.overlay.classList.remove('visible');
43 this.fab.classList.remove('active');
44 }
45}
46
47export default MobileToc;
CSS 实现
FAB 按钮样式
1.mobile-toc-fab {
2 display: none;
3 position: fixed;
4 bottom: 100px;
5 right: 24px;
6 width: 48px;
7 height: 48px;
8 border-radius: 50%;
9 background: var(--color-primary);
10 color: #fff;
11 border: none;
12 box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
13 z-index: 100;
14 cursor: pointer;
15 transition: transform 0.3s ease, box-shadow 0.3s ease;
16}
17
18.mobile-toc-fab.active {
19 transform: rotate(90deg);
20}
21
22@media (max-width: 1024px) {
23 .mobile-toc-fab {
24 display: flex;
25 align-items: center;
26 justify-content: center;
27 }
28}
底部抽屉面板
1.mobile-toc-drawer {
2 position: fixed;
3 bottom: 0;
4 left: 0;
5 right: 0;
6 max-height: 60vh;
7 background: var(--bg-card);
8 border-radius: 16px 16px 0 0;
9 box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
10 z-index: 101;
11 transform: translateY(100%);
12 transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
13 overflow-y: auto;
14 padding: 20px 24px 32px;
15}
16
17.mobile-toc-drawer.open {
18 transform: translateY(0);
19}
交互细节
- 弹出动画:使用
cubic-bezier(0.4, 0, 0.2, 1)缓动曲线,模拟物理惯性的滑动效果 - 半透明遮罩:抽屉弹出时显示深色遮罩,点击遮罩可关闭抽屉
- FAB 旋转:按钮在激活时旋转 90 度,从列表图标变为关闭图标,语义清晰
- 点击跳转后自动关闭:用户选择章节后无需手动关闭,减少操作步骤
- 安全区域适配:考虑 iPhone 底部安全区域,
padding-bottom留足空间
与桌面端 TOC 的协同
桌面端(>1024px)使用侧边栏固定目录,移动端使用 FAB + 抽屉方案。两者共享同一套目录数据源(Hugo 模板生成的 TOC HTML),仅在展示方式上有所不同。通过 CSS 媒体查询控制显示/隐藏,不存在 DOM 重复渲染的问题。
留言评论
期待你的想法评论加载中