安装

在 Hexo 根目录里

1
git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git themes/butterfly

应用主题

修改 Hexo 根目录下的 ,把主题改为 _config.ymlbutterfly

1
theme: butterfly

安装插件

如果没有 pug 以及 stylus 的渲染器,下载安装:

1
npm install hexo-renderer-pug hexo-renderer-stylus --save

主题配置

主题配置文件themes/butterfly/_config.yml

导航目录

设置顶部右上方区域的内容

1
2
3
4
5
6
7
8
9
10
# Menu 目录
menu:
首页: / || fas fa-home
归档: /archives/ || fas fa-archive
标签: /tags/ || fas fa-tags
分类: /categories/ || fas fa-folder-open
友链: /link/ || fas fa-link
关于: /about/ || fas fa-heart
# 格式如下:
# [标题]: [路径] || [图标]

这里的目录中,tagscategorieslinkabout目录默认不存在,需要在Bash窗口中使用命令生成对应页面

1
2
3
4
hexo new page tags
hexo new page categories
hexo new page link
hexo new page about

把创建的页面设置为对应的页面类型

代码高度限制

可配置代码高度限制,超出的部分会隐藏,并显示展开按钮。

1
highlight_height_limit: false # unit: px

注意:
单位是px,直接添加数字,如 200
实际限制高度为highlight_height_limit + 30 px ,多增加 30px 限制,目的是避免代码高度只超出highlight_height_limit 一点时,出现展开按钮,展开没内容。
不适用于隐藏后的代码块( css 设置 display: none

页脚自定义文本

custom_text是一个用来在页脚自定义文本的选项。通常可以在这里写声明文本等。支持 HTML。

修改主题配置文件_config.butterfly.yml

页脚如下,若在配置时没有出现github徽章,请参考教程添加Github徽标.

1
custom_text: I wish you to become your own sun, no need to rely on who's light.<p><a target="_blank" href="https://hexo.io/"><img src="https://img.shields.io/badge/Frame-Hexo-blue?style=flat&logo=hexo" title="博客框架为Hexo"></a>&nbsp;<a target="_blank" href="https://butterfly.js.org/"><img src="https://img.shields.io/badge/Theme-Butterfly-6513df?style=flat&logo=bitdefender" title="主题采用butterfly"></a>&nbsp;<a target="_blank" href="https://www.jsdelivr.com/"><img src="https://img.shields.io/badge/CDN-jsDelivr-orange?style=flat&logo=jsDelivr" title="本站使用JsDelivr为静态资源提供CDN加速"></a> &nbsp;<a target="_blank" href="https://vercel.com/ "><img src="https://img.shields.io/badge/Hosted-Vervel-brightgreen?style=flat&logo=Vercel" title="本站采用双线部署,默认线路托管于Vercel"></a>&nbsp;<a target="_blank" href="https://vercel.com/ "><img src="https://img.shields.io/badge/Hosted-Coding-0cedbe?style=flat&logo=Codio" title="本站采用双线部署,联通线路托管于Coding"></a>&nbsp;<a target="_blank" href="https://github.com/"><img src="https://img.shields.io/badge/Source-Github-d021d6?style=flat&logo=GitHub" title="本站项目由Gtihub托管"></a>&nbsp;<a target="_blank" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img src="https://img.shields.io/badge/Copyright-BY--NC--SA%204.0-d42328?style=flat&logo=Claris" title="本站采用知识共享署名-非商业性使用-相同方式共享4.0国际许可协议进行许可"></a></p>

对于部分人需要写 ICP 的,也可以写在custom_text里。

1
custom_text: <a href="icp链接"><img class="icp-icon" src="icp图片"><span>备案号:xxxxxx</span></a>

打字效果

修改主题配置文件_config.butterfly.yml

1
2
3
4
5
6
7
# Typewriter Effect (打字效果)
# https://github.com/disjukr/activate-power-mode
activate_power_mode:
enable: false
colorful: true # open particle animation (冒光特效)
shake: true # open shake (抖動特效)
mobile: false

页面加载动画preloader

当进入网页时,因为加载速度的问题,可能会导致top_img图片出现断层显示,或者网页加载不全而出现等待时间,开启preloader后,会显示加载动画,等页面加载完,加载动画会消失。

1
2
# 加载动画 Loading Animation
preloader: true

启用本地搜索

1
2
3
4
5
search:
# Choose: algolia_search / local_search / docsearch
# leave it empty if you don't need search
use: local_search
placeholder:

文章 meta 显示

post_meta 这个属性用于显示文章的相关信息的。

修改主题配置文件_config.butterfly.yml.

1
2
3
4
5
6
7
8
9
10
11
12
13
post_meta:
page:
date_type: both # created or updated or both 主页文章日期是创建日或者更新日或都显示
date_format: relative # date/relative 显示日期还是相对日期
categories: true # true or false 主页是否显示分类
tags: true # true or false 主页是否显示标签
label: true # true or false 显示描述性文字
post:
date_type: both # created or updated or both 文章页日期是创建日或者更新日或都显示
date_format: relative # date/relative 显示日期还是相对日期
categories: true # true or false 文章页是否显示分类
tags: true # true or false 文章页是否显示标签
label: true # true or false 显示描述性文字

文章版权和协议许可

修改主题配置文件_config.butterfly.yml

1
2
3
4
5
6
post_copyright:
enable: true
decode: false
author_href:
license: CC BY-NC-SA 4.0
license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/

由于 Hexo 4.1 开始,默认对网址进行解码,以至于如果是中文网址,会被解码,可设置 decode: true 来显示中文网址。

如果有文章(例如:转载文章)不需要显示版权,可以在文章页 Front-matter中单独设置。

1
copyright: false

文章打赏

修改主题配置文件_config.butterfly.yml

1
2
3
4
5
6
7
8
9
reward:
enable: true
QR_code:
- img: /img/wechat.jpg
link:
text: wechat
- img: /img/alipay.jpg
link:
text: alipay

阅读模式

阅读模式下会去掉除文章外的内容,避免干扰阅读。只会出现在文章页面,右下角会有阅读模式按钮。

修改主题配置文件_config.butterfly.yml

1
readmode: true

池塘养鱼

在主题配置文件 _config.butterfly.yml 加入以下代码:

1
2
3
4
5
6
inject:
head:

bottom:
- <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
- <script src="https://cdn.jsdelivr.net/gh/xiabo2/CDN@latest/fishes.js"></script>

有时候会遇到cdn解析失败,导致鱼塘加载不出来,可以js文件格式本地引入

在 source 目录下新建一个 styles 文件夹,用于存放相关文件
添加如下两个文件

文件一:fish-base.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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
var RENDERER = {
POINT_INTERVAL : 5,
FISH_COUNT : 3,
MAX_INTERVAL_COUNT : 50,
INIT_HEIGHT_RATE : 0.5,
THRESHOLD : 50,

init : function(){
this.setParameters();
this.reconstructMethods();
this.setup();
this.bindEvent();
this.render();
},
setParameters : function(){
this.$window = $(window);
this.$container = $('#jsi-flying-fish-container');
this.$canvas = $('<canvas />');
this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d');
this.points = [];
this.fishes = [];
this.watchIds = [];
},
createSurfacePoints : function(){
var count = Math.round(this.width / this.POINT_INTERVAL);
this.pointInterval = this.width / (count - 1);
this.points.push(new SURFACE_POINT(this, 0));

for(var i = 1; i < count; i++){
var point = new SURFACE_POINT(this, i * this.pointInterval),
previous = this.points[i - 1];

point.setPreviousPoint(previous);
previous.setNextPoint(point);
this.points.push(point);
}
},
reconstructMethods : function(){
this.watchWindowSize = this.watchWindowSize.bind(this);
this.jdugeToStopResize = this.jdugeToStopResize.bind(this);
this.startEpicenter = this.startEpicenter.bind(this);
this.moveEpicenter = this.moveEpicenter.bind(this);
this.reverseVertical = this.reverseVertical.bind(this);
this.render = this.render.bind(this);
},
setup : function(){
this.points.length = 0;
this.fishes.length = 0;
this.watchIds.length = 0;
this.intervalCount = this.MAX_INTERVAL_COUNT;
this.width = this.$container.width();
this.height = this.$container.height();
this.fishCount = this.FISH_COUNT * this.width / 500 * this.height / 500;
this.$canvas.attr({width : this.width, height : this.height});
this.reverse = false;

this.fishes.push(new FISH(this));
this.createSurfacePoints();
},
watchWindowSize : function(){
this.clearTimer();
this.tmpWidth = this.$window.width();
this.tmpHeight = this.$window.height();
this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL));
},
clearTimer : function(){
while(this.watchIds.length > 0){
clearTimeout(this.watchIds.pop());
}
},
jdugeToStopResize : function(){
var width = this.$window.width(),
height = this.$window.height(),
stopped = (width == this.tmpWidth && height == this.tmpHeight);

this.tmpWidth = width;
this.tmpHeight = height;

if(stopped){
this.setup();
}
},
bindEvent : function(){
this.$window.on('resize', this.watchWindowSize);
this.$container.on('mouseenter', this.startEpicenter);
this.$container.on('mousemove', this.moveEpicenter);
this.$container.on('click', this.reverseVertical);
},
getAxis : function(event){
var offset = this.$container.offset();

return {
x : event.clientX - offset.left + this.$window.scrollLeft(),
y : event.clientY - offset.top + this.$window.scrollTop()
};
},
startEpicenter : function(event){
this.axis = this.getAxis(event);
},
moveEpicenter : function(event){
var axis = this.getAxis(event);

if(!this.axis){
this.axis = axis;
}
this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y);
this.axis = axis;
},
generateEpicenter : function(x, y, velocity){
if(y < this.height / 2 - this.THRESHOLD || y > this.height / 2 + this.THRESHOLD){
return;
}
var index = Math.round(x / this.pointInterval);

if(index < 0 || index >= this.points.length){
return;
}
this.points[index].interfere(y, velocity);
},
reverseVertical : function(){
this.reverse = !this.reverse;

for(var i = 0, count = this.fishes.length; i < count; i++){
this.fishes[i].reverseVertical();
}
},
controlStatus : function(){
for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].updateSelf();
}
for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].updateNeighbors();
}
if(this.fishes.length < this.fishCount){
if(--this.intervalCount == 0){
this.intervalCount = this.MAX_INTERVAL_COUNT;
this.fishes.push(new FISH(this));
}
}
},
render : function(){
requestAnimationFrame(this.render);
this.controlStatus();
this.context.clearRect(0, 0, this.width, this.height);
this.context.fillStyle = 'hsl(0, 0%, 95%)';

for(var i = 0, count = this.fishes.length; i < count; i++){
this.fishes[i].render(this.context);
}
this.context.save();
this.context.globalCompositeOperation = 'xor';
this.context.beginPath();
this.context.moveTo(0, this.reverse ? 0 : this.height);

for(var i = 0, count = this.points.length; i < count; i++){
this.points[i].render(this.context);
}
this.context.lineTo(this.width, this.reverse ? 0 : this.height);
this.context.closePath();
this.context.fill();
this.context.restore();
}
};
var SURFACE_POINT = function(renderer, x){
this.renderer = renderer;
this.x = x;
this.init();
};
SURFACE_POINT.prototype = {
SPRING_CONSTANT : 0.03,
SPRING_FRICTION : 0.9,
WAVE_SPREAD : 0.3,
ACCELARATION_RATE : 0.01,

init : function(){
this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE;
this.height = this.initHeight;
this.fy = 0;
this.force = {previous : 0, next : 0};
},
setPreviousPoint : function(previous){
this.previous = previous;
},
setNextPoint : function(next){
this.next = next;
},
interfere : function(y, velocity){
this.fy = this.renderer.height * this.ACCELARATION_RATE * ((this.renderer.height - this.height - y) >= 0 ? -1 : 1) * Math.abs(velocity);
},
updateSelf : function(){
this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height);
this.fy *= this.SPRING_FRICTION;
this.height += this.fy;
},
updateNeighbors : function(){
if(this.previous){
this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height);
}
if(this.next){
this.force.next = this.WAVE_SPREAD * (this.height - this.next.height);
}
},
render : function(context){
if(this.previous){
this.previous.height += this.force.previous;
this.previous.fy += this.force.previous;
}
if(this.next){
this.next.height += this.force.next;
this.next.fy += this.force.next;
}
context.lineTo(this.x, this.renderer.height - this.height);
}
};
var FISH = function(renderer){
this.renderer = renderer;
this.init();
};
FISH.prototype = {
GRAVITY : 0.4,

init : function(){
this.direction = Math.random() < 0.5;
this.x = this.direction ? (this.renderer.width + this.renderer.THRESHOLD) : -this.renderer.THRESHOLD;
this.previousY = this.y;
this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1);

if(this.renderer.reverse){
this.y = this.getRandomValue(this.renderer.height * 1 / 10, this.renderer.height * 4 / 10);
this.vy = this.getRandomValue(2, 5);
this.ay = this.getRandomValue(0.05, 0.2);
}else{
this.y = this.getRandomValue(this.renderer.height * 6 / 10, this.renderer.height * 9 / 10);
this.vy = this.getRandomValue(-5, -2);
this.ay = this.getRandomValue(-0.2, -0.05);
}
this.isOut = false;
this.theta = 0;
this.phi = 0;
},
getRandomValue : function(min, max){
return min + (max - min) * Math.random();
},
reverseVertical : function(){
this.isOut = !this.isOut;
this.ay *= -1;
},
controlStatus : function(context){
this.previousY = this.y;
this.x += this.vx;
this.y += this.vy;
this.vy += this.ay;

if(this.renderer.reverse){
if(this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE){
this.vy -= this.GRAVITY;
this.isOut = true;
}else{
if(this.isOut){
this.ay = this.getRandomValue(0.05, 0.2);
}
this.isOut = false;
}
}else{
if(this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE){
this.vy += this.GRAVITY;
this.isOut = true;
}else{
if(this.isOut){
this.ay = this.getRandomValue(-0.2, -0.05);
}
this.isOut = false;
}
}
if(!this.isOut){
this.theta += Math.PI / 20;
this.theta %= Math.PI * 2;
this.phi += Math.PI / 30;
this.phi %= Math.PI * 2;
}
this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY);

