窝点精装修记录
这篇记录并非边搭边写,小学期课很多,时间不够使,以下内容皆为后记
部分预设来自大佬Fomalhaut🥝的站内分享,链接:Fomalhaut🥝,欢迎来访!🍭🍭🍭
主题设置
导航栏
1 | menu: |
启用主题默认导航栏,清单分类暂时不想弄,注释掉。在source/css目录下新建custom.css文件,自定义全局css样式:
1 |
|
效果是导航栏居中,搜索栏居右。搜索栏的搜索二字略显突兀,仅保留图标,在\themes\butterfly\layout\includes\header\nav.pug把- span= ' ' + _p('search.title')一行注释或删掉。最终效果如下所示
主页一图流设置
原Butterfly主题自带header和footer的黑色遮罩,会遮住壁纸,全部去掉。
在source/css/custom.css中添加如下代码
1 | /* 页脚与头图透明 */ |
在主题配置文件[BlogRoot]_config.butterfly.yml文件中的inject配置项的head子项加入以下代码,代表引入刚刚创建的custom.css
1 | inject: |
所有的header和footer图都被取消,仅需设置_config.butterfly.yml的background便可达成一图流效果,我这里注释掉了白天模式遮罩透明,背景过亮会导致导航栏难以辨认,根据实际情况酌情调整。
首页分类磁贴
在博客根目录下运行以下命令,安装插件
1 | npm install hexo-butterfly-categories-card --save |
添加配置信息,以下为写法示例
在站点配置文件_config.yml或者主题配置文件_config.butterfly.yml中添加以下代码,注意要根据他的默认描述排序改为你自己对应的分类名字:
1 | # hexo-butterfly-categories-card |
文章置顶滚动栏
在博客根目录下运行以下命令,安装插件
1 | npm install hexo-butterfly-swiper --save |
在站点配置文件_config.yml或者主题配置文件_config.butterfly.yml中添加
1 | # hexo-butterfly-swiper |
使用需要在文章front_matter中添加swiper_index配置项即可。
wowjs动画
在博客根目录下运行以下命令,安装插件
1 | npm install hexo-butterfly-wowjs --save |
在站点配置文件_config.yml或者主题配置文件_config.butterfly.yml中添加
1 | wowjs: |
星空背景和流星特效
在[BlogRoot]/source/js目录下新建universe.js,输入以下代码:
1 | function dark() {window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame;var n,e,i,h,t=.05,s=document.getElementById("universe"),o=!0,a="180,184,240",r="226,225,142",d="226,225,224",c=[];function f(){n=window.innerWidth,e=window.innerHeight,i=.216*n,s.setAttribute("width",n),s.setAttribute("height",e)}function u(){h.clearRect(0,0,n,e);for(var t=c.length,i=0;i<t;i++){var s=c[i];s.move(),s.fadeIn(),s.fadeOut(),s.draw()}}function y(){this.reset=function(){this.giant=m(3),this.comet=!this.giant&&!o&&m(10),this.x=l(0,n-10),this.y=l(0,e),this.r=l(1.1,2.6),this.dx=l(t,6*t)+(this.comet+1-1)*t*l(50,120)+2*t,this.dy=-l(t,6*t)-(this.comet+1-1)*t*l(50,120),this.fadingOut=null,this.fadingIn=!0,this.opacity=0,this.opacityTresh=l(.2,1-.4*(this.comet+1-1)),this.do=l(5e-4,.002)+.001*(this.comet+1-1)},this.fadeIn=function(){this.fadingIn&&(this.fadingIn=!(this.opacity>this.opacityTresh),this.opacity+=this.do)},this.fadeOut=function(){this.fadingOut&&(this.fadingOut=!(this.opacity<0),this.opacity-=this.do/2,(this.x>n||this.y<0)&&(this.fadingOut=!1,this.reset()))},this.draw=function(){if(h.beginPath(),this.giant)h.fillStyle="rgba("+a+","+this.opacity+")",h.arc(this.x,this.y,2,0,2*Math.PI,!1);else if(this.comet){h.fillStyle="rgba("+d+","+this.opacity+")",h.arc(this.x,this.y,1.5,0,2*Math.PI,!1);for(var t=0;t<30;t++)h.fillStyle="rgba("+d+","+(this.opacity-this.opacity/20*t)+")",h.rect(this.x-this.dx/4*t,this.y-this.dy/4*t-2,2,2),h.fill()}else h.fillStyle="rgba("+r+","+this.opacity+")",h.rect(this.x,this.y,this.r,this.r);h.closePath(),h.fill()},this.move=function(){this.x+=this.dx,this.y+=this.dy,!1===this.fadingOut&&this.reset(),(this.x>n-n/4||this.y<0)&&(this.fadingOut=!0)},setTimeout(function(){o=!1},50)}function m(t){return Math.floor(1e3*Math.random())+1<10*t}function l(t,i){return Math.random()*(i-t)+t}f(),window.addEventListener("resize",f,!1),function(){h=s.getContext("2d");for(var t=0;t<i;t++)c[t]=new y,c[t].reset();u()}(),function t(){document.getElementsByTagName('html')[0].getAttribute('data-theme')=='dark'&&u(),window.requestAnimationFrame(t)}()}; |
在[BlogRoot]/source/css目录下新建universe.css,输入以下代码:
1 | /* 背景宇宙星光 */ |
在主题配置文件_config.butterfly.yml的inject配置项中head下填入:
1 | inject: |
在主题配置文件_config.butterfly.yml的inject配置项中bottom下填入:
1 | inject: |
个人卡片渐变色
在[BlogRoot]\source\css\custom.css自定义样式的文件中引入如下代码(最后记得在inject配置项引入!!!):
1 | /* 侧边栏个人信息卡片动态渐变色 */ |
B站视频适配
在source\css\custom.css中插入以下代码
1 | /*哔哩哔哩视频适配*/ |
在themes\butterfly\scripts\tag中创建bilibili.js文件,粘贴如下代码
1 | /** |
要启用效果,有两种方式:
方式一:使用新的bilibili标签(推荐)
1 | {% bilibili BV1xx411c7mD %} # 使用BV号 |
方式二:传统iframe方式
1 | <div align=center class="aspect-ratio"> |
演示一下
Vue+Element样式弹窗
原教程的cdn源国内可能没法直接访问,此处改为jsdelivr
- 在主题配置文件
\_config.butterfly.yml中,引入Vue和Element相关依赖:1
2
3
4
5
6
7
8inject:
head:
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/theme-chalk/index.css">
bottom:
- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.min.js"></script>
- <script src="https://cdn.jsdelivr.net/npm/element-ui@2.15.6/lib/index.js"></script>
- <script defer src="/js/notification.js"></script>
- <script defer src="/js/test-notification.js"></script> - 在
source\js中创建notification.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// 检测F12开发者工具和复制操作
let devtools = {
open: false,
orientation: null
};
// 检测F12开发者工具
function detectDevTools() {
const threshold = 160;
const widthThreshold = window.outerWidth - window.innerWidth > threshold;
const heightThreshold = window.outerHeight - window.innerHeight > threshold;
if (widthThreshold || heightThreshold) {
if (!devtools.open) {
devtools.open = true;
devtools.orientation = widthThreshold ? 'vertical' : 'horizontal';
showF12Notification('你已被发现😜', '小伙子,扒源记住要遵循GPL协议!');
}
} else {
devtools.open = false;
devtools.orientation = null;
}
}
// 检测复制操作
function detectCopy() {
showCopyNotification('复制成功🙌', '发现一个cv大师😀');
}
// 显示通知弹窗
function showF12Notification(title, description) {
if (typeof Vue !== 'undefined' && typeof ELEMENT !== 'undefined' && Vue.prototype.$notify) {
Vue.prototype.$notify({
title: title,
message: description,
position: 'top-left',
offset: 50,
showClose: true,
type: "warning",
duration: 5000
});
} else {
setTimeout(function() {
showF12Notification(title, description);
}, 1000);
}
}
function showCopyNotification(title, description) {
if (typeof Vue !== 'undefined' && typeof ELEMENT !== 'undefined' && Vue.prototype.$notify) {
Vue.prototype.$notify({
title: title,
message: description,
position: 'top-left',
offset: 50,
showClose: true,
type: "success",
duration: 5000
});
} else {
setTimeout(function() {
showCopyNotification(title, description);
}, 1000);
}
}
// 等待页面加载完成后初始化检测
document.addEventListener('DOMContentLoaded', function() {
console.log('页面加载完成,开始初始化检测'); // 调试日志
// 监听窗口大小变化(检测F12)
window.addEventListener('resize', detectDevTools);
// 监听复制事件
document.addEventListener('copy', detectCopy);
// 监听键盘事件(检测F12快捷键)
document.addEventListener('keydown', function(e) {
if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && e.key === 'I') || (e.ctrlKey && e.shiftKey && e.key === 'J') || (e.ctrlKey && e.key === 'U')) {
console.log('检测到开发者工具快捷键'); // 调试日志
setTimeout(function() {
detectDevTools();
}, 100);
}
});
// 定期检测开发者工具
setInterval(detectDevTools, 1000);
// 初始检测
detectDevTools();
// 测试弹窗功能(5秒后自动触发一次测试)
setTimeout(function() {
console.log('测试弹窗功能'); // 调试日志
showNotification('测试弹窗', '如果您看到这个弹窗,说明Vue和Element UI已正常工作!');
}, 5000);
});
我保留了大部分测试用代码,如果弹窗不能正确加载,F12切换到控制台看看日志,没有问题就可以把测试的代码都删了
notify:弹窗类型,可以替换为message(信息提示)和confirm(二次确认提示)title:弹窗标题,可以改为自定义标题message:弹窗信息,可以改为自定义内容position:弹出位置,bottom、top和left、right两两组合offset:偏移量,简单可以理解为与边界的距离showClose:是否显示关闭按钮type:提示类型,可选success/warning/info/error等duration:停留时间,弹出停留至消失的时间,单位ms
夜间模式切换动画
- 新建[BlogRoot]\themes\butterfly\layout\includes\custom\sun_moon.pug,这部分其实实质上就是一个svg文件,通过js操作它的旋转显隐,淡入淡出实现动画效果。
1 | svg(aria-hidden='true', style='position:absolute; overflow:hidden; width:0; height:0') |
- 新建[BlogRoot]\themes\butterfly\source\css_layout\sun_moon.styl
1 | .Cuteen_DarkSky, |
- 新建[BlogRoot]\themes\butterfly\source\js\sun_moon.js,去除了冗余代码,去jquery
1 | function switchNightMode() { |
- 修改[BlogRoot]\themes\butterfly\layout\includes\head.pug,在文件末位加上一行
1 | //- global config |
- 修改[BlogRoot]\themes\butterfly\layout\includes\rightside.pug,把原本的昼夜切换按钮替换掉
1 | when 'translate' |
- 修改[BlogRoot]_config.butterfly.yml,引入一下js
1 | inject: |
欢迎信息显示地理位置
- 获取API Key:进入腾讯位置服务应用管理界面,点击创建应用,应用名称和类型随便填。在新创建的应用中点击添加key,产品选择WebServiceAPI,域名白名单填自己的域名或不填。把得到的key记下。如果开启白名单记得把localhost也加上
首页文章三栏
- 修改
[BlogRoot]\themes\butterfly\layout\includes\mixins\post-ui.pug,整个替换为下面的代码,注意,我这里用的是彩色的图标,每个//- i.fas那里表示我注释了黑白的额图标并换上彩色图标,彩色图标引入的具体方法见之前的教程,这里只需要替换成你自己的图标名字和调节相应的大小即可:
1 | mixin postUI(posts) |
- 样式方案提供两种:
- 样式一:电脑端宽屏采用滑动卡片,平板宽度采用双栏布局,手机宽度采用单栏卡片
- 样式二:移除滑动卡片,按屏幕宽度依次应用三栏、双栏、单栏
新建目录[BlogRoot]\themes\butterfly\source\css\_index_card_style\,并在下面新建对应的文件slidecard.styl和multicard.styl并分别填入以下内容,第一个滑动卡片的是店长原版的,我微调一下第二个的样式,大家可以根据自己的选择进行修改:
1 | //default color: |
1 | :root |
- 修改
[BlogRoot]\themes\butterfly\source\css\_page\homepage.styl,将整文件内容替换为以下代码:
1 | if hexo-config('index_card_style') == 'slidecard' |
- 然后在主题配置文件
[BlogRoot]\_config.butterfly.yml里新增配置项,这样我们就可以通过配置项自由切换使用哪款了:
1 | # 主页卡片样式 |
- 考虑到不管是样式一还是样式二都存在一个布局突变的情况。为了不至于让首页的文章出现空缺,建议将首页生成的文章数量控制为1,2,3的公倍数。修改站点配置文件
[BlogRoot]\_config.yml。找到以下配置项进行调整,注意这是站点配置文件本就有的配置项,不是新增配置项。建议是调整为12篇。如果你的侧边栏魔改内容特别多,那么建议改成18、24、30。务必确保文章卡片栏比侧栏完全展开要长,这样展示效果最好
1 | # Home page setting |
首页文章三栏重制版
修改
[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
147mixin 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样式方案提供两种:
- 样式一:电脑端宽屏采用滑动卡片,平板宽度采用双栏布局,手机宽度采用单栏卡片
- 样式二:移除滑动卡片,按屏幕宽度依次应用三栏、双栏、单栏
新建目录[BlogRoot]\themes\butterfly\source\css\_index_card_style\,并在下面新建对应的文件slidecard.styl和multicard.styl并分别填入以下内容,第一个滑动卡片的是店长原版的,我微调一下第二个的样式,大家可以根据自己的选择进行修改:
1 | //default color: |
1 | //default color: |
修改[BlogRoot]\themes\butterfly\source\css_page\homepage.styl,将整文件内容替换为以下代码:
1
2
3
4if hexo-config('index_card_style') == 'slidecard'
@import './_index_card_style/slidecard'
else if hexo-config('index_card_style') == 'multicard'
@import './_index_card_style/multicard'然后在主题配置文件[BlogRoot]_config.butterfly.yml里新增配置项,这样我们就可以通过配置项自由切换使用哪款了:
1
2
3# 主页卡片样式
# Docs: https://akilar.top/posts/d6b69c49/
index_card_style: multicard # slidecard | multicard考虑到不管是样式一还是样式二都存在一个布局突变的情况。为了不至于让首页的文章出现空缺,建议将首页生成的文章数量控制为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: 12
order_by: -date本教程讨论的卡片都是考虑有封面和有描述的。所以需要保证你已经开启了相应的配置,查看主题配置文件[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





