右边按钮阅读进度(Leonus)

点击查看教程

实现原理其实很简单,我们只需要使用 被顶部卷去的高度 / (页面总高度 - 可视高度) ,既可算出百分比。
之所以减去可视高度,是因为当我们在滑到最底部的时候,可以看出 页面高度 = 被卷去的高度 + 可视高度

  1. 修改文件[BlogRoot]\themes\butterfly\layout\includes\rightside.pug,在最下面插入如下两行代码(注意去掉前面的+号,别傻呼呼的直接复制粘贴)
1
2
3
4
button#go-up(type="button" title=_p("rightside.back_to_top"))
i.fas.fa-arrow-up
+ span#percent 0
+ span %
  1. 新建文件[BlogRoot]\source\js\readPercent.js,在自定义js文件中加入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
window.onscroll = percent;// 执行函数
// 页面百分比
function percent() {
let a = document.documentElement.scrollTop || window.pageYOffset, // 卷去高度
b = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight) - document.documentElement.clientHeight, // 整个网页高度
result = Math.round(a / b * 100), // 计算百分比
up = document.querySelector("#go-up") // 获取按钮

if (result <= 95) {
up.childNodes[0].style.display = 'none'
up.childNodes[1].style.display = 'block'
up.childNodes[1].innerHTML = result;
} else {
up.childNodes[1].style.display = 'none'
up.childNodes[0].style.display = 'block'
}
}
  1. 创建css文件[BlogRoot]\source\css\readPercent.css写入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* 返回顶部 */

button#go-up #percent {
display: none;
font-weight: bold;
font-size: 15px !important;
}

button#go-up span {
font-size: 12px!important;
margin-right: -1px;
}

/* 鼠标滑动到按钮上时显示返回顶部图标 */
button#go-up:hover i {
display: block !important;
}

button#go-up:hover #percent {
display: none !important;
}
  1. 引入js文件与css文件
1
2
3
4
5
inject:
head:
+ - <link rel="stylesheet" href="/css/readPercent.css">
bottom:
+ - <script defer data-pjax src="/js/readPercent.js"></script>
  1. 重启项目即可看到效果
1
hexo cl;hexo s

评论表情包放大(Leonus)

点击查看教程

其实实现的原理很简单,就是创建一个盒子,将表情包的内容放在盒子里面,然后控制盒子位置和显示隐藏即可。

  1. 新建文件[BlogRoot]\source\js\emoji.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
// 如果当前页有评论就执行函数
if (document.getElementById('post-comment')) owoBig();
// 表情放大
function owoBig() {
let flag = 1, // 设置节流阀
owo_time = '', // 设置计时器
m = 3; // 设置放大倍数
// 创建盒子
let div = document.createElement('div'),
body = document.querySelector('body');
// 设置ID
div.id = 'owo-big';
// 插入盒子
body.appendChild(div)

// 构造observer
let observer = new MutationObserver(mutations => {

for (let i = 0; i < mutations.length; i++) {
let dom = mutations[i].addedNodes,
owo_body = '';
if (dom.length == 2 && dom[1].className == 'OwO-body') owo_body = dom[1];
// 如果需要在评论内容中启用此功能请解除下面的注释
// else if (dom.length == 1 && dom[0].className == 'tk-comment') owo_body = dom[0];
else continue;

// 禁用右键(手机端长按会出现右键菜单,为了体验给禁用掉)
if (document.body.clientWidth <= 768) owo_body.addEventListener('contextmenu', e => e.preventDefault());
// 鼠标移入
owo_body.onmouseover = (e) => {
if (flag && e.target.tagName == 'IMG') {
flag = 0;
// 移入300毫秒后显示盒子
owo_time = setTimeout(() => {
let height = e.path[0].clientHeight * m, // 盒子高
width = e.path[0].clientWidth * m, // 盒子宽
left = (e.x - e.offsetX) - (width - e.path[0].clientWidth) / 2, // 盒子与屏幕左边距离
top = e.y - e.offsetY; // 盒子与屏幕顶部距离

if ((left + width) > body.clientWidth) left -= ((left + width) - body.clientWidth + 10); // 右边缘检测,防止超出屏幕
if (left < 0) left = 10; // 左边缘检测,防止超出屏幕
// 设置盒子样式
div.style.cssText = `display:flex; height:${height}px; width:${width}px; left:${left}px; top:${top}px;`;
// 在盒子中插入图片
div.innerHTML = `<img src="${e.target.src}">`
}, 300);
}
};
// 鼠标移出隐藏盒子
owo_body.onmouseout = () => { div.style.display = 'none', flag = 1, clearTimeout(owo_time); }
}

})
observer.observe(document.getElementById('post-comment'), { subtree: true, childList: true }) // 监听的 元素 和 配置项
}
  1. 新建文件[BlogRoot]\source\js\emoji.css,写入如下内容(更推荐全部写在custom.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
#owo-big {
position: fixed;
align-items: center;
background-color: rgb(255, 255, 255);
border: 1px #aaa solid;
border-radius: 10px;
z-index: 9999;
display: none;
transform: translate(0, -105%);
overflow: hidden;
animation: owoIn 0.3s cubic-bezier(0.42, 0, 0.3, 1.11);
}

[data-theme=dark] #owo-big {
background-color: #4a4a4a
}

#owo-big img {
width: 100%;
}

/* 动画效果代码由 Heo:https://blog.zhheo.com/ 提供 */
@keyframes owoIn {
0% {
transform: translate(0, -95%);
opacity: 0;
}
100% {
transform: translate(0, -105%);
opacity: 1;
}
}

评论输入提醒(Leonus)

点击查看教程

实现很简单,纯css实现,在custom.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
/* 设置文字内容 :nth-child(1)的作用是选择第几个 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(1):before {
content: '输入QQ号会自动获取昵称和头像🐧';
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(2):before {
content: '收到回复将会发送到您的邮箱📧';
}

.el-input.el-input--small.el-input-group.el-input-group--prepend:nth-child(3):before {
content: '可以通过昵称访问您的网站🔗';
}

/* 当用户点击输入框时显示 */
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::before,
.el-input.el-input--small.el-input-group.el-input-group--prepend:focus-within::after {
display: block;
}

/* 主内容区 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::before {
/* 先隐藏起来 */
display: none;
/* 绝对定位 */
position: absolute;
/* 向上移动60像素 */
top: -60px;
/* 文字强制不换行,防止left:50%导致的文字换行 */
white-space: nowrap;
/* 圆角 */
border-radius: 10px;
/* 距离左边50% */
left: 50%;
/* 然后再向左边挪动自身的一半,即可实现居中 */
transform: translate(-50%);
/* 填充 */
padding: 14px 18px;
background: #444;
color: #fff;
}

/* 小角标 */
.el-input.el-input--small.el-input-group.el-input-group--prepend::after {
display: none;
content: '';
position: absolute;
/* 内容大小(宽高)为0且边框大小不为0的情况下,每一条边(4个边)都是一个三角形,组成一个正方形。
我们先将所有边框透明,再给其中的一条边添加颜色就可以实现小三角图标 */
border: 12px solid transparent;
border-top-color: #444;
left: 50%;
transform: translate(-50%, -48px);
}

快速添加友链

点击查看教程
  1. 新建文件’[BlogRoot]\source\js\kslink.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
var leonus = {
linkCom: e => {
var t = document.querySelector(".el-textarea__inner");
"bf" == e ? (t.value = "```yml\n", t.value += "- name: \n link: \n avatar: \n descr: \n siteshot: ", t.value += "\n```", t.setSelectionRange(15, 15)) : (t.value = "站点名称:\n站点地址:\n头像链接:\n站点描述:\n站点截图:", t.setSelectionRange(5, 5)), t.focus()
},
owoBig: () => {
if (!document.getElementById("post-comment") || document.body.clientWidth < 768) return;
let e = 1,
t = "",
o = document.createElement("div"),
n = document.querySelector("body");
o.id = "owo-big", n.appendChild(o), new MutationObserver((l => {
for (let a = 0; a < l.length; a++) {
let i = l[a].addedNodes,
s = "";
if (2 == i.length && "OwO-body" == i[1].className) s = i[1];
else {
if (1 != i.length || "tk-comment" != i[0].className) continue;
s = i[0]
}
s.onmouseover = l => {
e && ("OwO-body" == s.className && "IMG" == l.target.tagName || "tk-owo-emotion" == l.target.className) && (e = 0, t = setTimeout((() => {
let e = 3 * l.path[0].clientHeight,
t = 3 * l.path[0].clientWidth,
a = l.x - l.offsetX - (t - l.path[0].clientWidth) / 2,
i = l.y - l.offsetY;
a + t > n.clientWidth && (a -= a + t - n.clientWidth + 10), a < 0 && (a = 10), o.style.cssText = `display:flex; height:${e}px; width:${t}px; left:${a}px; top:${i}px;`, o.innerHTML = `<img src="${l.target.src}">`
}), 300))
}, s.onmouseout = () => {
o.style.display = "none", e = 1, clearTimeout(t)
}
}
})).observe(document.getElementById("post-comment"), {
subtree: !0,
childList: !0
})
},
};
  1. 新建文件[BlogRoot]\source\css\kslink.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
