hexo主题Hacker的二开经历

博客二开

前言

因为之前想要改改现在使用的主题,但是太小众了,根本搜不到什么文章,于是就萌生了自己写篇文章的想法。

先介绍一下改了哪些东西吧。

  • 代码块mac格式

  • 代码块自动折叠

  • 代码块复制

  • 目录

  • 文章更新时间


但是由于这些改变是断断续续进行的,所以就只会介绍我还记得清楚的部分。如果感兴趣可以在我的github仓库查看具体的改变。

代码块mac格式

先找到node_modules/hexo-util/dist/highlight.js,可能不是这个目录,反正找到这个文件就行了。

前面改成

1
2
3
4
let result = `<div class="highlight-wrap" data-rel="${data.language}">`;
// 添加复制按钮
result += `<input class="expand-code" type="image" src="/images/open.svg" alt="展开" />`;
result += '<input class="btn-copy" type="image" src="/images/copy.svg" alt="复制" />';

后面补上div标签

1
result += '</div>';

进行了这一步之后就可以直接git我的仓库使用了。

image-20250318200333167

接着在主题的source/css/components目录创建一个mac.styl

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
//mac 风格
.highlight-wrap[data-rel] {
position: relative;
background: #000;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 10px 30px 0px rgba(0, 0, 0, 0.4);
margin: 20px 0 20px 0;
padding: 17px 0px 0px 0px;

&::before {
color: white;
content: attr(data-rel);
height: 35px;
line-height: 35px;
background: #21252b;
color: #fff;
font-size: 16px;
position: absolute;
top: -1px;
left: -15px;
width: 100%;
font-family: 'Consolas', sans-serif;
font-size: 1.2em;
font-weight: bold;
padding: 0px 80px;
text-indent: 15px;
float: left;
}
&::after {
content: ' ';
position: absolute;
-webkit-border-radius: 50%;
border-radius: 50%;
background: #fc625d;
width: 12px;
height: 12px;
top: -3px;
left: 15px;
margin-top: 13px;
-webkit-box-shadow: 20px 0px #fdbc40, 40px 0px #35cd4b;
box-shadow: 20px 0px #fdbc40, 40px 0px #35cd4b;
z-index: 3;
}

&:: table{
margin: 0;
padding: 0;
}

&:: pre{
margin: 0;
padding: 0;
}
}

最后在style.styl加上@import “components/mac”

代码块自动折叠

在source目录创建一个js目录,然后创建一个fold.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
document.addEventListener("DOMContentLoaded", function () {
const codeBlocks = document.querySelectorAll('figure.highlight');

codeBlocks.forEach(block => {
const pre = block.querySelector('pre');
const lines = pre.querySelectorAll('.line');
const maxLines = 15;
const highlightWrap = block.closest('.highlight-wrap');

if (!highlightWrap) return;

// 获取展开按钮
const expandButton = highlightWrap.querySelector('.expand-code');

if (lines.length > maxLines) {

block.classList.add('collapsed');

// 显示展开按钮
if (expandButton) {
expandButton.style.display = "block";

expandButton.addEventListener('click', function () {
block.classList.toggle('collapsed');
expandButton.classList.toggle('rotated');
});
}
} else {
// 代码行数少于 maxLines,隐藏展开按钮
if (expandButton) {
expandButton.style.display = "none";
}
}
});
});

在article.ejs里面写入

1
2
<!-- 代码块折叠js -->
<script type="text/javascript" src="../../../../js/fold.js"></script>

image-20250318201523845

在之前那个mac.styl的位置创建fold.styl

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
/* 代码块折叠时的样式 */
.highlight.collapsed pre {
max-height: 16rem; /* 设置代码块最大高度 */
overflow: hidden; /* 隐藏超出部分 */
position: relative;
}

/* 折叠时的遮罩效果 */
.highlight.collapsed:after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1rem;
/*background: linear-gradient(to bottom, transparent, #000);*/
}

/* 高亮容器的基础样式 */
.highlight {
position: relative;
border-radius: 12px;
overflow: hidden; /* 确保内容不会溢出 */
}

/* 展开/折叠按钮的样式 */
.expand-code {
display: inline-block;
cursor: pointer;
background-color: rgba(0, 0, 0, 0);
border-style: none;
border-radius: 5px;
font-size: 13px;
font-weight: 700;
line-height: 20px;
padding: 2px 6px;
position: absolute;
right: 2.5rem;
top: 0.7rem;
width: 1.5rem;
height: 1.5rem;
transition: transform 0.3s ease-in-out; /* 旋转动画 */
}

