Hexo-Fluid 功能拓展--banner随机化显示

Hexo-Fluid 功能拓展—banner随机化显示

由于原有指定图片显示功能过于呆板,笔者在原有主题基础上进行了随机化显示拓展,主要改动参考下文。如不需要了解细节部分但希望实现类似效果的读者直接复制代码到对应文件即可,你也可以在我的基础上继续探索类似的功能。


global hexo

Hexo 是一个基于 Node.js 的静态博客框架,它的插件系统大量使用事件驱动(EventEmitter)模式。hexo.on()方法就是用来监听 Hexo 在运行过程中触发的特定事件,当对应的事件发生时,就会执行你传入的回调函数。

generateBefore 是 Hexo 内置的核心事件之一。这个事件会在 Hexo 开始生成静态文件(执行hexo generatehexo g命令)的前一刻被触发。

themes\fluid\scripts\events\index.jshexo.on('generateBefore', () => { ... }代码中添加require('./lib/random-banner')(hexo);,确保随机化图片选择在实际生成页面之前完成。

修改后的完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* global hexo */

'use strict';

hexo.on('generateBefore', () => {
require('./lib/merge-configs')(hexo);
require('./lib/random-banner')(hexo);
require('./lib/compatible-configs')(hexo);
require('./lib/injects')(hexo);
require('./lib/highlight')(hexo);
require('./lib/lazyload')(hexo);
require('./lib/footnote')(hexo);
});

hexo.on('generateAfter', () => {
require('./lib/hello')(hexo);
});

random-banner

themes\fluid\scripts\events\lib目录下新建一个名为 random-banner.js 的文件。

它在 Hexo Fluid 主题中用于自动配置随机banner的功能模块,会根据指定目录下的图片自动生成随机横幅的图片路径列表。

核心逻辑是:当用户开启了 Fluid 主题的“随机banner”功能,且未手动指定固定图片时,自动扫描主题指定目录下的图片文件,生成可随机调用的图片路径列表,供主题在渲染文章时随机选取横幅图片使用。

1. 基础准备(模块引入与常量定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';
const fs = require('fs'); //读写目录/文件
const path = require('path'); //拼接/解析文件路径

//支持的横幅图片格式集合
const IMAGE_EXTS = new Set([
'.png',
'.jpg',
'.jpeg',
'.gif',
'.webp',
'.avif'
]);

2. 核心逻辑(导出函数)

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
module.exports = (hexo) => { //导出函数,接收hexo实例(Hexo插件标准写法)
//获取Fluid主题配置
const themeConfig = hexo.theme && hexo.theme.config;
if (!themeConfig || !themeConfig.post) {
return;
}

const randomEnabled = themeConfig.banner && typeof themeConfig.banner.random_img === 'boolean'
? themeConfig.banner.random_img
: false;
if (!randomEnabled) {
return;
}

//定位随机横幅图片的存放目录
//路径拼接:主题目录/source/img/random(如没有该路径需要添加)
const randomDir = path.join(hexo.theme_dir, 'source', 'img', 'random');
if (!fs.existsSync(randomDir)) {
hexo.log.warn(`[Fluid] Random banner directory not found: ${randomDir}`);
return;
}

const files = fs.readdirSync(randomDir)
.filter((file) => IMAGE_EXTS.has(path.extname(file).toLowerCase()));

if (files.length === 0) {
hexo.log.warn('[Fluid] Random banner directory is empty.');
return;
}

//将文件名转换为Hexo可访问的URL路径(/img/random/文件名)
themeConfig.banner_img_list = files.map((file) => `/img/random/${file}`);
hexo.log.debug(`[Fluid] Random banner images loaded: ${themeConfig.banner_img_list.length}`);
};

完整代码如下:

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
'use strict';

const fs = require('fs');
const path = require('path');

const IMAGE_EXTS = new Set([
'.png',
'.jpg',
'.jpeg',
'.gif',
'.webp',
'.avif'
]);

module.exports = (hexo) => {
const themeConfig = hexo.theme && hexo.theme.config;
if (!themeConfig || !themeConfig.post) {
return;
}

const randomEnabled = themeConfig.banner && typeof themeConfig.banner.random_img === 'boolean'
? themeConfig.banner.random_img
: false;
if (!randomEnabled) {
return;
}

const randomDir = path.join(hexo.theme_dir, 'source', 'img', 'random');
if (!fs.existsSync(randomDir)) {
hexo.log.warn(`[Fluid] Random banner directory not found: ${randomDir}`);
return;
}

const files = fs.readdirSync(randomDir)
.filter((file) => IMAGE_EXTS.has(path.extname(file).toLowerCase()));

if (files.length === 0) {
hexo.log.warn('[Fluid] Random banner directory is empty.');
return;
}

themeConfig.banner_img_list = files.map((file) => `/img/random/${file}`);
hexo.log.debug(`[Fluid] Random banner images loaded: ${themeConfig.banner_img_list.length}`);
};

themes\fluid\layout\_partials\header\banner.ejs是Hexo Fluid 主题中渲染页面横幅的模板代码,我们需要做针对性修改以适配我们的拓展功能。

1. 随机横幅列表初始化与默认值设置

1
2
3
4
5
6
7
var random_banner_list = []
if (!banner_img && random_enabled && Array.isArray(theme.banner_img_list) && theme.banner_img_list.length > 0) {
for (var i = 0; i < theme.banner_img_list.length; i++) {
random_banner_list.push(url_for(theme.banner_img_list[i]))
}
banner_img = random_banner_list[0]
}

2. data-random-banner自定义属性(模板与 JS 通信)

1
2
3
4
5
6
7
8
9
10
11
12
13
var random_banner_attr = ''
if (random_banner_list.length > 0) {
random_banner_attr = "data-random-banner='" + random_banner_list.join(',') + "'"
}
var banner_attrs = ''
//原有parallax属性处理
if (theme.banner && theme.banner.parallax) {
banner_attrs += ' parallax=true'
}
//新增拼接随机横幅属性
if (random_banner_attr) {
banner_attrs += ' ' + random_banner_attr
}

3. 横幅标签新增属性(DOM 层修改)

1
2
<div id="banner" class="banner"<%- banner_attrs %>
style="background: url('<%- url_for(banner_img) %>') no-repeat center center; background-size: cover;">

4. 前端 JS 随机选取逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<% if (random_banner_list.length > 0) { %>
<script>
(function() {
var banner = document.getElementById('banner');
if (!banner) return;
var list = banner.getAttribute('data-random-banner');
if (!list) return;
list = list.split(',').filter(Boolean);
if (list.length === 0) return;
//随机选取一个图片路径
var pick = list[Math.floor(Math.random() * list.length)];
banner.style.backgroundImage = "url('" + pick + "')";
})();
</script>
<% } %>

作用:

  • 仅当随机图片列表非空时,才插入这段自执行JS;

逻辑拆解:

  • 获取banner DOM元素,做前置校验;
  • 读取data-random-banner属性值,拆分为数组并过滤空值;
  • 通过Math.random()生成0到列表长度-1的随机索引,选取一张图片;
  • 覆盖bannerbackgroundImage样式,最终实现随机显示图片。

完整代码如下:

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
<%
var banner_img = page.banner_img || (is_post() && theme.post ? theme.post.banner_img : theme.index.banner_img)
var random_enabled = theme.banner && typeof theme.banner.random_img === 'boolean'
? theme.banner.random_img
: false
var random_banner_list = []
if (!banner_img && random_enabled && Array.isArray(theme.banner_img_list) && theme.banner_img_list.length > 0) {
for (var i = 0; i < theme.banner_img_list.length; i++) {
random_banner_list.push(url_for(theme.banner_img_list[i]))
}
banner_img = random_banner_list[0]
}
var random_banner_attr = ''
if (random_banner_list.length > 0) {
random_banner_attr = "data-random-banner='" + random_banner_list.join(',') + "'"
}
var banner_attrs = ''
if (theme.banner && theme.banner.parallax) {
banner_attrs += ' parallax=true'
}
if (random_banner_attr) {
banner_attrs += ' ' + random_banner_attr
}
var banner_img_height = parseFloat(page.banner_img_height || theme.index.banner_img_height)
var banner_mask_alpha = parseFloat(page.banner_mask_alpha || theme.index.banner_mask_alpha)
var subtitle = page.subtitle || page.title
%>

<div id="banner" class="banner"<%- banner_attrs %>
style="background: url('<%- url_for(banner_img) %>') no-repeat center center; background-size: cover;">
<div class="full-bg-img">
<div class="mask flex-center" style="background-color: rgba(0, 0, 0, <%= banner_mask_alpha %>)">
<div class="banner-text text-center fade-in-up">
<div class="h2">
<% if(theme.fun_features.typing.enable && in_scope(theme.fun_features.typing.scope)) { %>
<span id="subtitle" data-typed-text="<%= subtitle %>"></span>
<% } else { %>
<span id="subtitle"><%- subtitle %></span>
<% } %>
</div>

<% if (is_post()) { %>
<%- inject_point('postMetaTop') %>
<% } %>
</div>

<% if (theme.scroll_down_arrow.enable && theme.scroll_down_arrow.banner_height_limit <= banner_img_height && page.layout !== '404') { %>
<div class="scroll-down-bar">
<i class="iconfont icon-arrowdown"></i>
</div>
<% } %>
</div>
</div>
</div>

<% if (random_banner_list.length > 0) { %>
<script>
(function() {
var banner = document.getElementById('banner');
if (!banner) return;
var list = banner.getAttribute('data-random-banner');
if (!list) return;
list = list.split(',').filter(Boolean);
if (list.length === 0) return;
var pick = list[Math.floor(Math.random() * list.length)];
banner.style.backgroundImage = "url('" + pick + "')";
})();
</script>
<% } %>

相关配置

random文件夹

themes\fluid\source\img目录下创建名为random的文件夹,并将图片放入其中。

配置文件

_config.fluid.yml文件中添加以下配置:

1
2
3
4
5
6

banner:
# 是否开启所有页面的随机头图(仅在对应 banner_img 为空时生效)
# Enable random banner for all pages (only works when banner_img is empty)
random_img: true

如果你还没有执行Hexo Fluid 覆盖配置的操作,你需要修改的文件应该是themes\fluid\_config.yml


希望这能帮助你实现所需的功能!如果你有任何问题,请随时提问。


Hexo-Fluid 功能拓展--banner随机化显示
http://ruak.github.io/2026/01/21/Hexo-Fluid-功能拓展-banner随机化显示/
作者
HUANGDAN
发布于
2026年1月21日
许可协议