/* 添加友链按钮 */
/* 快速填写格式 */
.addBtn {
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.addBtn button {
transition: .2s;
display: flex;
margin: 5px auto;
color: var(--global-bg);
padding: 15px;
border-radius: 40px;
background: var(--search-result-title);
align-items: center;
}

button {
padding: 0;
outline: 0;
border: none;
background: 0 0;
cursor: pointer;
touch-action: manipulation;
}
.fa-solid, .fas {
font-family: "Font Awesome 6 Free";
font-weight: 900;
}
.addBtn i {
font-size: 1.3rem;
margin-right: 10px;
}
.addBtn button:hover {
background: var(--theme-color);
color: #fff;
}
  1. 为了节省DOM规模,仅在你的友链页面(例如[BlogRoot]\source\link\index.md)对应的md文件最后,引入以上两个文件以及定义按钮元素:
1
2
3
<div class="addBtn"><button onclick="leonus.linkCom()"><i class="fa-solid fa-circle-plus"></i>快速申请 (默认样式)</button> <button onclick="leonus.linkCom(&quot;bf&quot;)"><i class="fa-solid fa-circle-plus"></i>快速申请 (Butterfly)</button></div>
<link rel="stylesheet" href="/css/kslink.css">
<script src="/js/kslink.js"></script>
  1. 重启项目并进入友链页面底部,看看效果吧:
1
hexo cl; hexo s

Vue+Element样式弹窗

点击查看教程
  1. 在主题配置文件[BlogRoot]\_config.butterfly.yml中 引入VueElement相关依赖:
1
2
3
4
5
6
inject:
head:
+ - <link rel="stylesheet" href="https://cdn1.tianli0.top/npm/element-ui@2.15.6/packages/theme-chalk/lib/index.css"> # 引入组件库(f12)
bottom:
+ - <script async src="https://cdn1.tianli0.top/npm/vue@2.6.14/dist/vue.min.js"></script> # 引入VUE(f12)
+ - <script async src="https://cdn1.tianli0.top/npm/element-ui@2.15.6/lib/index.js"></script> # 引入ElementUI(f12)
  1. 在你想要弹出弹窗的js代码中加入如下代码即可触发弹窗:
1
2
3
4
5
6
7
8
9
10
11
12
13
new Vue({
data: function () {
this.$notify({
title: "你已被发现😜",
message: "小伙子,扒源记住要遵循GPL协议!",
position: 'top-left',
offset: 50,
showClose: true,
type: "warning",
duration: 5000
});
}
})
  • notify:弹窗类型,可以替换为message(信息提示)和confirm(二次确认提示)

  • title:弹窗标题,可以改为自定义标题

  • message:弹窗信息,可以改为自定义内容

  • position:弹出位置,bottom、top和left、right两两组合

  • offset:偏移量,简单可以理解为与边界的距离

  • showClose:是否显示关闭按钮

  • type:提示类型,可选success/warning/info/error等

  • duration:停留时间,弹出停留至消失的时间,单位ms

  • 详见:Vue中常用的提示信息

按键防抖

点击查看教程

我们的博客按钮非常多,可能你平时并不在意,如果你为f12按钮绑定了一个弹窗,那当你长按f12可能会无限触发弹窗,这就是没有做按键防抖的后果,我们的是静态博客,所以只消耗本地的资源,如果是有后端服务的,频繁触发某个逻辑可能会消耗服务器资源,因此我们必须在前端过滤这些非法操作,不能让其跑到后端!我这里用的是最初等的计时器对象来防抖,原理就是延迟某个时间再触发逻辑(例如300ms),如果这段时间内按键再次按下直接忽略。

  1. 在任意一个js文件引入以下代码,这个js文件的引入最好在前面,否则可能不能及时生效
1
2
3
4
5
6
7
// 防抖全局计时器
let TT = null; //time用来控制事件的触发
// 防抖函数:fn->逻辑 time->防抖时间
function debounce(fn, time) {
if (TT !== null) clearTimeout(TT);
TT = setTimeout(fn, time);
}
  1. 将你需要防抖的函数fn()debounce(fn, 300)替代,防抖时间可以自定义,单位为ms,下面就是一个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 复制提醒
document.addEventListener("copy", function () {
debounce(function () {
new Vue({
data: function () {
this.$notify({
title: "哎嘿!复制成功🍬",
message: "若要转载最好保留原文链接哦,给你一个大大的赞!",
position: 'top-left',
offset: 50,
showClose: true,
type: "success",
duration: 5000
});
}
})
}, 300);
})
  1. 在主题配置文件引入以上js文件,具体过程略。这样写全局都是用一个计时器,考虑到我们的博客较为简单,这样做实测是没啥问题了,再也不怕手抖了!

分享链接按钮

点击查看教程

注意:分享按钮提醒依赖Vue弹窗和防抖计时器,请完成前面的前置教程!

  1. 引入ClipboardJS依赖:在主题配置文件[BlogRoot]\_config.butterfly.yml
1
2
3
inject:
bottom:
+ - <script src="https://cdn.bootcdn.net/ajax/libs/clipboard.js/2.0.11/clipboard.min.js"></script>
  1. 添加分享按钮,在[BlogRoot]\themes\butterfly\layout\includes\rightside.pug做以下修改:
1
2
3
4
5
6
7
8
9
+      when 'share'
+ button.share(type="button" title='分享链接' onclick="share()")
+ i.fas.fa-share-nodes

#rightside
- const { enable, hide, show } = theme.rightside_item_order
- const hideArray = enable ? hide && hide.split(',') : ['readmode','translate','darkmode']
- - const showArray = enable ? show && show.split(',') : ['chat','comment','hideAside','toc']
+ - const showArray = enable ? show && show.split(',') : ['chat','share','comment','hideAside','toc']
  1. 新建文件[BlogRoot]\source\js\share.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
// 分享本页
function share_() {
let url = window.location.origin + window.location.pathname
try {
// 截取标题
var title = document.title;
var subTitle = title.endsWith("| Fomalhaut🥝") ? title.substring(0, title.length - 14) : title;
navigator.clipboard.writeText('Fomalhaut🥝的站内分享\n标题:' + subTitle + '\n链接:' + url + '\n欢迎来访!🍭🍭🍭');
new Vue({
data: function () {
this.$notify({
title: "成功复制分享信息🎉",
message: "您现在可以通过粘贴直接跟小伙伴分享了!",
position: 'top-left',
offset: 50,
showClose: true,
type: "success",
duration: 5000
});
// return { visible: false }
}
})
} catch (err) {
console.error('复制失败!', err);
}
// new ClipboardJS(".share", { text: function () { return '标题:' + document.title + '\n链接:' + url } });
// btf.snackbarShow("本页链接已复制到剪切板,快去分享吧~")
}

// 防抖
function share() {
debounce(share_, 300);
}
这里我的页面名字截取了后面的重复段,你要截取的话就将'| MGHY'换成'| 你的网站名字',或者保留也行,就是直接复制!
  1. 在主题配置文件’[BlogRoot]_config.butterfly.yml’中引入该js文件
1
2
3
inject:
bottom:
+ - <script src="/js/share.js"></script>
  1. 清理并重启项目即可看到变更
1
hexo cl;hexo s

直达底部按钮

点击查看教程

在’[BlogRoot]\themes\butterfly\layout\includes\rightside.pug’做以下修改:

1
2
3
4
5
button#go-up(type="button" title=_p("rightside.back_to_top"))
i.fas.fa-arrow-up

+button#go-down(type="button" title="直达底部" onclick="btf.scrollToDest(document.body.scrollHeight, 500)")
+ i.fas.fa-arrow-down

节日弹窗与公祭日变灰

点击查看教程

本质就是用的sessionStorage这个本地存储对象去确定是不是同一个会话,如果是同一个的话,就弹窗一次,如果下次进来了就不是同一个会话了,又会弹窗一次

  1. 新建[BlogRoot]\source\js\day.js,并写入以下代码,这里公祭日灰度我设置的为60%:
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
var d = new Date();
m = d.getMonth() + 1;
dd = d.getDate();
y = d.getFullYear();

// 公祭日
if (m == 9 && dd == 18) {
document.getElementsByTagName("html")[0].setAttribute("style", "filter: grayscale(60%);");
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("今天是九一八事变" + (y - 1931).toString() + "周年纪念日\n🪔勿忘国耻,振兴中华🪔");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 7 && dd == 7) {
document.getElementsByTagName("html")[0].setAttribute("style", "filter: grayscale(60%);");
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("今天是卢沟桥事变" + (y - 1937).toString() + "周年纪念日\n🪔勿忘国耻,振兴中华🪔");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 12 && dd == 13) {
document.getElementsByTagName("html")[0].setAttribute("style", "filter: grayscale(60%);");
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("今天是南京大屠杀" + (y - 1937).toString() + "周年纪念日\n🪔勿忘国耻,振兴中华🪔");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 8 && dd == 14) {
document.getElementsByTagName("html")[0].setAttribute("style", "filter: grayscale(60%);");
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("今天是世界慰安妇纪念日\n🪔勿忘国耻,振兴中华🪔");
sessionStorage.setItem("isPopupWindow", "1");
}
}


