Fluid主题背景图全屏化与切换

对Hexo+Fluid博客进行一些样式改造,主要包括以下三个方面:

  1. 固定背景图
  2. 切换颜色主题切换不同背景图
  3. 文字主板毛玻璃效果

参考文章:

1. 固定背景图

使用Fluid主题的**注入代码**功能,实现无代码侵入地为博客添加样式:

  • 在博客根目录下新建一个scripts文件夹,再在这个文件夹中新建一个injector.js文件:
1
2
3
4
5
blog_root/--
|
|--scripts/--
|
|--injector.js
  • 编写injector.js内容:
1
2
3
4
// 注入背景容器
const { root: siteRoot = "/" } = hexo.config;
hexo.extend.injector.register("body_begin", `<div id="web_bg"></div>`);
hexo.extend.injector.register("body_end",`<script src="${siteRoot}js/backgroundize.js"></script>`);

这两步就能够实现固定背景图的功能了。

由于是使用了注入代码功能,上面这个JS文件不用像在source/js/下的js文件那样,还需在主题配置文件_config.fluid.yml中加入- /js/name.js引用。

2.切换颜色主题自动更换背景图

这步我是参照4rozeNfluid 全屏背景图随日夜模式切换和正文底页毛玻璃效果进行实现的,(也可能是我自己在照做的过程中与原文有出入),我完全按照原文操作之后发现并没有达到预期的效果,点击切换颜色主题之后背景图并没有随着发生变化。

在原来的基础上进行一些改动之后能实现预期效果了,并在原来的基础上进行功能改进:

  • 改进功能原来只能实现一个颜色主题只能使用一张背景,这里进一步实现了不同颜色主题的不同页面的可以设置不同的背景图片。
  • 缺点:增加了代码量,会拖慢速度。

具体实现

(1)在 source/js目录中新建背景修改 backgroundize.js 文件,我的代码内容如下(没有添加代码折叠,所以会比较占地方):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/**
* Fluid主题日夜模式和页面类型背景切换
*/

// 定义不同页面类型和主题模式下的背景图片
const BACKGROUNDS = {
home: { light: {}, dark: {} },
post: { light: {}, dark: {} },
archive: { light: {}, dark: {} },
category: { light: {}, dark: {} },
tag: { light: {}, dark: {} },
about: { light: {}, dark: {} },
links: { light: {}, dark: {} },
default: { light: {}, dark: {} }
};

// 为每个配置添加桌面和移动设备属性
for (const page in BACKGROUNDS) {
BACKGROUNDS[page].light.desktop = `var(--${page}-bg-image-light)`;
BACKGROUNDS[page].light.mobile = `var(--${page}-bg-image-light-mobile)`;
BACKGROUNDS[page].dark.desktop = `var(--${page}-bg-image-dark)`;
BACKGROUNDS[page].dark.mobile = `var(--${page}-bg-image-dark-mobile)`;
}

// 缓存DOM元素和状态
let cachedThemeMode = null;
let cachedPageType = null;
let cachedIsMobile = null;
let webBgElement = null;
let updateTimeout = null;

/**
* 检测当前的主题模式
*/
function detectThemeMode() {
const storedTheme = localStorage.getItem('Fluid_Color_Scheme');
const isDarkClass = document.body.classList.contains('dark-mode') ||
document.body.classList.contains('dark') ||
document.documentElement.classList.contains('dark');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

return (storedTheme === 'dark' || (storedTheme === null && isDarkClass) ||
(storedTheme === null && !isDarkClass && prefersDark)) ? 'dark' : 'light';
}

/**
* 检测当前页面类型
*/
function detectPageType() {
const path = window.location.pathname;
const bodyClasses = document.body.classList;

// 基于URL路径检测
if (path === '/' || path === '/index.html') return 'home';
if (path.includes('/post/') || path.includes('/posts/') || path.includes('/article/')) return 'post';
if (path.includes('/archives/') || path.includes('/archive/')) return 'archive';
if (path.includes('/categories/') || path.includes('/category/')) return 'category';
if (path.includes('/tags/') || path.includes('/tag/')) return 'tag';
if (path.includes('/about/')) return 'about';
if (path.includes('/links/')) return 'links';

// 基于body类名检测
if (bodyClasses.contains('index')) return 'home';
if (bodyClasses.contains('post-template') || bodyClasses.contains('post')) return 'post';
if (bodyClasses.contains('archive-template') || bodyClasses.contains('archive')) return 'archive';
if (bodyClasses.contains('category-template') || bodyClasses.contains('category')) return 'category';
if (bodyClasses.contains('tag-template') || bodyClasses.contains('tag')) return 'tag';
if (bodyClasses.contains('about-template') || bodyClasses.contains('about')) return 'about';
if (bodyClasses.contains('links-template') || bodyClasses.contains('links')) return 'links';

// 特定页面元素检测
if (document.querySelector('.post-content')) return 'post';
if (document.querySelector('.archive-list')) return 'archive';
if (document.querySelector('.category-list')) return 'category';
if (document.querySelector('.tag-list')) return 'tag';
if (document.querySelector('.about-content')) return 'about';
if (document.querySelector('.links-list')) return 'links';

return 'default';
}