if(this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD || this.vx < 0 && this.x < -this.renderer.THRESHOLD){
this.init();
}
},
render : function(context){
context.save();
context.translate(this.x, this.y);
context.rotate(Math.PI + Math.atan2(this.vy, this.vx));
context.scale(1, this.direction ? 1 : -1);
context.beginPath();
context.moveTo(-30, 0);
context.bezierCurveTo(-20, 15, 15, 10, 40, 0);
context.bezierCurveTo(15, -10, -20, -15, -30, 0);
context.fill();

context.save();
context.translate(40, 0);
context.scale(0.9 + 0.2 * Math.sin(this.theta), 1);
context.beginPath();
context.moveTo(0, 0);
context.quadraticCurveTo(5, 10, 20, 8);
context.quadraticCurveTo(12, 5, 10, 0);
context.quadraticCurveTo(12, -5, 20, -8);
context.quadraticCurveTo(5, -10, 0, 0);
context.fill();
context.restore();

context.save();
context.translate(-3, 0);
context.rotate((Math.PI / 3 + Math.PI / 10 * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1));

context.beginPath();

if(this.renderer.reverse){
context.moveTo(5, 0);
context.bezierCurveTo(10, 10, 10, 30, 0, 40);
context.bezierCurveTo(-12, 25, -8, 10, 0, 0);
}else{
context.moveTo(-5, 0);
context.bezierCurveTo(-10, -10, -10, -30, 0, -40);
context.bezierCurveTo(12, -25, 8, -10, 0, 0);
}
context.closePath();
context.fill();
context.restore();
context.restore();
this.controlStatus(context);
}
};
$(function(){
RENDERER.init();
});