// 节假日
if (m == 10 && dd <= 3) {//国庆节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("祝祖国" + (y - 1949).toString() + "岁生日快乐!");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 8 && dd == 15) {//搞来玩的,小日子投降
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("小日子已经投降" + (y - 1945).toString() + "年了😃");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 1 && dd == 1) {//元旦节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire(y.toString() + "年元旦快乐!🎉");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 3 && dd == 8) {//妇女节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("各位女神们,妇女节快乐!👩");
sessionStorage.setItem("isPopupWindow", "1");
}
}
l = ["非常抱歉,因为不可控原因,博客将于明天停止运营!", "好消息,日本没了!", "美国垮了,原因竟然是川普!", "微软垮了!", "你的电脑已经过载,建议立即关机!", "你知道吗?站长很喜欢你哦!", "一分钟有61秒哦", "你喜欢的人跟别人跑了!"]
if (m == 4 && dd == 1) {//愚人节,随机谎话
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire(l[Math.floor(Math.random() * l.length)]);
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 5 && dd == 1) {//劳动节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("劳动节快乐\n为各行各业辛勤工作的人们致敬!");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 5 && dd == 4) {//青年节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("青年节快乐\n青春不是回忆逝去,而是把握现在!");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 5 && dd == 20) {//520
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("今年是520情人节\n快和你喜欢的人一起过吧!💑");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 7 && dd == 1) {//建党节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("祝中国共产党" + (y - 1921).toString() + "岁生日快乐!");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 9 && dd == 10) {//教师节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("各位老师们教师节快乐!👩‍🏫");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if (m == 12 && dd == 25) {//圣诞节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("圣诞节快乐!🎄");
sessionStorage.setItem("isPopupWindow", "1");
}
}

//传统节日部分

if ((y == 2023 && m == 4 && dd == 5) || (y == 2024 && m == 4 && dd == 4) || (y == 2025 && m == 4 && dd == 4)) {//清明节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("清明时节雨纷纷,一束鲜花祭故人💐");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((y == 2023 && m == 12 && dd == 22) || (y == 2024 && m == 12 && dd == 21) || (y == 2025 && m == 12 && dd == 21)) {//冬至
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("冬至快乐\n快吃上一碗热热的汤圆和饺子吧🧆");
sessionStorage.setItem("isPopupWindow", "1");
}
}

var lunar = calendarFormatter.solar2lunar();

//农历采用汉字计算,防止出现闰月导致问题

