给安知鱼主题添加宠物挂件

给博客顶部和底部各添加一排宠物,是一种非常酷的效果,经过多方查找,终于在安知鱼主题下通过修改主题文件,进行实现,下面来看具体的操作过程。

1.顶部挂件

主要设置页面左上角随机动物挂件

image

1.1代码修改

仅针对安知鱼主题,其他主题自行修改,可直接替换

1
blog/themes/anzhiyu/layout/includes/bbTimeList.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
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
if site.data.essay
each i in site.data.essay
if i.home_essay
- let onclick_value = theme.pjax.enable ? `pjax.loadUrl("/essay/");` : '';
- let href_value = theme.pjax.enable ? 'javascript:void(0);' : `/essay/`;
#bbTimeList.bbTimeList.container
i.anzhiyufont.anzhiyu-icon-jike.bber-logo.fontbold(onclick=onclick_value, title="即刻短文", href=href_value, aria-hidden="true")
#bbtalk.swiper-container.swiper-no-swiping.essay_bar_swiper_container(tabindex="-1")
#bber-talk.swiper-wrapper(onclick=onclick_value)
each i in site.data.essay
each item, index in i.essay_list
if index < 10
- var contentText = item.image ? item.content + ' [图片]' : (item.video ? item.content + ' [视频]' : item.content)
a.li-style.swiper-slide(href=href_value)= contentText
a.bber-gotobb.anzhiyufont.anzhiyu-icon-circle-arrow-right(onclick=onclick_value, href=href_value, title="查看全文")
img.con-animals.entered.loaded(id="new-con-animals" src="")
script(src=url_for(theme.home_top.swiper.swiper_js))

style.
#bbTimeList {
position: relative;
}

.con-animals {
display: block;
position: absolute;
max-width: 260px;
top: -85px;
z-index: 2;
}

@media screen and (max-width: 1200px) {
.con-animals {
display: none;
}
}

script.
// 将lastImageUrl移到全局作用域
window.lastImageUrl = window.lastImageUrl || '';

function setRandomImage() {
const img = document.getElementById('new-con-animals');
const imageUrls = [
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/901216.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/074167.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/759434.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/526748.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/429029.webp"
];

let randomImage;
do {
randomImage = imageUrls[Math.floor(Math.random() * imageUrls.length)];
} while (randomImage === window.lastImageUrl);

window.lastImageUrl = randomImage;

if (img) {
img.src = randomImage;
}
}