文件二:fish.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fish();
function fish() {
return (
$("#footer-wrap").css({
position: "absolute",
"text-align": "center",
top: 0,
right: 0,
left: 0,
bottom: 0,
}),
$("footer").append(
'<div class="container" id="jsi-flying-fish-container"></div>'
),
$("body").append(
'<script src="/styles/fish-base.js"></script>'
),
this
);
}
在主题配置文件下引入这两个文件

代码如下:

1
- <script src="/styles/fish.js"></script>

一样也得引入 jquery:

1
- <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>

渐变色设置

在styles目录下新建一个 main.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* 鱼塘固定宽度 */
canvas:not(#ribbon-canvas), #web_bg {
margin-bottom: -0.5rem;
display: block;
width: 100%;
height: 160px
}

/* 鱼塘渐变色 */
body{
overflow-x: hidden;
}
#footer {
height:160px;
background: linear-gradient(
135deg,
rgba(23, 23, 116, 0.2),
rgba(28, 181, 224, 0.2)
);
}
#footer #footer-wrap,
#footer-wrap a {
color: #000;
}
#footer #footer-wrap {
z-index: 1;
}

#footer .container {
line-height: 0;
position: relative;
height: 100%;
width: 100vw;
}

/* 侧边栏个人信息卡片动态渐变色 */
#aside-content>.card-widget.card-info{
background: linear-gradient(-45deg, rgba(255, 225, 245, 0.6),
rgba(255, 225, 225, 0.9),
rgba(225, 255, 251, 0.9),
rgba(255, 225, 255, 0.6));
}
[data-theme=dark] #aside-content>.card-widget.card-info {
background: linear-gradient(-45deg, rgb(225, 238, 255),
rgba(225, 255, 253, 0.9),
rgba(225, 243, 255, 0.9),
rgba(255, 225, 253, 0.6));
}
/* 侧边栏公告栏卡片渐变色 */
#aside-content>.card-widget.card-announcement{
background: linear-gradient(-45deg, rgba(255, 225, 245, 0.6),
rgba(255, 225, 225, 0.9),
rgba(225, 255, 251, 0.9),
rgba(255, 225, 255, 0.6));
}
[data-theme=dark] #aside-content>.card-widget.card-announcement{
background: linear-gradient(-45deg, rgba(255, 225, 245, 0.6),
rgba(255, 225, 225, 0.9),
rgba(225, 255, 251, 0.9),
rgba(255, 225, 255, 0.6));
}
/* 侧边栏目录最新文章卡片渐变色 */
#aside-content>.sticky_layout>.card-widget{
background: linear-gradient(-45deg, rgba(255, 225, 245, 0.6),
rgba(255, 225, 225, 0.9),
rgba(225, 255, 251, 0.9),
rgba(255, 225, 255, 0.6));
}
[data-theme=dark] #aside-content>.sticky_layout>.card-widget{
background: linear-gradient(-45deg, rgba(255, 225, 245, 0.6),
rgba(255, 225, 225, 0.9),
rgba(225, 255, 251, 0.9),
rgba(255, 225, 255, 0.6));
}
/* 个人信息Follow me按钮 */
#aside-content>.card-widget.card-info>#card-info-btn {
background-color: #3eb8be;
border-radius: 8px;
}
/*文章页面*/
/* .layout>#post {
background: linear-gradient(-45deg, rgba(225, 255, 252, 0.8),
rgba(255, 225, 229, 0.9),
rgba(225, 255, 225, 0.9),
rgba(253, 255, 225, 0.8));
}
[data-theme=dark] .layout>#post {
background: linear-gradient(-45deg, rgba(24,40,72,.2),
rgba(35,37,58,.9),
rgba(35,37,58,.9),
rgba(24,40,72,.2));
} */
/*主页文章预览页面*/
#recent-posts>.recent-post-item{
background: linear-gradient(-45deg, rgba(225, 255, 252, 0.8),
rgba(255, 225, 229, 0.9),
rgba(225, 255, 225, 0.9),
rgba(253, 255, 225, 0.8));
}
[data-theme=dark] #recent-posts>.recent-post-item{
background: linear-gradient(-45deg, rgba(24,40,72,.2),
rgba(35,37,58,.9),
rgba(35,37,58,.9),
rgba(24,40,72,.2));
}

/*分类页面*/
.layout>#page {
background: linear-gradient(-45deg, rgba(225,235,255,.8),
rgba(225,235,255,.9),
rgba(225,235,255,.9),
rgba(225,235,255,.8));
}
[data-theme=dark] .layout>#page {
background: linear-gradient(-45deg, rgba(24,40,72,.2),
rgba(35,37,58,.9),
rgba(35,37,58,.9),
rgba(24,40,72,.2));
}

/*时间轴页面*/
.layout>#archive {
background: linear-gradient(-45deg, rgba(225,235,255,.8),
rgba(225,235,255,.9),
rgba(225,235,255,.9),
rgba(225,235,255,.8));
}
[data-theme=dark] .layout>#archive {
background: linear-gradient(-45deg, rgba(24,40,72,.2),
rgba(35,37,58,.9),
rgba(35,37,58,.9),
rgba(24,40,72,.2));
}

这里只修改了鱼塘和部分个人主页的渐变色,也可以自己调成你喜欢的渐变色

同样,也需要在主题配置文件中引入这个css文件:

1
- <link rel="stylesheet" href="/styles/main.css">