if ((lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初六") || (lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初五") || (lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初四") || (lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初三") || (lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初二") || (lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "初一") || (lunar["IMonthCn"] == "腊月" && lunar["IDayCn"] == "三十") || (lunar["IMonthCn"] == "腊月" && lunar["IDayCn"] == "廿九")) {
//春节,本来只有大年三十到初六,但是有时候除夕是大年二十九,所以也加上了
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire(y.toString() + "年新年快乐\n🎊祝你心想事成,诸事顺利🎊");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((lunar["IMonthCn"] == "正月" && lunar["IDayCn"] == "十五")) {
//元宵节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("元宵节快乐\n送你一个大大的灯笼🧅");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((lunar["IMonthCn"] == "五月" && lunar["IDayCn"] == "初五")) {
//端午节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("端午节快乐\n请你吃一条粽子🍙");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((lunar["IMonthCn"] == "七月" && lunar["IDayCn"] == "初七")) {
//七夕节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("七夕节快乐\n黄昏后,柳梢头,牛郎织女来碰头");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((lunar["IMonthCn"] == "八月" && lunar["IDayCn"] == "十五")) {
//中秋节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("中秋节快乐\n请你吃一块月饼🍪");
sessionStorage.setItem("isPopupWindow", "1");
}
}
if ((lunar["IMonthCn"] == "九月" && lunar["IDayCn"] == "初九")) {
//重阳节
if (sessionStorage.getItem("isPopupWindow") != "1") {
Swal.fire("重阳节快乐\n独在异乡为异客,每逢佳节倍思亲");
sessionStorage.setItem("isPopupWindow", "1");
}
}

// 切换主题提醒
// if (y == 2022 && m == 12 && (dd >= 18 && dd <= 20)) {
// if (sessionStorage.getItem("isPopupWindow") != "1") {
// Swal.fire("网站换成冬日限定主题啦⛄");
// sessionStorage.setItem("isPopupWindow", "1");
// }
// }
  1. 由于有的节日是农历的,因此还要引入一个计算农历的代码,新建[BlogRoot]\source\js\lunar.js,并写入以下代码,太长了压缩了一下:
1
var lunarInfo=[19416,19168,42352,21717,53856,55632,91476,22176,39632,21970,19168,42422,42192,53840,119381,46400,54944,44450,38320,84343,18800,42160,46261,27216,27968,109396,11104,38256,21234,18800,25958,54432,59984,28309,23248,11104,100067,37600,116951,51536,54432,120998,46416,22176,107956,9680,37584,53938,43344,46423,27808,46416,86869,19872,42416,83315,21168,43432,59728,27296,44710,43856,19296,43748,42352,21088,62051,55632,23383,22176,38608,19925,19152,42192,54484,53840,54616,46400,46752,103846,38320,18864,43380,42160,45690,27216,27968,44870,43872,38256,19189,18800,25776,29859,59984,27480,23232,43872,38613,37600,51552,55636,54432,55888,30034,22176,43959,9680,37584,51893,43344,46240,47780,44368,21977,19360,42416,86390,21168,43312,31060,27296,44368,23378,19296,42726,42208,53856,60005,54576,23200,30371,38608,19195,19152,42192,118966,53840,54560,56645,46496,22224,21938,18864,42359,42160,43600,111189,27936,44448,84835,37744,18936,18800,25776,92326,59984,27424,108228,43744,41696,53987,51552,54615,54432,55888,23893,22176,42704,21972,21200,43448,43344,46240,46758,44368,21920,43940,42416,21168,45683,26928,29495,27296,44368,84821,19296,42352,21732,53600,59752,54560,55968,92838,22224,19168,43476,41680,53584,62034,54560],solarMonth=[31,28,31,30,31,30,31,31,30,31,30,31],Gan=["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"],Zhi=["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"],Animals=["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"],solarTerm=["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"],sTermInfo=["9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd0b06bdb0722c965ce1cfcc920f","b027097bd097c36b0b6fc9274c91aa","9778397bd19801ec9210c965cc920e","97b6b97bd19801ec95f8c965cc920f","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd197c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bd09801d98082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec95f8c965cc920e","97bcf97c3598082c95f8e1cfcc920f","97bd097bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c3598082c95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf97c359801ec95f8c965cc920f","97bd097bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd19801ec9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b97bd19801ec95f8c965cc920f","97bd07f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c91aa","97b6b97bd19801ec9210c965cc920e","97bd07f1487f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c965cc920e","97bcf7f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b97bd19801ec9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b97bd197c36c9210c9274c920e","97bcf7f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","9778397bd097c36c9210c9274c920e","97b6b7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c36b0b6fc9210c8dc2","9778397bd097c36b0b70c9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9274c91aa","97b6b7f0e47f531b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c91aa","97b6b7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","9778397bd097c36b0b6fc9210c8dc2","977837f0e37f149b0723b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f5307f595b0b0bc920fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f595b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc9210c8dc2","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd097c35b0b6fc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0b0bb0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14998082b0723b06bd","7f07e7f0e37f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e397bd07f595b0b0bc920fb0722","977837f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f595b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e37f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f1487f531b0b0bb0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e47f149b0723b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14998082b0723b06bd","7f07e7f0e37f14998083b0787b0721","7f0e27f0e47f531b0723b0b6fb0722","7f0e37f0e366aa89801eb072297c35","7ec967f0e37f14898082b0723b02d5","7f07e7f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66aa89801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b0721","7f07e7f0e47f531b0723b0b6fb0722","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b0723b02d5","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e36665b66a449801e9808297c35","665f67f0e37f14898082b072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e26665b66a449801e9808297c35","665f67f0e37f1489801eb072297c35","7ec967f0e37f14998082b0787b06bd","7f07e7f0e47f531b0723b0b6fb0721","7f0e27f1487f531b0b0bb0b6fb0722"],nStr1=["日","一","二","三","四","五","六","七","八","九","十"],nStr2=["初","十","廿","卅"],nStr3=["正","二","三","四","五","六","七","八","九","十","冬","腊"];function lYearDays(b){var f,c=348;for(f=32768;f>8;f>>=1)c+=lunarInfo[b-1900]&f?1:0;return c+leapDays(b)}function leapMonth(b){return 15&lunarInfo[b-1900]}function leapDays(b){return leapMonth(b)?65536&lunarInfo[b-1900]?30:29:0}function monthDays(b,f){return f>12||f<1?-1:lunarInfo[b-1900]&65536>>f?30:29}function solarDays(b,f){if(f>12||f<1)return-1;var c=f-1;return 1===c?b%4==0&&b%100!=0||b%400==0?29:28:solarMonth[c]}function toGanZhiYear(b){var f=(b-3)%10,c=(b-3)%12;return 0===f&&(f=10),0===c&&(c=12),Gan[f-1]+Zhi[c-1]}function toAstro(b,f){return"魔羯水瓶双鱼白羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯".substr(2*b-(f<[20,19,21,21,21,22,23,23,23,23,22,22][b-1]?2:0),2)+"座"}function toGanZhi(b){return Gan[b%10]+Zhi[b%12]}function getTerm(b,f){if(b<1900||b>2100)return-1;if(f<1||f>24)return-1;var c=sTermInfo[b-1900],e=[parseInt("0x"+c.substr(0,5)).toString(),parseInt("0x"+c.substr(5,5)).toString(),parseInt("0x"+c.substr(10,5)).toString(),parseInt("0x"+c.substr(15,5)).toString(),parseInt("0x"+c.substr(20,5)).toString(),parseInt("0x"+c.substr(25,5)).toString()],a=[e[0].substr(0,1),e[0].substr(1,2),e[0].substr(3,1),e[0].substr(4,2),e[1].substr(0,1),e[1].substr(1,2),e[1].substr(3,1),e[1].substr(4,2),e[2].substr(0,1),e[2].substr(1,2),e[2].substr(3,1),e[2].substr(4,2),e[3].substr(0,1),e[3].substr(1,2),e[3].substr(3,1),e[3].substr(4,2),e[4].substr(0,1),e[4].substr(1,2),e[4].substr(3,1),e[4].substr(4,2),e[5].substr(0,1),e[5].substr(1,2),e[5].substr(3,1),e[5].substr(4,2)];return parseInt(a[f-1])}function toChinaMonth(b){if(b>12||b<1)return-1;var f=nStr3[b-1];return f+="月"}function toChinaDay(b){var f;switch(b){case 10:f="初十";break;case 20:f="二十";break;case 30:f="三十";break;default:f=nStr2[Math.floor(b/10)],f+=nStr1[b%10]}return f}function getAnimal(b){return Animals[(b-4)%12]}function solar2lunar(b,f,c){if(b<1900||b>2100)return-1;if(1900===b&&1===f&&c<31)return-1;var e,a,r=null,t=0;b=(r=b?new Date(b,parseInt(f)-1,c):new Date).getFullYear(),f=r.getMonth()+1,c=r.getDate();var d=(Date.UTC(r.getFullYear(),r.getMonth(),r.getDate())-Date.UTC(1900,0,31))/864e5;for(e=1900;e<2101&&d>0;e++)d-=t=lYearDays(e);d<0&&(d+=t,e--);var n=new Date,s=!1;n.getFullYear()===b&&n.getMonth()+1===f&&n.getDate()===c&&(s=!0);var u=r.getDay(),o=nStr1[u];0===u&&(u=7);var l=e;a=leapMonth(e);var i=!1;for(e=1;e<13&&d>0;e++)a>0&&e===a+1&&!1===i?(--e,i=!0,t=leapDays(l)):t=monthDays(l,e),!0===i&&e===a+1&&(i=!1),d-=t;0===d&&a>0&&e===a+1&&(i?i=!1:(i=!0,--e)),d<0&&(d+=t,--e);var h=e,D=d+1,g=f-1,v=toGanZhiYear(l),y=getTerm(b,2*f-1),m=getTerm(b,2*f),p=toGanZhi(12*(b-1900)+f+11);c>=y&&(p=toGanZhi(12*(b-1900)+f+12));var M=!1,T=null;y===c&&(M=!0,T=solarTerm[2*f-2]),m===c&&(M=!0,T=solarTerm[2*f-1]);var I=toGanZhi(Date.UTC(b,g,1,0,0,0,0)/864e5+25567+10+c-1),C=toAstro(f,c);return{lYear:l,lMonth:h,lDay:D,Animal:getAnimal(l),IMonthCn:(i?"闰":"")+toChinaMonth(h),IDayCn:toChinaDay(D),cYear:b,cMonth:f,cDay:c,gzYear:v,gzMonth:p,gzDay:I,isToday:s,isLeap:i,nWeek:u,ncWeek:"星期"+o,isTerm:M,Term:T,astro:C}}var calendarFormatter={solar2lunar:function(b,f,c){return solar2lunar(b,f,c)},lunar2solar:function(b,f,c,e){if((e=!!e)&&leapMonth!==f)return-1;if(2100===b&&12===f&&c>1||1900===b&&1===f&&c<31)return-1;var a=monthDays(b,f),r=a;if(e&&(r=leapDays(b,f)),b<1900||b>2100||c>r)return-1;for(var t=0,d=1900;d<b;d++)t+=lYearDays(d);var n=0,s=!1;for(d=1;d<f;d++)n=leapMonth(b),s||n<=d&&n>0&&(t+=leapDays(b),s=!0),t+=monthDays(b,d);e&&(t+=a);var u=Date.UTC(1900,1,30,0,0,0),o=new Date(864e5*(t+c-31)+u);return solar2lunar(o.getUTCFullYear(),o.getUTCMonth()+1,o.getUTCDate())}};
  1. 引入以下以上两个js文件和一个弹窗依赖(注意顺序不能颠倒):
1
2
3
4
5
inject: 
bottom:
+ - <script defer type="text/javascript" src="https://cdn1.tianli0.top/npm/sweetalert2@8.19.0/dist/sweetalert2.all.js"></script> # 节日弹窗依赖
+ - <script defer src="/js/lunar.js"></script> # 农历计算
+ - <script defer src="/js/day.js"></script> # 节日弹窗
  1. 重启项目,如果是日子对了自动会有弹窗的
1
hexo cl;hexo s

欢迎信息显示地理位置

点击查看教程
  1. 获取API Key:进入腾讯位置服务应用管理界面,点击创建应用,应用名称和类型随便填。在新创建的应用中点击添加key,产品选择WebServiceAPI,域名白名单填自己的域名或不填。把得到的key记下。如果开启白名单记得把localhost也加上

  2. 新建[BlogRoot]\source\js\txmap.js,并写入如下代码,记住替换key的内容:

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
//get请求
$.ajax({
type: 'get',
url: 'https://apis.map.qq.com/ws/location/v1/ip',
data: {
key: '你的key',
output: 'jsonp',
},
dataType: 'jsonp',
success: function (res) {
ipLoacation = res;
}
})
function getDistance(e1, n1, e2, n2) {
const R = 6371
const { sin, cos, asin, PI, hypot } = Math
let getPoint = (e, n) => {
e *= PI / 180
n *= PI / 180
return { x: cos(n) * cos(e), y: cos(n) * sin(e), z: sin(n) }
}

let a = getPoint(e1, n1)
let b = getPoint(e2, n2)
let c = hypot(a.x - b.x, a.y - b.y, a.z - b.z)
let r = asin(c / 2) * 2 * R
return Math.round(r);
}

function showWelcome() {

let dist = getDistance(113.34499552, 23.15537143, ipLoacation.result.location.lng, ipLoacation.result.location.lat); //这里换成自己的经纬度
let pos = ipLoacation.result.ad_info.nation;
let ip;
let posdesc;
//根据国家、省份、城市信息自定义欢迎语
switch (ipLoacation.result.ad_info.nation) {
case "日本":
posdesc = "よろしく,一起去看樱花吗";
break;
case "美国":
posdesc = "Let us live in peace!";
break;
case "英国":
posdesc = "想同你一起夜乘伦敦眼";
break;
case "俄罗斯":
posdesc = "干了这瓶伏特加!";
break;
case "法国":
posdesc = "C'est La Vie";
break;
case "德国":
posdesc = "Die Zeit verging im Fluge.";
break;
case "澳大利亚":
posdesc = "一起去大堡礁吧!";
break;
case "加拿大":
posdesc = "拾起一片枫叶赠予你";
break;
case "中国":
pos = ipLoacation.result.ad_info.province + " " + ipLoacation.result.ad_info.city + " " + ipLoacation.result.ad_info.district;
ip = ipLoacation.result.ip;
switch (ipLoacation.result.ad_info.province) {
case "北京市":
posdesc = "北——京——欢迎你~~~";
break;
case "天津市":
posdesc = "讲段相声吧。";
break;
case "河北省":
posdesc = "山势巍巍成壁垒,天下雄关。铁马金戈由此向,无限江山。";
break;
case "山西省":
posdesc = "展开坐具长三尺,已占山河五百余。";
break;
case "内蒙古自治区":
posdesc = "天苍苍,野茫茫,风吹草低见牛羊。";
break;
case "辽宁省":
posdesc = "我想吃烤鸡架!";
break;
case "吉林省":
posdesc = "状元阁就是东北烧烤之王。";
break;
case "黑龙江省":
posdesc = "很喜欢哈尔滨大剧院。";
break;
case "上海市":
posdesc = "众所周知,中国只有两个城市。";
break;
case "江苏省":
switch (ipLoacation.result.ad_info.city) {
case "南京市":
posdesc = "这是我挺想去的城市啦。";
break;
case "苏州市":
posdesc = "上有天堂,下有苏杭。";
break;
default:
posdesc = "散装是必须要散装的。";
break;
}
break;
case "浙江省":
posdesc = "东风渐绿西湖柳,雁已还人未南归。";
break;
case "河南省":
switch (ipLoacation.result.ad_info.city) {
case "郑州市":
posdesc = "豫州之域,天地之中。";
break;
case "南阳市":
posdesc = "臣本布衣,躬耕于南阳。此南阳非彼南阳!";
break;
case "驻马店市":
posdesc = "峰峰有奇石,石石挟仙气。嵖岈山的花很美哦!";
break;
case "开封市":
posdesc = "刚正不阿包青天。";
break;
case "洛阳市":
posdesc = "洛阳牡丹甲天下。";
break;
default:
posdesc = "可否带我品尝河南烩面啦?";
break;
}
break;
case "安徽省":
posdesc = "蚌埠住了,芜湖起飞。";
break;
case "福建省":
posdesc = "井邑白云间,岩城远带山。";
break;
case "江西省":
posdesc = "落霞与孤鹜齐飞,秋水共长天一色。";
break;
case "山东省":
posdesc = "遥望齐州九点烟,一泓海水杯中泻。";
break;
case "湖北省":
posdesc = "来碗热干面!";
break;
case "湖南省":
posdesc = "74751,长沙斯塔克。";
break;
case "广东省":
posdesc = "老板来两斤福建人。";
break;
case "广西壮族自治区":
posdesc = "桂林山水甲天下。";
break;
case "海南省":
posdesc = "朝观日出逐白浪,夕看云起收霞光。";
break;
case "四川省":
posdesc = "康康川妹子。";
break;
case "贵州省":
posdesc = "茅台,学生,再塞200。";
break;
case "云南省":
posdesc = "玉龙飞舞云缠绕,万仞冰川直耸天。";
break;
case "西藏自治区":
posdesc = "躺在茫茫草原上,仰望蓝天。";
break;
case "陕西省":
posdesc = "来份臊子面加馍。";
break;
case "甘肃省":
posdesc = "羌笛何须怨杨柳,春风不度玉门关。";
break;
case "青海省":
posdesc = "牛肉干和老酸奶都好好吃。";
break;
case "宁夏回族自治区":
posdesc = "大漠孤烟直,长河落日圆。";
break;
case "新疆维吾尔自治区":
posdesc = "驼铃古道丝绸路,胡马犹闻唐汉风。";
break;
case "台湾省":
posdesc = "我在这头,大陆在那头。";
break;
case "香港特别行政区":
posdesc = "永定贼有残留地鬼嚎,迎击光非岁玉。";
break;
case "澳门特别行政区":
posdesc = "性感荷官,在线发牌。";
break;
default:
posdesc = "带我去你的城市逛逛吧!";
break;
}
break;
default:
posdesc = "带我去你的国家逛逛吧。";
break;
}

//根据本地时间切换欢迎语
let timeChange;
let date = new Date();
if (date.getHours() >= 5 && date.getHours() < 11) timeChange = "<span>上午好</span>,一日之计在于晨!";
else if (date.getHours() >= 11 && date.getHours() < 13) timeChange = "<span>中午好</span>,该摸鱼吃午饭了。";
else if (date.getHours() >= 13 && date.getHours() < 15) timeChange = "<span>下午好</span>,懒懒地睡个午觉吧!";
else if (date.getHours() >= 15 && date.getHours() < 16) timeChange = "<span>三点几啦</span>,一起饮茶呀!";
else if (date.getHours() >= 16 && date.getHours() < 19) timeChange = "<span>夕阳无限好!</span>";
else if (date.getHours() >= 19 && date.getHours() < 24) timeChange = "<span>晚上好</span>,夜生活嗨起来!";
else timeChange = "夜深了,早点休息,少熬夜。";

try {
//自定义文本和需要放的位置
document.getElementById("welcome-info").innerHTML =
`<b><center>🎉 欢迎信息 🎉</center>&emsp;&emsp;欢迎来自 <span style="color:var(--theme-color)">${pos}</span> 的小伙伴,${timeChange}您现在距离站长约 <span style="color:var(--theme-color)">${dist}</span> 公里,当前的IP地址为: <span style="color:var(--theme-color)">${ip}</span>, ${posdesc}</b>`;
} catch (err) {
// console.log("Pjax无法获取#welcome-info元素🙄🙄🙄")
}
}
window.onload = showWelcome;
// 如果使用了pjax在加上下面这行代码
document.addEventListener('pjax:complete', showWelcome);
  1. 在主题配置文件[BlogRoot]\_config.butterfly.yml中引入jQuery依赖和刚刚的js文件:
1
2
3
4
inject: 
bottom:
+ - <script src="https://cdn.staticfile.org/jquery/3.6.3/jquery.min.js"></script> # jQuery
+ - <script async data-pjax src="/js/txmap.js"></script> # 腾讯位置API
  1. 在需要展示文本的容器上添加相应id(welcome-info)就可以了,例如我想添加在网站公告栏信息的下方,于是就在[BlogRoot]\themes\butterfly\layout\includes\widget\card_announcement.pug的最后一行加上这个,缩进与上一行相同即可
1
2
3
    .announcement_content!= theme.aside.card_announcement.content
//- 添加欢迎访客的信息
+ #welcome-info
  1. 在custom.css自定义样式里添加如下代码,可以根据你自己的喜好去改
1
2
3
4
5
6
7
8
9
/* 欢迎信息 */
#welcome-info {
background: linear-gradient(45deg, #b9f4f3, #e3fbf9);
border-radius: 18px;
padding: 8px;
}
[data-theme="dark"] #welcome-info {
background: #212121;
}
  1. hexo二连即可看到效果