.highlight:hover .expand-code{
display: block;
}
.expand-code.rotated {
transform: rotate(180deg); /* 旋转 180 度 */
}

代码块复制

先在js目录下载cdn.jsdelivr.net/npm/clipboard@latest/dist/clipboard.js

然后创建codeCopy.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
/*页面载入完成后,创建复制按钮*/
!function (e, t, a) {
var initCopyCode = function() {
var clipboard = new ClipboardJS('.btn-copy', {
target: function(trigger) {
return trigger.nextElementSibling;
}
});

clipboard.on('success', function(e) {
e.clearSelection();

// 在复制成功后,添加旋转效果
var button = e.trigger;

button.classList.add('rotated');
setTimeout(function() {
button.classList.remove('rotated');
}, 600);
});

clipboard.on('error', function(e) {
console.error('复制失败:', e.action);
});
}

initCopyCode();
}(window, document);

在article.ejs里面写入

1
2
3
<!-- 复制按钮 -->
<script type="text/javascript" src="../../../../js/clipboard.js"></script>
<script type="text/javascript" src="../../../../js/codeCopy.js"></script>

image-20250318201523845

创建copy.styl

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
//代码块复制
.highlight{
position: relative;
border-radius: 12px;
}

.btn-copy {
display: inline-block;
cursor: pointer;
background-color: rgba(0, 0, 0, 0.0);
border-style: none;
border-radius: 5px;
font-size: 13px;
font-weight: 700;
line-height: 20px;
padding: 2px 6px;
position: absolute;
right: 0.5rem;
top: 0.7rem;
width: 1.5rem;
height: 1.5rem;
transition: transform 0.3s ease; /* 添加平滑的旋转过渡 */
}


.highlight:hover .btn-copy{
display: block;
}

.btn-copy.rotated {
transform: rotate(180deg); /* 旋转 180 度 */
}

文章更新时间

在article.ejs里面写入

1
2
3
4
5
6
7
8
9
10
11
<!-- 更新时间 -->
<% if ( item.updated > item.date ){ %>
<% if ( theme.postview == 'cover' ){ %>
<div class="updated" style="margin-top: 0px">
<% } else { %>
<div class="updated" >
<% } %>
<div></div>
<span>该文章更新于 <%- item.updated.locale("zh-cn").format("YYYY.MM.DD") %></span>
</div>
<% } %>

image-20250318210514336

update.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 更新时间 */
.updated
width: 100%
height: 50px
background-color: #FFF7DE
border-radius: 10px
line-height: 50px
margin-top: 30px

.updated div
width: 10px
height: 50px
background-color: #F9B907
border-radius: 10px 0px 0px 10px
float: left
margin-right: 20px

.updated span
font-size: 15px
color: #A57900
font-weight: bold

目录

在article.ejs里面写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="article-content">
<div class="entry">
<% if (item.excerpt && index){ %>
<%- item.excerpt %>
<% if (theme.read_more_btn){ %>
<p><a class="excerpt-btn" href="<%- config.root %><%- item.path %>"><span><%- __('read_more') %></span></a></p>
<% } %>
<% } else { %>
<%- partial('toc', {post: item}) %>
<%- item.content %>
<% } %>
</div>

</div>

image-20250318210810405

创建toc.styl

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
.toc-article
visibility hidden
position fixed
background: #eee;
margin: -30rem 2rem 0rem 90rem
padding: 1em;
-webkit-border-radius: 5px;
border-radius: 5px;
line-height: 1.5em;
font-size: 0.8em;
float: right;
max-width:30%;
min-width: 200px;
border: 1px solid #C0C0C0;
white-space: 8;
overflow: hidden;
strong
padding: 0.3em 0;

ol
li
margin: 0 0 0 0.2em;
list-style-type: none;
padding-inline-start: 0.5rem;

ol li:before
display: none;

/*下级目录的缩进量 */
.toc-child
margin-left: 0.8em;

最后的style.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@import "base/normalize.css"
@import "components/layout"
@import "components/header"
@import "components/article"
@import "components/archive"
@import "components/tags"
@import "components/categories"
@import "components/pagination"
@import "components/footer"
@import "components/icon"
@import "components/syntax"
@import "components/responsive"
@import "components/toc"
@import "components/update"
@import "components/mac"
@import "components/fold"
@import "components/copy"

结尾

还有一些小的改动,不太方便一处一处说,自己慢慢调css就行了。