/**
* 设置背景图片 - 只在需要时更新
*/
function updateBackgroundImage() {
// 防止短时间内多次调用
if (updateTimeout) {
clearTimeout(updateTimeout);
}

updateTimeout = setTimeout(() => {
// 获取当前状态
const themeMode = detectThemeMode();
const pageType = detectPageType();
const isMobile = window.innerWidth < 768;

// 如果DOM元素还未缓存,获取它
if (!webBgElement) {
webBgElement = document.querySelector('#web_bg');
if (!webBgElement) return;
}

// 当状态变化时才更新背景
if (themeMode !== cachedThemeMode ||
pageType !== cachedPageType ||
isMobile !== cachedIsMobile) {

// 更新缓存的状态
cachedThemeMode = themeMode;
cachedPageType = pageType;
cachedIsMobile = isMobile;

// 获取对应背景
const bgConfig = BACKGROUNDS[pageType] || BACKGROUNDS.default;
const bgImage = isMobile ? bgConfig[themeMode].mobile : bgConfig[themeMode].desktop;

// 应用背景图片
webBgElement.style.backgroundImage = bgImage;

// 重置Banner样式
const banner = document.querySelector("#banner");
const mask = document.querySelector("#banner .mask");

if (banner) banner.style.backgroundImage = 'none';
if (mask) mask.style.backgroundColor = 'rgba(0,0,0,0)';
}

updateTimeout = null;
}, 50);
}

/**
* 初始化
*/
function init() {
// 初始化背景
updateBackgroundImage();

// 设置主题切换监听器
const themeToggleBtn = document.querySelector('#color-toggle-btn');
if (themeToggleBtn) {
themeToggleBtn.addEventListener('click', updateBackgroundImage);
}

// localStorage变化
window.addEventListener('storage', (event) => {
if (event.key === 'Fluid_Color_Scheme') {
updateBackgroundImage();
}
});

// body类变化
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.attributeName === 'class') {
updateBackgroundImage();
break;
}
}
});

observer.observe(document.body, { attributes: true });
observer.observe(document.documentElement, { attributes: true });

// 系统主题变化
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', updateBackgroundImage);

// 窗口大小变化
window.addEventListener('resize', updateBackgroundImage, { passive: true });

// 监听点击事件,捕获内部导航
document.addEventListener('click', (event) => {
const target = event.target.closest('a');
if (target && target.href && target.href.includes(window.location.hostname)) {
setTimeout(updateBackgroundImage, 100);
}
});

// 浏览器历史记录变化
window.addEventListener('popstate', () => {
setTimeout(updateBackgroundImage, 100);
});

// 观察内容变化
const contentObserver = new MutationObserver((mutations) => {
if (mutations.some(mutation =>
mutation.type === 'childList' &&
(mutation.addedNodes.length > 3 || mutation.removedNodes.length > 3)
)) {
setTimeout(updateBackgroundImage, 100);
}
});

// 观察主要内容区域
const contentAreas = [
document.querySelector('main'),
document.querySelector('#main'),
document.querySelector('.container'),
document.querySelector('.content')
].filter(Boolean);

contentAreas.forEach(area => {
contentObserver.observe(area, {
childList: true,
subtree: true
});
});
}

// 页面初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}

// 提供手动触发更新的方法
window.updatePageBackground = function() {
updateBackgroundImage();
return `已手动更新背景,页面类型: ${detectPageType()}, 主题模式: ${detectThemeMode()}`;
};

(2)添加CSS文件:source/css/backgroundize.css,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/* 参考教程:https: //4rozen.github.io/archives/Hexo/60191.html */
:root {
/* 首页背景 */
--home-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--home-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--home-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--home-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');

/* 文章页背景 */
--post-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/free-cute.png');
--post-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--post-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/m013.jpg');
--post-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');

/* 归档页背景 */
--archive-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--archive-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--archive-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--archive-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');

/* 标签页背景 */
--tag-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--tag-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--tag-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--tag-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');

/* 分类页背景 */
--category-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--category-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--category-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--category-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');

/* 关于页背景 */
--about-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--about-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--about-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--about-bg-image-dark-mobile: url('https://cbc25ff.web.li/hua-cdn/b08.jpg');

/* 友链页背景 */
--links-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/LongMao.webp');
--links-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/LongMao_m.jpg');
--links-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--links-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/LongMao_m.jpg');

/* 默认背景(可以重复使用其他背景,或设置新背景) */
--default-bg-image-light: url('https://cbc25ff.webp.li/hua-cdn/street01.png');
--default-bg-image-light-mobile: url('https://cbc25ff.webp.li/hua-cdn/m0.jpg');
--default-bg-image-dark: url('https://cbc25ff.webp.li/hua-cdn/b2.jpg');
--default-bg-image-dark-mobile: url('https://cbc25ff.webp.li/hua-cdn/b08.jpg');
}

#web_bg {
/* 触发GPU加速 */
will-change: background-image;
/* 启用CSS Containment */
content-visibility: auto;
background-image: var(--desktop-bg-image-normal);
position: fixed;
width: 100%;
height: 100%;
z-index: -1;
background-size: cover;
/* 添加过渡效果 */
transition: all 0.5s;
}

注意:

这里的backgroundzie.jsbackgroundize.css要在「主题配置文件_config.fluid.yml」中添加到用户自定义js和用户自定义css中进行启用。

1
2
3
4
5
custom_js:
- /js/backgroundize

custom_css:
- /css/backgroundize

篇结。