1
hexo cl;hexo s

添加fps显示(LYX)

点击查看教程
  1. 新建文件[BlogRoot]\source\js\fps.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
if (window.localStorage.getItem("fpson") == undefined || window.localStorage.getItem("fpson") == "1") {
var rAF = function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
}
);
}();
var frame = 0;
var allFrameCount = 0;
var lastTime = Date.now();
var lastFameTime = Date.now();
var loop = function () {
var now = Date.now();
var fs = (now - lastFameTime);
var fps = Math.round(1000 / fs);

lastFameTime = now;
// 不置 0,在动画的开头及结尾记录此值的差值算出 FPS
allFrameCount++;
frame++;

if (now > 1000 + lastTime) {
var fps = Math.round((frame * 1000) / (now - lastTime));
if (fps <= 5) {
var kd = `<span style="color:#bd0000">卡成ppt🤢</span>`
} else if (fps <= 15) {
var kd = `<span style="color:red">电竞级帧率😖</span>`
} else if (fps <= 25) {
var kd = `<span style="color:orange">有点难受😨</span>`
} else if (fps < 35) {
var kd = `<span style="color:#9338e6">不太流畅🙄</span>`
} else if (fps <= 45) {
var kd = `<span style="color:#08b7e4">还不错哦😁</span>`
} else {
var kd = `<span style="color:#39c5bb">十分流畅🤣</span>`
}
document.getElementById("fps").innerHTML = `FPS:${fps} ${kd}`;
frame = 0;
lastTime = now;
};

rAF(loop);
}

loop();
} else {
document.getElementById("fps").style = "display:none!important"
}
  1. 在自定义样式文件custom.css中加入如下代码,我这里让这块东西在左下角,你可以自己指定位置,其中backdrop-filter过滤器也可以自己指定,也可以不要:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 帧率检测 */
#fps {
position: fixed;
/* 指定位置 */
left: 10px;
bottom: 10px;
z-index: 1919810;
}
[data-theme="light"] #fps {
background-color: rgba(255, 255, 255, 0.85);
backdrop-filter: var(--backdrop-filter);
padding: 4px;
border-radius: 4px;
}
[data-theme="dark"] #fps {
background-color: rgba(0, 0, 0, 0.72);
backdrop-filter: var(--backdrop-filter);
padding: 4px;
border-radius: 4px;
}
  1. 在主题配置文件_config.butterfly.yml文件中加入以下代码:
1
2
3
4
5
inject: 
head:
+ - <span id="fps"></span> # 帧率检测
bottom:
+ - <script async src="/js/fps.js"></script> # 帧率检测
  1. 重启项目看看角落有没有出现帧率块
1
hexo cl;hexo s

网站恶搞标题

点击查看教程
  1. 新建文件[BlogRoot]\source\js\title.js,写入以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//动态标题
var OriginTitile = document.title;
var titleTime;
document.addEventListener('visibilitychange', function () {
if (document.hidden) {
//离开当前页面时标签显示内容
document.title = '👀跑哪里去了~';
clearTimeout(titleTime);
} else {
//返回当前页面时标签显示内容
document.title = '🐖抓到你啦~';
//两秒后变回正常标题
titleTime = setTimeout(function () {
document.title = OriginTitile;
}, 2000);
}
});
  1. 在主题配置文件_config.butterfly.yml引入该文件:
1
2
3
inject: 
bottom:
+ - <script async src="/js/title.js"></script>
  1. 重启项目
1
hexo cl; hexo s

导航栏魔改增强版(LYX)

点击查看教程
  1. 重构导航栏:修改[blogRoot]\themes\Butterfly\layout\includes\header\nav.pug,替换成下面的代码,其中图标啥的自己改一下:
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
nav#nav
span#blog_name
a#site-name(href=url_for('/')) #[=config.title]

#menus
!=partial('includes/header/menu_item', {}, {cache: true})
//- 这两行是导航栏显示标题用的
center(id="name-container")
a(id="page-name" href="javascript:scrollToTop()") PAGE_NAME

#nav-right
if (theme.algolia_search.enable || theme.local_search.enable)
#search-button
a.search.faa-parent.animated-hover(title="检索站内任何你想要的信息")
svg.faa-tada.icon(style="height:24px;width:24px;fill:currentColor;position:relative;top:6px" aria-hidden="true")
use(xlink:href='#icon-valentine_-search-love-find-heart')
span=' '+_p('search.title')

//- 美化设置
a.meihua.faa-parent.animated-hover(onclick="toggleWinbox()" title="美化设置-自定义你的风格" id="meihua-button")
svg.faa-tada.icon(style="height:26px;width:26px;fill:currentColor;position:relative;top:8px" aria-hidden="true")
use(xlink:href='#icon-tupian1')

//- 暗黑模式按钮
a.sun_moon.faa-parent.animated-hover(onclick='switchNightMode()', title=_p('rightside.night_mode_title') id="nightmode-button")
svg.faa-tada(style="height:25px;width:25px;fill:currentColor;position:relative;top:7px", viewBox='0 0 1024 1024')
use#modeicon(xlink:href='#icon-moon')