function initializeDragImage() {
const img = document.getElementById('new-con-animals');
const container = document.getElementById('bbTimeList');

if (!img || !container) return;

if (!window.lastImageUrl) {
setRandomImage();
} else {
img.src = window.lastImageUrl;
}

let isDragging = false, wasDragged = false, startX, startLeft;
const containerWidth = container.clientWidth;
const imgWidth = img.clientWidth;
const maxLeft = containerWidth - imgWidth;
const edgeThreshold = 20;
let lastLeft = parseInt(localStorage.getItem('imgPositionLeft')) || 0;
lastLeft = Math.min(maxLeft, Math.max(0, lastLeft));
img.style.left = `${lastLeft}px`;

const savePosition = (left) => localStorage.setItem('imgPositionLeft', left);

img.addEventListener('click', () => {
if (!wasDragged) {
let currentLeft = lastLeft;
let newLeft;

if (currentLeft <= edgeThreshold) {
newLeft = Math.min(currentLeft + 200, maxLeft);
} else if (currentLeft >= maxLeft - edgeThreshold) {
newLeft = Math.max(currentLeft - 200, 0);
} else {
newLeft = currentLeft + (Math.random() < 0.5 ? -200 : 200);
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
}

if (newLeft !== lastLeft) {
lastLeft = newLeft;
img.style.left = `${newLeft}px`;
savePosition(newLeft);
}
}
});

img.addEventListener('mousedown', (e) => {
isDragging = true;
wasDragged = false;
startX = e.clientX;
startLeft = lastLeft;
img.style.transition = 'none';

const onMouseMove = (e) => {
if (!isDragging) return;
wasDragged = true;
const offsetX = e.clientX - startX;
lastLeft = Math.max(0, Math.min(startLeft + offsetX, maxLeft));
requestAnimationFrame(() => {
img.style.left = `${lastLeft}px`;
});
};

const onMouseUp = () => {
isDragging = false;
img.style.transition = 'left 0.5s ease-in-out';
savePosition(lastLeft);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};

document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}

document.addEventListener('DOMContentLoaded', initializeDragImage);
document.addEventListener('pjax:success', initializeDragImage);

代码修改的地方只有两处,第一处是在源文件的末尾

1
script(src=url_for(theme.home_top.swiper.swiper_js))

上面代码的上一行,也就是倒数第二行,本身就是一个空行,添加

1
img.con-animals.entered.loaded(id="new-con-animals" src="")

注意添加的这一样的缩颈,与上面的代码对齐,是如下样式

1
2
a.bber-gotobb.anzhiyufont.anzhiyu-icon-circle-arrow-right(onclick=onclick_value, href=href_value, title="查看全文")
img.con-animals.entered.loaded(id="new-con-animals" src="")

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
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
style.
#bbTimeList {
position: relative;
}

.con-animals {
display: block;
position: absolute;
max-width: 260px;
top: -85px;
z-index: 2;
}

@media screen and (max-width: 1200px) {
.con-animals {
display: none;
}
}

script.
// 将lastImageUrl移到全局作用域
window.lastImageUrl = window.lastImageUrl || '';

function setRandomImage() {
const img = document.getElementById('new-con-animals');
const imageUrls = [
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/901216.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/074167.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/759434.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/526748.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/429029.webp"
];

let randomImage;
do {
randomImage = imageUrls[Math.floor(Math.random() * imageUrls.length)];
} while (randomImage === window.lastImageUrl);

window.lastImageUrl = randomImage;

if (img) {
img.src = randomImage;
}
}

function initializeDragImage() {
const img = document.getElementById('new-con-animals');
const container = document.getElementById('bbTimeList');

if (!img || !container) return;

if (!window.lastImageUrl) {
setRandomImage();
} else {
img.src = window.lastImageUrl;
}

let isDragging = false, wasDragged = false, startX, startLeft;
const containerWidth = container.clientWidth;
const imgWidth = img.clientWidth;
const maxLeft = containerWidth - imgWidth;
const edgeThreshold = 20;
let lastLeft = parseInt(localStorage.getItem('imgPositionLeft')) || 0;
lastLeft = Math.min(maxLeft, Math.max(0, lastLeft));
img.style.left = `${lastLeft}px`;

const savePosition = (left) => localStorage.setItem('imgPositionLeft', left);

img.addEventListener('click', () => {
if (!wasDragged) {
let currentLeft = lastLeft;
let newLeft;

if (currentLeft <= edgeThreshold) {
newLeft = Math.min(currentLeft + 200, maxLeft);
} else if (currentLeft >= maxLeft - edgeThreshold) {
newLeft = Math.max(currentLeft - 200, 0);
} else {
newLeft = currentLeft + (Math.random() < 0.5 ? -200 : 200);
newLeft = Math.max(0, Math.min(newLeft, maxLeft));
}

if (newLeft !== lastLeft) {
lastLeft = newLeft;
img.style.left = `${newLeft}px`;
savePosition(newLeft);
}
}
});

img.addEventListener('mousedown', (e) => {
isDragging = true;
wasDragged = false;
startX = e.clientX;
startLeft = lastLeft;
img.style.transition = 'none';

const onMouseMove = (e) => {
if (!isDragging) return;
wasDragged = true;
const offsetX = e.clientX - startX;
lastLeft = Math.max(0, Math.min(startLeft + offsetX, maxLeft));
requestAnimationFrame(() => {
img.style.left = `${lastLeft}px`;
});
};

const onMouseUp = () => {
isDragging = false;
img.style.transition = 'left 0.5s ease-in-out';
savePosition(lastLeft);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};

document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
}

document.addEventListener('DOMContentLoaded', initializeDragImage);
document.addEventListener('pjax:success', initializeDragImage);

1.2.两个问题

上面的代码会有两个问题,需要用下面的方法来解决

1.2.1.图片问题

上述代码保存之后,需要注意的是随机图片的设置,原文中

1
2
3
4
5
6
7
const imageUrls = [
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/901216.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/074167.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/759434.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/19/526748.webp",
"https://i1.wp.com/ruom.wuaze.com/i/2024/10/18/429029.webp"
];

图片代码已经失效,需要替换为自己的图片,我的建议是将替换图片放在

1
blog/themes/anzhiyu/source/img

文件夹中,这个文件夹会被 Hexo 拷贝到网站根目录。

1.2.2黑暗模式图片被遮挡

如果你发现夜间模式会被遮挡一部分可以添加下这段 css 试试,暂且不知道会不会影响其它部分

1
2
3
4
5
6
7
8
/* 小动物夜间显示优化 */
[data-theme='dark'] #page-header.nav-fixed #nav {
background: var(--anzhiyu-black)!important;
}

[data-theme='dark'] #page-header #nav {
background: 0 !important;
}

将上面的代码放到

1
2
3
4
style.
#bbTimeList {
position: relative;
}

