Lumin Blog 标签云:漂浮碰撞 + 文章列表 + 分页
📌 概述
标签云页面是 Lumin Blog 的特色功能之一,采用物理模拟的漂浮碰撞动画,标签在容器内自由移动并在碰撞时弹开。点击标签后展开带封面的文章卡片列表,支持前端分页。整个标签墙高度根据标签数量和窗口宽度自动适配,确保标签始终有充足的活动空间。
🎯 一、功能说明
| 属性 | 说明 |
|---|---|
| 页面路径 | /tags/ |
| 标签展示 | 彩色标签在容器内自由漂浮,碰撞时弹开 |
| 鼠标悬停 | 悬停标签冻结不动,方便点击 |
| 点击标签 | 展开下方文章列表面板 |
| 文章卡片 | 左侧封面图 + 右侧三行信息(元数据/标题/描述) |
| 分页 | JS 前端分页,默认每页 15 篇 |
| 分页配置 | hugo.toml 中 params.tagCloud.pageSize |
| 高度自适应 | 标签墙高度根据标签数量和窗口宽度自动计算 |
🏗️ 二、架构设计
文件结构
1themes/hugo-theme-lumin/
2├── layouts/_default/taxonomy.html # 标签云页面模板
3├── assets/js/modules/tag-cloud.js # 漂浮动画 + 文章面板逻辑
4└── assets/css/main.css # 标签云相关样式(L4548 起)
数据流
1Hugo 模板 (taxonomy.html)
2 └─ data-articles 属性 → "标题|URL|日期|封面|分类|阅读时间|字数|描述;..."
3 ↓
4JS 解析 (tag-cloud.js)
5 └─ parseArticles() → 按 ";" 分割条目,按 "|" 分割字段
6 ↓
7渲染文章卡片
8 └─ renderArticles() → 动态创建 DOM 元素
🎨 三、标签云头部
头部区域采用图标 + 文字水平排列的布局,居中显示:
- 图标:圆角方形紫色渐变背景,内含标签 SVG 图标
- 标题:中文「标签」,紫色渐变文字
- 副标题:「探索所有文章标签,点击查看相关文章」
- 统计:白色卡片药丸,显示「98 个标签」
分类页(/categories/)同步适配,标题显示「分类」,副标题为「浏览所有文章分类」。
🌊 四、漂浮碰撞动画
核心原理
每个标签作为一个「小球」,拥有位置 (x, y) 和速度 (vx, vy),在 requestAnimationFrame 循环中:
- 移动:
x += vx,y += vy - 边界反弹:碰到容器四壁时速度取反
- 碰撞检测:AABB 矩形重叠检测
- 碰撞响应:按最小重叠轴分离 + 速度交换(模拟弹球)
速度参数
1var speed = 0.35 + Math.random() * 0.5; // 范围 0.35 ~ 0.85 px/帧
2var angle = Math.random() * Math.PI * 2; // 随机方向
3var vx = Math.cos(angle) * speed;
4var vy = Math.sin(angle) * speed;
鼠标悬停冻结
1// mouseenter → frozen = true → 动画循环跳过该标签
2// mouseleave → frozen = false → 恢复移动
碰撞时如果一方被冻结,另一方速度取反(从冻结标签弹开)。
📐 五、高度自适应
标签墙高度根据标签自然排列高度动态计算,确保任何窗口宽度下标签都有充足空间:
1function updateWallHeight() {
2 // 1. 先清除内联 minHeight,让容器回到 CSS 默认值
3 wall.style.minHeight = '';
4
5 // 2. 临时切换为正常文档流,测量自然高度
6 items.forEach(function (t) {
7 t.style.position = 'relative';
8 t.style.left = '';
9 t.style.top = '';
10 });
11 var naturalHeight = wall.scrollHeight;
12
13 // 3. 恢复绝对定位
14 items.forEach(function (t) {
15 t.style.position = 'absolute';
16 });
17
18 // 4. 设置漂浮高度 = 自然高度 × 1.8
19 var floatHeight = Math.max(300, Math.ceil(naturalHeight * 1.8));
20 wall.style.minHeight = floatHeight + 'px';
21}
关键点:
- 先清除
minHeight再测量,确保窗口放大后高度能正确缩小 - 窗口缩放时(
resize事件)重新计算,200ms 防抖 - 标签增减时高度自动跟随变化
📋 六、文章列表
数据格式
通过 data-articles HTML 属性传递,格式:
1标题|URL|日期|封面图|分类名|阅读时间|字数|描述;标题|URL|...
分类名通过 category-display-name.html partial 转换为中文(如 编程开发、示例演示)。
描述字段:优先使用文章 description,为空时自动截取正文前 80 字。
卡片布局
每篇文章卡片为水平布局:
1┌──────────┬──────────────────────────────────┐
2│ │ 2026-05-22 编程开发 6分钟 2544字 │
3│ 封面图 │ 文章标题 │
4│ │ 文章描述文字,最多显示两行... │
5└──────────┴──────────────────────────────────┘
- 第一行:日期 + 分类 + 阅读时间 + 字数(统一灰色文字)
- 第二行:文章标题(加粗,最多 2 行省略)
- 第三行:描述(灰色小字,最多 2 行省略)
📄 七、分页功能
配置
在 hugo.toml 中设置:
1[params.tagCloud]
2 pageSize = 15 # 每页显示文章数(默认 15)
实现
- JS 前端分页,通过
currentPage和pageSize切片显示 - 分页按钮居中排列,当前页为紫色渐变激活态
- 切换页面后自动滚动到文章面板顶部
🎨 八、圆圈数字
标签右侧的数字采用圆形徽章样式:
1.tag-wall-count {
2 display: inline-flex;
3 align-items: center;
4 justify-content: center;
5 min-width: 20px;
6 height: 20px;
7 padding: 0 5px;
8 border-radius: 50%;
9 font-size: 0.65em;
10 font-weight: 700;
11 background: rgba(255,255,255,0.25);
12 color: inherit;
13}
🌙 九、暗黑模式适配
| 元素 | 亮色模式 | 暗黑模式 |
|---|---|---|
| 标签墙背景 | 白色 | rgba(0,0,0,0.2) |
| 标签文字 | 彩色 | 彩色 + text-shadow |
| 圆圈数字 | rgba(255,255,255,0.25) | rgba(255,255,255,0.12) |
| 头部背景 | 淡紫色渐变 | 加深紫色渐变 |
| 文章卡片 | 白色边框 | 深色半透明 |
| 分页按钮 | 白色边框 | 深色半透明 |
📱 十、移动端适配
| 断点 | 标签墙高度 | 封面图宽度 | 文字大小 |
|---|---|---|---|
| 桌面端 (>768px) | 自动计算 × 1.8 | 200px | 标题 1rem |
| 平板端 (≤768px) | 自动计算 × 1.8 | 140px | 标题 0.9rem |
| 手机端 (≤480px) | 自动计算 × 1.8 | 110px | 标题 0.82rem |
⚙️ 十一、配置参考
1# hugo.toml
2
3# 标签云分页
4[params.tagCloud]
5 pageSize = 15
6
7# 默认封面图(文章无封面时使用)
8[params.article]
9 defaultFeaturedImage = "/images/default-cover.webp"
🔧 十二、自定义指南
修改漂浮速度
编辑 tag-cloud.js 中的速度参数:
1var speed = 0.35 + Math.random() * 0.5; // 减小数值 → 更慢
修改高度系数
编辑 tag-cloud.js 中的乘数:
1var floatHeight = Math.max(300, Math.ceil(naturalHeight * 1.8)); // 1.8 → 更大则更高
修改调色板
编辑 tag-cloud.js 中的 palette 数组:
1var palette = [
2 '#4a47a3', '#8e44ad', '#c0392b', '#2980b9',
3 // ... 添加或替换颜色
4];
留言评论
期待你的想法评论加载中