#toggle-menu
a
i.fas.fa-bars.fa-fw
  1. 标题增强:在custom.css加入如下代码,其中var(--theme-color)替换成你自己的主题色:
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
/* 标题增强 */
#site-name::before {
opacity: 0;
background-color: var(--theme-color) !important;
border-radius: 8px;
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-ms-border-radius: 8px;
-o-border-radius: 8px;
transition: 0.3s;
-webkit-transition: 0.3s;
-moz-transition: 0.3s;
-ms-transition: 0.3s;
-o-transition: 0.3s;
position: absolute;
top: 0 !important;
right: 0 !important;
width: 100%;
height: 100%;
content: "\f015";
box-shadow: 0 0 5px var(--theme-color);
font-family: "Font Awesome 6 Free";
text-align: center;
color: white;
line-height: 34px; /*如果有溢出或者垂直不居中的现象微调一下这个参数*/
font-size: 18px; /*根据个人喜好*/
}
#site-name:hover::before {
opacity: 1;
scale: 1.03;
}
#site-name {
position: relative;
font-size: 24px; /*一定要把字体调大点,否则效果惨不忍睹!*/
}
  1. 顶栏常驻:在custom.css加入如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.nav-fixed #nav{
transform: translateY(58px)!important;
-webkit-transform: translateY(58px)!important;
-moz-transform: translateY(58px)!important;
-ms-transform: translateY(58px)!important;
-o-transform: translateY(58px)!important;
}
#nav{
transition: none!important;
-webkit-transition: none!important;
-moz-transition: none!important;
-ms-transition: none!important;
-o-transition: none!important;
}
  1. 显示标题:新建[BlogRoot]\source\js\nav.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
document.addEventListener('pjax:complete', tonav);
document.addEventListener('DOMContentLoaded', tonav);
//响应pjax
function tonav() {
document.getElementById("name-container").setAttribute("style", "display:none");
var position = $(window).scrollTop();
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > position) {
document.getElementById("name-container").setAttribute("style", "");
document.getElementsByClassName("menus_items")[1].setAttribute("style", "display:none!important");
} else {
document.getElementsByClassName("menus_items")[1].setAttribute("style", "");
document.getElementById("name-container").setAttribute("style", "display:none");
}
position = scroll;
});
//修复没有弄右键菜单的童鞋无法回顶部的问题
document.getElementById("page-name").innerText = document.title.split(" | Fomalhaut🥝")[0];
}

function scrollToTop() {
document.getElementsByClassName("menus_items")[1].setAttribute("style", "");
document.getElementById("name-container").setAttribute("style", "display:none");
btf.scrollToDest(0, 500);
}
然后还要在`custom.css`添加以下css,其中`--theme-color`换为你自己的主题色:
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
/* 导航栏显示标题 */
#page-name::before {
font-size: 18px;
position: absolute;
width: 100%;
height: 100%;
border-radius: 8px;
color: white !important;
top: 0;
left: 0;
content: "回到顶部";
background-color: var(--theme-color);
transition: all 0.3s;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
-ms-transition: all 0.3s;
-o-transition: all 0.3s;
opacity: 0;
box-shadow: 0 0 3px var(--theme-color);
line-height: 45px; /*如果垂直位置不居中可以微调此值,也可以删了*/
}
#page-name:hover:before {
opacity: 1;
}
#name-container {
transition: all 0.3s;
-webkit-transition: all 0.3s;
-moz-transition: all 0.3s;
-ms-transition: all 0.3s;
-o-transition: all 0.3s;
}
#name-container:hover {
scale: 1.03;
}
#page-name {
position: relative;
padding: 10px 30px; /*如果文字间隔不合理可以微调修改,第二个是水平方向的padding,第一个是垂直的*/
}
#nav{
padding: 0 20px;
}

/* 修复滚动显示标题居中 */
center#name-container {
position: absolute !important;
width: fit-content !important;
left: 42% !important;
}
@media screen and (max-width: 768px) {
center#name-container {
display: none;
}
}
  1. 重启项目:
1
hexo cl;hexo s

配置文件CDN替换

点击查看教程

主题默认的CDN有:local、cdnjs、jsdelivr、unpkg等,但是速度偶读比较一般,要想提高部分标准静态资源的响应速度,走CDN是最好的办法,最好是在国内的CDN。

参考教程:

  1. CSDN:Web前端常用CDN网站汇总
  2. 洪哥:Butterfly CDN链接更改指南,替换jsdelivr提升访问速度
  3. 安知鱼:目前可用cdn整理

修改教程,我分享一下我目前在用的方案:

修改主题配置文件_config.butterfly.yml的CDN配置项:

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
# CDN
# Don't modify the following settings unless you know how they work
# 非必要請不要修改
CDN:
# The CDN provider of internal scripts (主題內部 js 的 cdn 配置)
# option: local/jsdelivr/unpkg/cdnjs/custom
# Dev version can only choose. ( dev版的主題只能設置為 local )
internal_provider: local

# The CDN provider of third party scripts (第三方 js 的 cdn 配置)
# option: local/jsdelivr/unpkg/cdnjs/custom
# when set it to local, you need to install hexo-butterfly-extjs
third_party_provider: cdnjs

# Add version number to CDN, true or false
version: false

# Custom format
# For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file}
custom_format:

option:
# main_css:
# main:
# utils:
# translate: https://cdn1.tianli0.top/npm/js-heo@1.0.6/translate/tw_cn.js
# local_search:
# algolia_js:
algolia_search_v4: https://cdn.staticfile.org/algoliasearch/4.14.3/algoliasearch-lite.umd.min.js
instantsearch_v4: https://cdn.staticfile.org/instantsearch.js/4.49.2/instantsearch.production.min.js
pjax: https://lib.baomitu.com/pjax/0.2.8/pjax.min.js
# gitalk: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/gitalk/1.7.2/gitalk.min.js
# gitalk_css: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/gitalk/1.7.2/gitalk.min.css
# blueimp_md5:
# valine: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/valine/1.4.16/Valine.min.js
# disqusjs: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/disqusjs/1.3.0/disqus.js
# disqusjs_css: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/disqusjs/1.3.0/disqusjs.css
twikoo: https://cdn.staticfile.org/twikoo/1.6.8/twikoo.all.min.js
# waline_js: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/waline/1.5.4/Waline.min.js
# waline_css:
sharejs: https://lib.baomitu.com/social-share.js/1.0.16/js/social-share.min.js
sharejs_css: https://lib.baomitu.com/social-share.js/1.0.16/css/share.min.css
# mathjax: https://cdn.staticfile.org/mathjax/3.2.2/es5/mml-chtml.min.js
# katex: https://lib.baomitu.com/KaTeX/latest/katex.min.css
# katex_copytex:
# mermaid:
# canvas_ribbon:
# canvas_fluttering_ribbon:
# canvas_nest:
lazyload: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vanilla-lazyload/17.3.1/lazyload.iife.min.js
instantpage: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/instant.page/5.1.0/instantpage.min.js
typed: https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/typed.js/2.0.12/typed.min.js
# pangu:
fancybox_css_v4: https://cdn.staticfile.org/fancyapps-ui/4.0.31/fancybox.min.css
fancybox_v4: https://cdn.staticfile.org/fancyapps-ui/4.0.31/fancybox.umd.min.js
# medium_zoom: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/medium-zoom/1.0.6/medium-zoom.min.js
# snackbar_css: https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.css
# snackbar: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/node-snackbar/0.1.16/snackbar.min.js
# activate_power_mode:
# fireworks:
# click_heart:
# ClickShowText:
fontawesomeV6: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/font-awesome/6.0.0/css/all.min.css
# flickr_justified_gallery_js: https://cdn.staticfile.org/flickr-justified-gallery/2.1.2/fjGallery.min.js
# flickr_justified_gallery_css: https://cdn.staticfile.org/flickr-justified-gallery/2.1.2/fjGallery.min.css
aplayer_css: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.css
aplayer_js: https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/aplayer/1.10.1/APlayer.min.js
meting_js: https://cdn1.tianli0.top/npm/js-heo@1.0.12/metingjs/Meting.min.js
# prismjs_js: https://cdn1.tianli0.top/npm/prismjs@1.1.0/prism.js
# prismjs_lineNumber_js: https://cdn1.tianli0.top/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js
# prismjs_autoloader: https://cdn1.tianli0.top/npm/prismjs/plugins/autoloader/prism-autoloader.min.js

修改完成后可以 f12->源代码->网页 看看是否已经加载到对应的资源

右边滚动栏样式

点击查看教程
  1. custom.css中加入以下代码,其中var(--theme-color)换成你自己的主题色:
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
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}

::-webkit-scrollbar-track {
background-color: rgba(73, 177, 245, 0.2);
border-radius: 2em;
}

::-webkit-scrollbar-thumb {
background-color: var(--theme-color);
background-image: -webkit-linear-gradient(
45deg,
rgba(255, 255, 255, 0.4) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, 0.4) 50%,
rgba(255, 255, 255, 0.4) 75%,
transparent 75%,
transparent
);
border-radius: 2em;
}

::-webkit-scrollbar-corner {
background-color: transparent;
}

::-moz-selection {
color: #fff;
background-color: var(--theme-color);
}

博客宽屏适配(自用)

点击查看教程
  1. custom.css中加入以下样式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 全局宽度 */
