Hexo 博客 Fluid 主题实现代码折叠和文字遮盖效果
本文最后编辑于:2024年11月4日 早上
Hexo&Fluid 代码折叠功能
hexo 博客默认是没有代码折叠功能的,如果需要实现代码折叠功能,需要安装 hexo-sliding-spoiler 库或者借助 Hexo 过滤器功能。
hexo-sliding-spoiler 实现代码折叠
hexo-sliding-spoiler 提供 demo 演示:https://github.com/fletchto99/hexo-sliding-spoiler/blob/master/img/example.gif
安装命令
1
2
3
npm install hexo-sliding-spoiler --save
# or using yarn
yarn add hexo-sliding-spoiler
使用示例
1
2
3
{% spoiler title %}
content
{% endspoiler %}
带空格的需要使用双引号
1
2
3
{% spoiler "Several spaces in the title" %}
content
{% endspoiler %}
如果没起作用,可能是 hexo 没有检测到插件,可以到_config.yml
中加上
1
2
plugins:
- hexo-sliding-spoiler
使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
console.log("Hello, world!");
直接使用的话,会发现代码块和 spoiler 区域有间距,而且标题也是默认白色,如果想要修改的话,可以找到 node_modules\hexo-sliding-spoiler\assets\spoiler.css
进行修改。我的修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
.spoiler {
margin: 0;
padding: 0;
border: 1px solid #353535;
border-radius: 3px;
position: relative;
clear: both;
}
.spoiler .spoiler-title {
background: #303030;
margin: 0;
padding: 5px 15px;
color: #d6d6d6;
font-weight: bold;
font-size: 13px;
display: block;
cursor: pointer;
}
.spoiler .spoiler-title:before {
font-weight: bold;
}
.spoiler.collapsed .spoiler-title:before {
content: "Show ";
}
.spoiler.expanded .spoiler-title:before {
content: "Hide ";
}
.spoiler .spoiler-content {
padding: 0;
margin-bottom: 0;
-moz-transition-duration: 0.3s;
-webkit-transition-duration: 0.3s;
-o-transition-duration: 0.3s;
transition-duration: 0.3s;
-moz-transition-timing-function: ease-in-out;
-webkit-transition-timing-function: ease-in-out;
-o-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
}
.spoiler .spoiler-content figure {
margin: 0;
}
.spoiler.collapsed .spoiler-content {
overflow: auto;
max-height: 0;
}
.spoiler.expanded .spoiler-content {
max-height: 3000px;
overflow: auto;
}
.spoiler .spoiler-content p:first-child {
margin-top: 0 !important;
}
但还是不太完美,自由度不是很够,也可能是我不太会修改吧。
hexo-spoiler 实现文字遮盖
hexo-sliding-spoiler 是受 hexo-spoiler 插件的启发而成的。hexo-spoiler 同样可以帮助你做到文字遮挡效果:
安装命令
1
npm install hexo-spoiler --save
和上面的插件一样,如果没有起作用可能是配置文件得配置一下
1
2
plugins:
- hexo-spoiler
语法
1
{% spoiler option:value text... %}
option:value 中的 value 为可配置选项:
Optionname 选项名 | Type 类型 | value 值 | Effect 效果 |
---|---|---|---|
style | string | blur or box | 文本将被模糊处理或是被方框覆盖 |
color | string | All valid css color NO spaces allowed for inline option! | 仅在 style:box 时起作用;更改框的颜色。默认颜色为黑色 。(不允许内联选项中存在空格) |
p | boolean (in _config.yml or front-matter)string (in inline options) | empty or any string | 遮盖文字将因 <p> 标签换行而非 < span > 标签。如果你想在剧透文本前后加一个新行,可以添加这个。 对于内联选项,分配任何值(除了 “false” ),甚至忽略它会打开它;“false” 意味着关闭。默认状态为关闭。 |
这表的配置项都在_config.yml
中进行配置,例如:
1
2
3
4
5
# ... other configs
# be top-level
spoiler:
style: blur
p: true
对于单独的一篇文章,你可以在文章的 front-matter 中设置,例如:
1
2
3
4
5
6
7
---
title: blah blah
spoiler:
style: box
color: yellow
p: false
---
优先级:inline option 内联选项 > front-matter > _config.yml > default
warning
如果你改变了_config.yml
,请运行 hexo clean
清除缓存。
Hexo 过滤器实现代码折叠(推荐)
Kiyan 佬的文章启发了我,于是我自行钻研了一下。
编写 js
由于 fluid 主题已经引入了 Bootstrap,所以我们可以编写一个 js 文件来满足我们的要求。
在 scripts
目录下创建 codeFloding.js
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 获取唯一 ID
function getUuid() {
// 生成一个随机的唯一标识符,由当前时间戳和随机数拼接而成
return Math.random().toString(36).substring(2, 8) + Date.now().toString(36);
}
// 注册 Hexo 的过滤器,在文章渲染后处理代码块
hexo.extend.filter.register(
"after_post_render",
(data) => {
// 获取代码高亮设置
const { line_number, lib } = hexo.theme.config.code.highlight;
let reg;
// 根据使用的高亮库设置正则表达式
if (lib === "highlightjs") {
if (line_number) {
reg = /(<figure class="highlight.+?>)(.+?hljs (.*?)".+?)(<\/figure>)/gims; // 处理包含行号的 figure
} else {
reg = /(<div class="code-wrapper.+?>)(.+?hljs (.*?)".+?)(<\/div>)/gims; // 处理不包含行号的 div
}
} else if (lib === "prismjs") {
reg = /(<div class="code-wrapper.+?>)(.+?data-language="(.*?)".+?)(<\/div>)/gims; // 处理 PrismJS 的代码块
}
// match begin inner lang end offset string 分别表示的意思是:匹配到的内容的开始、内容、语言、结束、偏移量、原始字符串
data.content = data.content.replace(reg, (match, begin, inner, lang, end, offset, string) => {
const collapseId = `collapse-${getUuid()}`; // 生成唯一的折叠 ID
// 创建一个容器,用于包裹按钮和语言提示
const collapseContainer = `
<div class="collapse-header">
<button class="collapse-btn collapsed" type="button" data-toggle="collapse" data-target="#${collapseId}">
<svg t="1730562455310" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9905">
<path d="M857.766234 511.488511L345.623017 0 297.174825 49.348412 759.846873 511.488511 297.174825 974.651588 345.623017 1022.977023z" fill="#ffffff" p-id="9906">
</path>
</svg>
</button>
<span>${lang}</span> <!-- 显示代码语言 -->
</div>
`;
// collapse表示默认收起,collapse show表示默认展开
const collapseDiv = `<div class="collapse" id="${collapseId}">${inner}</div>`;
return begin + collapseContainer + collapseDiv + end; // 返回修改后的内容
});
return data; // 返回处理后的数据
},
10000 // 设置优先级,使得在其他渲染后执行
);
这份代码的核心在于 <button class="collapse-btn collapsed">
中的属性:
1
data-toggle="collapse" data-target="#${collapseId}"
为什么这么说呢?
当使用 data-toggle="collapse"
和 data-target
属性时,Bootstrap 的 JavaScript 会自动识别这些属性并处理折叠效果。这意味着我们不需要手动编写任何其他 JavaScript 代码来控制折叠内容的显示和隐藏。
处理过程是:
- 当收起或展开按钮被点击时,Bootstrap 先查找与
data-target
属性对应的元素(这里是被加上了id="${collapseId}"
的collapseDiv
)。 - 根据当前状态,Bootstrap 会往查找到的带
data-target
值(这里是collapseId
)的标签中添加或移除show
类,从而控制折叠内容的显示或隐藏。
人话是:Bootstrap 会自动给const collapseDiv
加上show
类或移除来控制展开还是收起。collapseDiv
这里是代码块。 - 同时给按钮更新
aria-expanded
属性,以反映当前状态(值为 true 即展开反之则收起)。 - 同时给按钮增加
collapsed
类来标识收起状态,若无此类则表示展开。
所以,这里有两个值的设定需要注意:
const collapseDiv
的class="collapse"
:
若类名为class="collapse"
表示默认将代码块收起,collapse show
表示默认展开。const collapseDiv
的类名若为不带 show 的类名表示默认收起,则<button class="collapse-btn collapsed"
中的类名class="collapse-btn"
需要加上collapsed
。
即:class="collapse-btn collapsed"
与collapseDiv
收起或展开的状态保持一致。
例如,我的代码默认行为是将代码块收起,所以要设置 const collapseDiv
为:
1
const collapseDiv = `<div class="collapse" id="${collapseId}">${inner}</div>`;
且按钮 button 应设置为:
1
<button class="collapse-btn collapsed" ...略...
另外,你也可以找一个你喜欢的 svg 图用于设置按钮的图标。
相关推荐:阿里巴巴矢量图库
编写 css
在 source\css\
目录下创建 codeFloding.css
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
.collapse-header {
border-radius: 5px 5px 0 0; /* 设置圆角,使背景更柔和 */
position: relative; /* 使按钮可以定位 */
z-index: 1;
background-color: #303030; /* 设置背景颜色为深灰色 */
padding: 5px 5px; /* 添加上下左右各5px的内边距 */
}
.collapse-header span {
color: white;
}
.collapse-header button {
border: none; /* 去掉按钮的边框 */
margin-right: 5px; /* 按钮与语言文本之间的间隔 */
background-color: #303030; /* 按钮背景与容器背景相同 */
cursor: pointer; /* 鼠标悬停时显示为手指光标,表示可点击 */
transition: transform 0.3s ease; /* 添加平滑的旋转过渡效果 */
}
.collapse-header button:focus {
outline: none; /* 去掉按钮的聚焦样式,保持界面简洁 */
}
.collapse-header button svg {
width: 10px;
height: 10px;
fill: white;
transition: transform 0.3s ease; /* 添加过渡效果 */
}
/* 初始状态:箭头向右,为收起状态 */
.collapse-header .collapse-btn.collapsed {
transform: rotate(0deg);
}
/* 点击按钮后,箭头向下,为展开状态 */
.collapse-header .collapse-btn {
transform: rotate(90deg);
}
/* 为展开的内容区域添加底部连接效果 */
.collapse.show {
border-radius: 0 0 5px 5px;
background-color: #303030;
}
.category-collapse.collapse.show {
/* 将背景色设置为透明,使内容区域背景透明 */
background-color: transparent;
}
有兴趣可以自行修改 css 样式使其符合你的审美。
warning
请注意在_config.fluid.yml
中进行引入
一个可选的配置
因为我默认开启了代码块的语言类型显示,所以在代码的右上角会自动显示代码语言,如果点击按钮将代码展开,则会在 header 上出现两个代码语言文字,一左一右不是很美观。
于是我创建了 source\js\watch.js
前情提要
watch.js
是以前写的,你也可以找一个已经写过的 js 文件,在底部写,或是单独创建一个 js 文件用于编写。
为了解决上面的重复问题,编写 js 代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 给代码收起按钮添加监听事件
const collapseBtns = document.querySelectorAll('.collapse-header button');
collapseBtns.forEach(button => {
button.addEventListener('click', () => {
// 获取当前按钮的类名
const btnClassName = button.className; // 使用 button 变量,而不是 collapseBtns
const codeTypeSpan = button.nextElementSibling; // 获取当前按钮后面的 span 标签
// console.log('当前获取到的按钮类名为:', btnClassName);
// console.log('当前获取到的 span 标签为:', codeTypeSpan);
// console.log('当前检测展开状态:', btnClassName.includes('collapsed'));
if (btnClassName.includes('collapsed')) { // 判断按钮是否在展开状态
// 展开之后将 span 标签设为隐藏
codeTypeSpan.style.display = 'none';
} else {
// 收起之后将 span 标签设为显示
codeTypeSpan.style.display = 'inline-block';
}
});
});
这样一来,点击按钮展开之后,header 的代码语言提示问题将会被设为隐藏(并非卸载)。
一个可选配置的优化
为什么说是优化呢?因为实际使用下来,我发现每次都要手动使用鼠标点击那么小的一个按钮实在过于折磨,不如直接点击 header 实现折叠或展开。修改就不多说了,如果你能看懂上面的配置,那么下面的配置也能,因为我只做了小小的修改。
codeFloding.js
:将 data-toggle
和 data-target
换到 header 上,并在 watch.js 中手动切换 button 的 class 类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 获取唯一 ID
function getUuid() {
return Math.random().toString(36).substring(2, 8) + Date.now().toString(36);
}
// 注册 Hexo 的过滤器,在文章渲染后处理代码块
hexo.extend.filter.register(
"after_post_render",
(data) => {
const { line_number, lib } = hexo.theme.config.code.highlight;
let reg;
if (lib === "highlightjs") {
reg = line_number
? /(<figure class="highlight.+?>)(.+?hljs (.*?)".+?)(<\/figure>)/gims
: /(<div class="code-wrapper.+?>)(.+?hljs (.*?)".+?)(<\/div>)/gims;
} else if (lib === "prismjs") {
reg = /(<div class="code-wrapper.+?>)(.+?data-language="(.*?)".+?)(<\/div>)/gims;
}
data.content = data.content.replace(reg, (match, begin, inner, lang, end) => {
const collapseId = `collapse-${getUuid()}`;
const collapseContainer = `
<div class="collapse-header" data-toggle="collapse" data-target="#${collapseId}">
<button class="collapse-btn collapsed" type="button">
<svg t="1730562455310" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9905">
<path d="M857.766234 511.488511L345.623017 0 297.174825 49.348412 759.846873 511.488511 297.174825 974.651588 345.623017 1022.977023z" fill="#ffffff" p-id="9906"></path>
</svg>
</button>
<span>${lang}</span>
</div>
`;
const collapseDiv = `<div class="collapse" id="${collapseId}">${inner}</div>`;
return begin + collapseContainer + collapseDiv + end;
});
return data;
},
10000
);
codeFloding.css
:增加了鼠标悬停在 header 时,将鼠标样式改为 pointer 提示用户
1
2
3
4
5
6
7
8
9
.collapse-header {
border-radius: 5px 5px 0 0; /* 设置圆角,使背景更柔和 */
position: relative; /* 使按钮可以定位 */
z-index: 1;
background-color: #303030; /* 设置背景颜色为深灰色 */
padding: 5px 5px; /* 添加上下左右各5px的内边距 */
cursor: pointer; /* 鼠标悬停时显示为手指光标 */
}
...略...
watch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 给代码收起按钮和整个 header 添加监听事件
*/
const collapseHeaders = document.querySelectorAll('.collapse-header');
collapseHeaders.forEach(header => {
const button = header.querySelector('button');
const codeTypeSpan = header.querySelector('span');
header.addEventListener('click', () => {
console.log('header clicked');
// 手动切换按钮的类名
button.classList.toggle('collapsed');
// 根据按钮状态显示或隐藏 span
if (button.classList.contains('collapsed')) {
// console.log('收起代码块');
codeTypeSpan.style.display = 'inline-block';
} else {
// console.log('展开代码块');
codeTypeSpan.style.display = 'none';
}
});
button.addEventListener('click', (event) => {
// 阻止事件冒泡,避免触发 header 的点击事件
event.stopPropagation();
});
});
以上内容若有侵权,可联系删除