为 Hexo 主题添加分类与标签列表

1.7k 3 mins
... ...

  今日成果:如题,以及增加了新参数为首页卡片的文章标题进行换行,外加把标题字体换成了「京华老宋体」。是说这个字体居然在字图 CDN 上不是热门,好看死了!一群没品味的东西!
  当然好看的东西也并不是很完美,比如半角空格似乎太宽了一点导致中英文之间留白过大,小问题,用现成的标点缩进方案继续解决一下就行,(ゝω・)テヘッ

  写代码真的能很好地克服我的启动困难,因为这实在是一件太有「秩序感」的事:提出问题→设计方案→着手解决→show off
  代价是克服过头躁狂发作,作息又变回昼伏夜出,根据鼻尖隐隐传来的痛感不出意外的话马上就要冒痘了……
  嗯道理我都懂但我还是在熬夜。毕竟作息正常的时候我一天只睡 6 个小时,但是昼伏夜出能睡满 8 个小时……总之假期还有一个月应该还能慢慢调回去……吧……

示例

  分类列表的实现原理和 tag 是一样的,只是我暂时还没想好分类的 css 该怎么写,就用 tag 来做个示范吧。
  实现这个可以到处搬的 tag 列表用到了以下流程:

  • 使用 markdown 的 front matter 判断是否渲染列表
  • 添加渲染 tag 列表的 ejs
  • 在需要放置列表的地方手动插入设置了 id 的 div
  • 使用 JS 将渲染出的列表插入到 div 中

  接下来是每一步的实现过程⸺

基础实现

  首先用 hexo new page xxx 新建一个页面用来展示标签列表,因为目前内容太少不好排版我直接扔进 about 里了【。
  然后在 markdown 文件的 front matter 部分(就是写标题分类的那块地方)加一行写上 taglist: true ,数据名写成什么不重要,后面设置为 true 就行。

Hexo 变量

  需要用到的变量如下:

  • site.tags|用于读取全站标签列表
  • site.categories|用于读取全站分类列表

  具体的变量可以参考官方文档,写这篇文章的时候我才惊觉我当时写代码的时候好像误入了什么盗版文档,然后被狗屁不通的机翻吓晕.jpg 好险我还以为 hexo 的中文文档要和中文搜索引擎一样完蛋了呢哈哈

EJS 部分

  注:如果你的主题并不是用 ejs 写的,那么这个阶段请自食其力吧.jpg

  在主题的 layout\_partials 文件夹下新建 taglist.ejs ,然后写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<% if (page.taglist && site.tags.length){ %>
// 判断页面包含「taglist:true」,且网站存在一个或以上的 tag
<ul id="tag-list">
<% site.tags.sort('name').each(function(item){ %>
// 按照 tag 名称排序后逐个渲染,实际上执行的时候并不是按字面上的「名称」而是后台的 ID
<li id='<%= item.name %>'>
// 把列表元素的 ID 设为 tag 的名称
<a class="flat-box" href="<%= url_for(item.path) %>">
// 呃我是不是想写 flex 打错了?总之在列表里添加一个链接指向 tag 的页面
<span class='name'><%= item.name %></span>
// 创建一个 span 写出 tag 名称
<span class='count'><%= item.posts.length %></span>
// 创建一个 span 写出 tag 关联的文章数量
</a>
</li>
<% }); %>
</ul>
<% } %>

  分类和 tag 的原理是一样的,把 tags 相关的变量替换成 categories 就行。
  写完这个文件以后找到主题的 layout\post.ejs 文件,新建一行写上:

1
<%- partial('_partials/list', {post: page}) %>

  此时刷新页面 tag 列表就已经正确生成了,不过大概率根本看不到或者出现在奇怪的地方,接下来需要用 JS 把它放到正确的位置。

JS 部分

  先直接在 markdown 文件里创建一个 div 用于定位:

1
<div id='tag-box'></div>

  然后在代码中写上(需要先引入 jQuery):

1
2
3
4
5
if ($('#tag-list').length > 0) {
$(document).ready(function() {
("#tag-list").prependTo("#tag-box");
})
};

  哎这个是纯乱写的我觉得所见即所得吧,不翻译了感觉看着有点丢人【。
  总之做完这一步 tag 列表就出现在页面正文中了,接下来开始完成样式⸺

样式设计

  我对这个标签列表的期望如下:

  • 要显示每个 tag 关联的文章数量 ✓
  • 鼠标悬停在 tag 上的时候悬停的对象要变宽
  • tag 的宽度和颜色要根据使用频率有不同的表现

  第一条在刚才的 layout 文件里已经实现了,现在开始解决后面两条。

悬停变宽

  我发现我对翻译 css 这件事没什么热情……写了太多年了 css 对我来说已经像喝水一样熟悉,一个字一个字地翻译它实在无聊。
  总之把列表 display: block 之后在悬停上加上 padding 就能做到,算是很简单的部分。

按使用频率变换尺寸

  变换尺寸用的是 flex-grow 属性,需要容器上有空位才能出现宽度差,不过一会还要变色,这里看不出来问题也不大。
  检测词频最快的方法还是直接改 layout ,回到刚才新建的文件找到生成 li 的那一行:

1
2
3
4
5
<li id='<%= item.name %>'>
// ↑把这行改成↓
<li id='<%= item.name %>'
style="flex-grow: <%= Math.round(item.posts.length / site.posts.length * 100) %>;">
// 将 flex-grow 值设置成 tag 文章数占全站文章的比例再 × 100

按使用频率变色

  变色同样需要改 layout,继续在 li 标签里加东西:

1
2
3
4
5
6
7
8
9
10
11
12
<li id='<%= item.name %>'
style="flex-grow: <%= Math.round(item.posts.length / site.posts.length * 100) %>;">
// ↑改成↓
<li id='<%= item.name %>'
style="flex-grow:
<%= Math.round(item.posts.length / site.posts.length * 100) %>;
background: rgba(176,15,69,
<%= Math.round(item.posts.length / site.posts.length * 100) %>
// 将 tag 文章数占比 × 100 后取整作为背景颜色的透明百分比
// 不用 (0,0,0,.11) 的形式是因为计算结果如果是个位数的话会变成「.9」即 90%
%)"
>

  这里就得到了一个根据词频拥有不同透明度背景的 tag 列表,接下来为了达成在两种颜色渐变的效果,为 li 添加 position:relative; 样式后创建一个伪对象:

1
2
3
4
5
6
7
8
li::before {
content: '';
position: absolute;
background: #000;
width: 100%;
height: 100%;
z-index: -1;
left: 0;

  这样就会在 tag 底下垫一层黑色的底色,如果黑色挡住了文字继续调整 z-index 属性即可。
  需要注意的是因为背景颜色直接写在了 dom 标签里,所以用 css 添加悬停变色效果的时候需要在后面加上 !important 才能正确覆盖,如下:

1
2
3
4
#tag-box #tag-list li:hover {
background: #f00 !important;
padding: 0 1em;
}