.layout {
max-width: 1400px;
}

/* 侧边卡片栏宽度 */
.aside-content {
max-width: 318px;
min-width: 300px;
}

/* 平板尺寸自适应(不启用侧边栏宽度限制) */
@media screen and (max-width: 900px) {
.aside-content {
max-width: none !important;
padding: 0 5px 0 5px;
}
}
  1. 不想再非首页的地方显示侧边栏,那就需要给非首页的页面加上标记,修改 [BlogRoot]\themes\butterfly\layout\includes\layout.pug为以下内容:
1
2
3
4
5
6
7
8
9
 - var htmlClassHideAside = theme.aside.enable && theme.aside.hide ? 'hide-aside' : ''
- page.aside = is_archive() ? theme.aside.display.archive: is_category() ? theme.aside.display.category : is_tag() ? theme.aside.display.tag : page.aside
- var hideAside = !theme.aside.enable || page.aside === false ? 'hide-aside' : ''
- - var pageType = is_post() ? 'post' : 'page'
+ - var pageType = is_home() ? 'page home' : is_post() ? 'post' : 'page'

doctype html
html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside)
...

现在主页的class就变成page home了,我们再在custom.css加入如下css,主题就能智能区分主页和分页了,可以自动选择卡片显示:

1
2
3
4
5
6
7
8
9
10
/* 除了首页以外其他页面隐藏卡片,并采用宽屏显示 */
#archive,
#page,
#category,
#tag {
width: 100%;
}
.page:not(.page.home) .aside-content {
display: none;
}
  1. 重启项目即可看到变更:
1
hexo cl;hexo s

文章三栏(店长+微调)

点击查看教程
  1. 修改[BlogRoot]\themes\butterfly\layout\includes\mixins\post-ui.pug,整个替换为下面的代码,注意,我这里用的是彩色的图标,每个//- i.fas那里表示我注释了黑白的额图标并换上彩色图标,彩色图标引入的具体方法见之前的教程,这里只需要替换成你自己的图标名字和调节相应的大小即可:
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
mixin postUI(posts)
each article , index in page.posts.data
.recent-post-item
-
let link = article.link || article.path
let title = article.title || _p('no_title')
const position = theme.cover.position
let leftOrRight = position === 'both'
? index%2 == 0 ? 'left' : 'right'
: position === 'left' ? 'left' : 'right'
let post_cover = article.cover
let no_cover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : ''
-
.recent-post-content(class=leftOrRight)
.recent-post-cover
img.article-cover(src=url_for(post_cover) onerror=`this.onerror=null;this.src='`+ url_for(theme.error_img.post_page) + `'` alt=title)
.recent-post-info
a.article-title(href=url_for(link) title=title)
.article-title-link= title
.recent-post-meta
.article-meta-wrap
if (is_home() && (article.top || article.sticky > 0))
span.article-meta
//- i.fas.fa-thumbtack.sticky
svg.meta_icon(style="width:16px;height:16px;position:relative;top:3px").post-ui-icon
use(xlink:href='#icon-tuding')
span.sticky= _p('sticky')
span.article-meta-separator |
if (theme.post_meta.page.date_type)
span.post-meta-date
if (theme.post_meta.page.date_type === 'both')
//- i.far.fa-calendar-alt
svg.meta_icon(style="width:21px;height:21px;position:relative;top:6px").post-ui-icon
use(xlink:href='#icon-rili')
span.article-meta-label=_p('post.created')
time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))=date(article.date, config.date_format)
span.article-meta-separator |
//- i.fas.fa-history
svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
use(xlink:href='#icon-gengxin1')
span.article-meta-label=_p('post.updated') + " "
time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))=date(article.updated, config.date_format)
else
- let data_type_updated = theme.post_meta.page.date_type === 'updated'
- let date_type = data_type_updated ? 'updated' : 'date'
- let date_icon = data_type_updated ? 'fas fa-history' :'far fa-calendar-alt'
- let date_title = data_type_updated ? _p('post.updated') : _p('post.created')
i(class=date_icon)
span.article-meta-label=date_title
time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format)
if (theme.post_meta.page.categories && article.categories.data.length > 0)
span.article-meta
span.article-meta-separator |
//- i.fas.fa-inbox
svg.meta_icon(style="width:12px;height:12px;position:relative;top:1px").post-ui-icon
use(xlink:href='#icon-fenlei')
each item, index in article.categories.data
a(href=url_for(item.path)).article-meta__categories #[=item.name]
if (index < article.categories.data.length - 1)
i.fas.fa-angle-right.article-meta-link
if (theme.post_meta.page.tags && article.tags.data.length > 0)
span.article-meta.tags
span.article-meta-separator |
//- i.fas.fa-tag
svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
use(xlink:href='#icon-biaoqian')
each item, index in article.tags.data
a(href=url_for(item.path)).article-meta__tags #[=item.name]
if (index < article.tags.data.length - 1)
span.article-meta-link #[=' • ']

mixin countBlockInIndex
- needLoadCountJs = true
span.article-meta
span.article-meta-separator |
//- i.fas.fa-comments
svg.meta_icon(style="width:13px;height:13px;position:relative;top:2px").post-ui-icon
use(xlink:href='#icon-pinglun1')
if block
block
span.article-meta-label= ' ' + _p('card_post_count')

if theme.comments.card_post_count
case theme.comments.use[0]
when 'Disqus'
+countBlockInIndex
a(href=full_url_for(link) + '#disqus_thread')
i.fa-solid.fa-spinner.fa-spin
when 'Disqusjs'
+countBlockInIndex
a(href=full_url_for(link) + '#disqusjs')
span.disqus-comment-count(data-disqus-url=full_url_for(link))
i.fa-solid.fa-spinner.fa-spin
when 'Valine'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
span.valine-comment-count(data-xid=url_for(link))
i.fa-solid.fa-spinner.fa-spin
when 'Waline'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
span.waline-comment-count(id=url_for(link))
i.fa-solid.fa-spinner.fa-spin
when 'Twikoo'
+countBlockInIndex
a.twikoo-count(href=url_for(link) + '#post-comment')
i.fa-solid.fa-spinner.fa-spin
when 'Facebook Comments'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
span.fb-comments-count(data-href=urlNoIndex(article.permalink))
when 'Remark42'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
span.remark42__counter(data-url=urlNoIndex(article.permalink))
i.fa-solid.fa-spinner.fa-spin
when 'Artalk'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
span.artalk-count(data-page-key=url_for(link))
i.fa-solid.fa-spinner.fa-spin
a.article-content(href=url_for(link) title=title)
//- Display the article introduction on homepage
case theme.index_post_content.method
when false
- break
when 1
.article-content-text!= article.description
when 2
if article.description
.article-content-text!= article.description
else
- const content = strip_html(article.content)
- let expert = content.substring(0, theme.index_post_content.length)
- content.length > theme.index_post_content.length ? expert += ' ...' : ''
.article-content-text!= expert
default
- const content = strip_html(article.content)
- let expert = content.substring(0, theme.index_post_content.length)
- content.length > theme.index_post_content.length ? expert += ' ...' : ''
.article-content-text!= expert
.recent-post-arrow

if theme.ad && theme.ad.index
if (index + 1) % 3 == 0
.recent-post-item.ads-wrap!=theme.ad.index
  1. 样式方案提供两种:
    • 样式一:电脑端宽屏采用滑动卡片,平板宽度采用双栏布局,手机宽度采用单栏卡片
    • 样式二:移除滑动卡片,按屏幕宽度依次应用三栏、双栏、单栏
      新建目录[BlogRoot]\themes\butterfly\source\css\_index_card_style\,并在下面新建对应的文件slidecard.stylmulticard.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
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
//default color:
:root
--recent-post-bgcolor: rgba(255, 255, 255, 0.9) //默认背景
--article-content-bgcolor: #49b1f5 //描述版块背景
--recent-post-arrow: #ffffff //箭头配色
--recent-post-cover-shadow: #ffffff //封面遮罩层配色,建议和默认值的颜色相对应。
--recent-post-transition: all 0.5s cubic-bezier(0.59, 0.01, 0.48, 1.17) //动画效果。不了解的不要改动
[data-theme="dark"]
--recent-post-bgcolor: rgba(35,35,35,0.5)
--article-content-bgcolor: #99999a
--recent-post-arrow: #37e2dd
--recent-post-cover-shadow: #232323
// 默认的首页卡片容器布局
.recent-posts
padding 0 15px 0 15px
height fit-content
.recent-post-item
margin-bottom 15px
width 100%
background var(--recent-post-bgcolor)
overflow hidden
border-radius 15px
.recent-post-content
display flex
background var(--recent-post-bgcolor)
position relative
.recent-post-cover
display flex
background transparent
.recent-post-info
display flex
background transparent
flex-direction column
justify-content center
align-items center
.article-title
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-end
flex-direction: column
.article-title-link
color: var(--text-highlight-color)
transition: all .2s ease-in-out
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
&:hover
color: $text-hover
.recent-post-meta
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-start
flex-direction: column
.article-meta-wrap
color #969797
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
a
color: var(--text-highlight-color)
transition: all .2s ease-in-out
color #969797
&:hover
color: $text-hover
.article-content
display flex
text-align: center
flex-direction row
align-items center
justify-content center
.article-content-text
display -webkit-box
-webkit-box-orient vertical
text-overflow: ellipsis
overflow hidden
color #fff
text-shadow 1px 2px 3px #000
&::before
content "❝"
font-size 20px
&::after
content "❞"
font-size 20px
&.ads-wrap
display: block !important
height: auto !important
// PC端滑动卡片样式
@media screen and (min-width:1069px)
.recent-posts
padding 0 15px 0 15px
.recent-post-item
.recent-post-content
position relative
height 200px
width 100%
transition var(--recent-post-transition)
&:hover
.recent-post-cover-shadow
width 10.1%
transition var(--recent-post-transition)
.recent-post-cover
width 10%
transition var(--recent-post-transition)
.article-content
width calc(30% + 80px)
transition var(--recent-post-transition)
.article-content-text
opacity 1
.recent-post-arrow
transition var(--recent-post-transition)
.recent-post-cover-shadow
z-index: 1
transition var(--recent-post-transition)
position: absolute
height 200px
width 40%
.recent-post-cover
height 200px
width 40%
transition var(--recent-post-transition)
img
height 100%
width 100%
object-fit cover