下面就可以,不过需要调整缩进问题,修改起来问题应该不大。

1.2.3不显示问题

上述代码我只是在安知鱼主图之中进行的测试,如果你是其他主题,仅供参考。另外图片不显示,生成之后可以多刷新两次页面。我的测试只是首页显示,余下的页面貌似也不显示,不清楚问题在哪里。

1.3重新生成

需要重新生成, Hexo 三连(hexo cl && hexo g && hexo s)然后就可以看到效果。

2.页脚挂件

页脚挂件如下所示

image

2.1.开启主题配置footerBar功能

1
2
footerBar:
enable: true

存放路径为:

1
blog/themes/anzhiyu/source/js/footer-animal.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
function initFooterAnimal() {
const footerBar = document.querySelector('#footer-bar');
if (!footerBar) return console.error('找不到指定元素');

const footerAnimal = document.createElement('div');
footerAnimal.id = 'footer-animal';
footerAnimal.innerHTML = `
<img class="animal entered loaded"
src="https://i-blog.csdnimg.cn/img_convert/bcaf63d0b91b5d263b4e55d2e908bbdd.webp"
alt="动物" />
`;

footerBar.insertAdjacentElement('beforebegin', footerAnimal);

const style = document.createElement('style');
style.textContent = `
#footer-animal {
position: relative;
}
#footer-animal::before {
content: '';
position: absolute;
bottom: 0;
width: 100%;
height: 36px;
background: url(https://i-blog.csdnimg.cn/img_convert/bcaf63d0b91b5d263b4e55d2e908bbdd.webp) repeat center / auto 100%;
box-shadow: 0 4px 7px rgba(0,0,0,.15);
}
.animal {
position: relative;
max-width: min(974px, 100vw);
margin: 0 auto;
display: block;
}
#footer-bar {
margin-top: 0 !important;
}
@media screen and (max-width: 1023px) {
#footer-animal::before {
height: 4vw;
}
}
[data-theme=dark] #footer-animal {
filter: brightness(.8);
}
`;
document.head.appendChild(style);
}

document.addEventListener('DOMContentLoaded', initFooterAnimal);
document.addEventListener('pjax:success', initFooterAnimal);

需要注意的一点,文件中调用的图片

1
https://i-blog.csdnimg.cn/img_convert/bcaf63d0b91b5d263b4e55d2e908bbdd.webp

不定时就会不能够访问,大家需要下载合适的图片当做背景图片,并存放在适当的文件夹中,我的建议是将图片存放在

1
blog/themes/anzhiyu/source/img

中,这个文件夹在 Hexo 博客生成过程中,里面的图片都会被拷贝到网站根目录,这样就可以直接调用。

2.3.引入自定义 js

做好上面的 JS 文件之后,在安知鱼主题的配置文件中搜索 inject ,就会找到如下代码

1
2
3
4
5
6
7
8
9
10
11
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
# 插入代码到头部 </head> 之前 和 底部 </body> 之前
inject:
head:
# 自定义css
# - <link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'">

bottom:
# 自定义js
# - <script src="/js/xxx"></script>

bottom 处进行自定义 js 文件的引入工作:

1
- <script src="/js/custom/footer-animal.js" defer></script>

修改之后的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
# Inject
# Insert the code to head (before '</head>' tag) and the bottom (before '</body>' tag)
# 插入代码到头部 </head> 之前 和 底部 </body> 之前
inject:
head:
# 自定义css
# - <link rel="stylesheet" href="/css/custom.css" media="defer" onload="this.media='all'">

bottom:
# 自定义js
# - <script src="/js/xxx"></script>
- <script src="/js/custom/footer-animal.js" defer></script>

3.最后总结

最后 Hexo 三连(hexo cl && hexo g && hexo s)就可以看到文章开始页面顶部的效果。