.recent-post-info
height 200px
width calc(60% - 80px)
.article-title
margin: 0px 40px
font-size 24px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 12px
-webkit-line-clamp: 3;
.article-content
height 200px
width 90px
background var(--article-content-bgcolor)
transition var(--recent-post-transition)
.article-content-text
-webkit-line-clamp 4
transition: var(--recent-post-transition)
opacity 0
.recent-post-arrow
transition var(--recent-post-transition)
display block
position absolute
height 20px
width 8px
background var(--recent-post-arrow)
&.both,
&.right
.recent-post-cover-shadow
left 0
background linear-gradient(to left, var(--recent-post-cover-shadow), transparent)
.recent-post-cover
order: 1
.recent-post-info
order: 2
.article-content
order: 3
clip-path polygon(0 50%, 80px 0, 100% 0, 100% 100%, 80px 100%)
.article-content-text
margin 20px 40px 20px 80px
.recent-post-arrow
order: 4
left calc(100% - 80px)
top calc(50% - 10px)
clip-path polygon(0 10px, 8px 0, 8px 20px)
&:hover
.recent-post-arrow
left calc(100% - 40px)
&.left
.recent-post-cover-shadow
right 0
background linear-gradient(to right, var(--recent-post-cover-shadow), transparent)
.recent-post-cover
order: 4
.recent-post-info
order: 3
.article-content
order: 2
clip-path polygon(100% 50%,calc(100% - 80px) 100%,0 100%,0 0,calc(100% - 80px) 0)
.article-content-text
margin 20px 80px 20px 40px
.recent-post-arrow
order: 1
left 72px
top calc(50% - 10px)
clip-path polygon(0 0, 8px 10px, 0 20px)
&:hover
.recent-post-arrow
left 32px
// 双栏布局卡片自适应适配
@media screen and (min-width:572px) and (max-width:1068px)
.recent-posts
padding 0 15px 0 15px
display flex
flex-direction row
flex-wrap wrap
.recent-post-item
border-radius 15px
overflow hidden
width 47%
margin 0px 3% 20px 0px
nav#pagination
width: 100%
// 手机端单栏布局自适应适配
@media screen and (max-width:572px)
.recent-posts
padding 0 15px 0 15px
.recent-post-item
border-radius 15px
overflow hidden

// 手机端及双栏卡片样式
@media screen and (max-width:1068px)
.recent-posts
.recent-post-item
.recent-post-content
flex-direction column
flex-wrap nowrap
align-items center
max-height 350px
height: auto
width 100%
.recent-post-cover
width 100%
height 200px
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
img
height 200px
width 100%
object-fit cover
.recent-post-info
height 150px
width 100%
padding 0px 25px 5px 25px
.article-title
margin: 0px 40px
font-size 18px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 12px
-webkit-line-clamp: 3;
.article-content
position absolute
height 200px
width 100%
background rgba(25,25,25,0.5)
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
.article-content-text
-webkit-line-clamp 3
font-size 16px
margin 0px 25px 30px 25px
.recent-post-arrow
display block
background var(--article-content-bgcolor)
position absolute
height 10px
width 20px
clip-path polygon(0 0,100% 0,50% 100%)
top 20px
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
:root
--theme-color:rgb(57, 197, 187)
--text-bg-hover:rgba(57, 197, 187, 0.7)

.recent-posts
padding 0 5px 0 5px
height fit-content
.recent-post-item
margin-bottom 15px
overflow hidden
border-radius 15px
.recent-post-content
display flex
position relative
.recent-post-cover
display flex
background transparent
.recent-post-info
display flex
background transparent
flex-direction column
justify-content center
align-items center
.article-title
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-end
flex-direction: column
.article-title-link
color: var(--text-highlight-color)
transition: all .2s ease-in-out
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
&:hover
color: var(--theme-color)
.recent-post-meta
height 50%
display: flex
text-align: center
align-items: center
justify-content: flex-start
flex-direction: column
.article-meta-wrap
color #969797
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
a
color: var(--text-highlight-color)
transition: all .2s ease-in-out
color #969797
&:hover
color: var(--theme-color)
.article-content
display flex
text-align: center
flex-direction row
align-items center
justify-content center
.article-content-text
display -webkit-box
-webkit-box-orient vertical
text-overflow: ellipsis
overflow hidden
color #fff
text-shadow 1px 2px 3px #000
transition transform 0.6s;
&:hover
transform: scale(1.1);
&.ads-wrap
display: block !important
height: auto !important
nav#pagination
width: 100%
// 卡片单元布局样式
.recent-posts
padding 0 5px 0 5px
display flex
flex-direction row
flex-wrap wrap
.recent-post-item
border-radius 15px
overflow hidden
.recent-post-content
flex-direction column
flex-wrap nowrap
align-items center
max-height 350px
height: auto
width 100%
.recent-post-cover
width 100%
height 200px
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
img
height 200px
width 100%
object-fit cover
.recent-post-info
height 145px
width 100%
padding 0px 25px 5px 25px
.article-title
margin: 0px 40px
font-size 19px
.article-title-link
-webkit-line-clamp: 2;
.recent-post-meta
margin: 0px 20px
.article-meta-wrap
font-size 13px
-webkit-line-clamp: 3;
.article-content
position absolute
height 200px
width 100%
background rgba(25,25,25,0.4)
clip-path polygon(0 130px,0 0,100% 0,100% 130px,50% 100%)
.article-content-text
-webkit-line-clamp 3
font-size 16px
margin 0px 25px 30px 25px
&::before
content "「"
font-size 20px
&::after
content "」"
font-size 20px
.recent-post-arrow
display block
background var(--text-bg-hover)
position absolute
height 10px
width 20px
clip-path polygon(0 0,100% 0,50% 100%)
// 三栏布局滑动卡片样式
@media screen and (min-width:1069px)
.recent-posts
.recent-post-item
width 32.3%
margin 0px 1% 20px 0px
.recent-post-content
.recent-post-info
.article-title
margin: 0px 5px
.article-title-link
-webkit-line-clamp: 1;
.recent-post-meta
margin: 0px 5px
.article-meta-wrap
-webkit-line-clamp: 2;
// 双栏布局卡片自适应适配
@media screen and (min-width:572px) and (max-width:1068px)
.recent-posts
.recent-post-item
width 47%
margin 0px 3% 20px 0px
// 单栏布局卡片自适应适配
@media screen and (max-width:572px)
.recent-posts
.recent-post-item
width 100%
1. 修改`[BlogRoot]\themes\butterfly\source\css\_page\homepage.styl`,将整文件内容替换为以下代码:
1
2
3
4
if hexo-config('index_card_style') == 'slidecard'
@import './_index_card_style/slidecard'
else if hexo-config('index_card_style') == 'multicard'
@import './_index_card_style/multicard'
1. 然后在主题配置文件`[BlogRoot]\_config.butterfly.yml`里新增配置项,这样我们就可以通过配置项自由切换使用哪款了:
1
2
3
# 主页卡片样式
# Docs: https://akilar.top/posts/d6b69c49/
index_card_style: multicard # slidecard | multicard
1. 考虑到不管是样式一还是样式二都存在一个布局突变的情况。为了不至于让首页的文章出现空缺,建议将首页生成的文章数量控制为1,2,3的公倍数。修改站点配置文件`[BlogRoot]\_config.yml`。找到以下配置项进行调整,注意这是站点配置文件本就有的配置项,不是新增配置项。建议是调整为12篇。如果你的侧边栏魔改内容特别多,那么建议改成18、24、30。务必确保文章卡片栏比侧栏完全展开要长,这样展示效果最好
1
2
3
4
5
6
7
8
# Home page setting
# path: Root path for your blogs index page. (default = '')
# per_page: Posts displayed per page. (0 = disable pagination)
# order_by: Posts order. (Order by date descending by default)
index_generator:
path: ''
per_page: 18
order_by: -date
1. 本教程讨论的卡片都是考虑有封面和有描述的。所以需要保证你已经开启了相应的配置,查看主题配置文件`[BlogRoot]\_config.butterfly.yml`,找到配置项开启描述栏,建议选择2模式
1
2
3
4
5
6
7
8
# Display the article introduction on homepage
# 1: description
# 2: both (if the description exists, it will show description, or show the auto_excerpt)
# 3: auto_excerpt (default)
# false: do not show the article introduction
index_post_content:
method: 2
length: 500 # if you set method to 2 or 3, the length need to config