为hexo博客Fluid主题添加相册功能

引用

  1. 一开始我也觉得,一个教你怎么用的文章,连一个配图都没没有,全是文字,搞毛线?本身对代码又不是特别懂,后来发现,有些东西沉下心来,对文章细嚼慢咽一下,很多看起来困难的事情其实也能迎刃而解的。加上现在 ChatGPT 的帮助,很多不会的都可以问问它,再不济自己 bing 搜索一下也可以的。
  2. 然后在开始前,一定要理清楚路径的关系,同级路径、上一级路径、下一级路径,这个基本的概念,因为我在这里踩了很多次坑。

创建导航菜单

  1. 进入 hexo-theme-fluid 文件夹,找到Fluid的配置文件 _config.yml 注意:这个是Fluid的配置文件。
  2. 按下 Ctrl+F 找到导航菜单条目 menu
  3. 找到导航栏,在第六行后添加一个菜单(顺序自定不一定要在第六行),用于相册的,这是我自己的导航菜单结构样式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
menu:
- { key: "home", link: "/", icon: "iconfont icon-home-fill" }
- { key: "archive", link: "/archives/", icon: "iconfont icon-archive-fill" }
- { key: "category", link: "/categories/", icon: "iconfont icon-category-fill" }
- { key: "tag", link: "/tags/", icon: "iconfont icon-tags-fill" }
- { key: "links", link: "/links/", icon: "iconfont icon-link-fill" }
- { key: "相册", link: "/photo/", icon: "iconfont icon-images" }
- {
key: 'about',
icon: 'iconfont icon-user-fill',
submenu: [
{ key: '此博客', link: '/about/' },
{ key: '本人', link: '/i/' },
]
}
  1. 关于这个菜单的解释:
1
2
3
4
5
6
7
8
9
10
11
12
13
- { key: "相册", link: "/photo/", icon: "iconfont icon-images" }
# key 是自定义显示的名称,如果不考虑多语言的话,其实可以直接把key: "photo"换成key: "相册"就行了
# /phto/ 是定义这个页面为photo,这里也可以写为photos,看喜好了,重点是后面别搞错引用路径,不然要折腾好久的
# icon 是引用图标


### 规范写法 ###
#在themes/hexo-theme-fluid/languages/zh-CN.yml文件中添加下面的内容即可:

photo:
menu: '相册'
title: '相册'
subtitle: '相册'
  1. 然后在Fluid的配置文件中找到 自定义页 更改首页图片为你需要的。

创建相册页面

  1. 手动进入Blog(你的博客) - source 目录下,新建一个 photo 文件夹,这是相册页路径,
  2. 新建一个 index.md 文件,这是相册页面,
  3. 编辑 index.md ,前面的引导配置,不能少一定要有,关于 <Style> 里的代码是相册展示样式,可以自行改,我用的是 HBulder X 编辑这些内容,更方便和专业一些。
  4. 一定要注意这个文件类型 layout: photo 如果你创建的是photos,在后面的 injector.js 也要一并修改,这样layout 为你修改的 photos 的时候才会导入这些 js 与 css ,这里我就用的 photo
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
---
title: 照片墙 v1.0
subtitle: 欢迎来到照片墙
layout: photo
date: 2024-04-1 12:44:07
---

</br>

## 在这里记录我的blender的练习记录和成长脚印。

---



<style>
.ImageGrid {
width: 100%;
max-width: 1040px;
margin: 0 auto;
text-align: center;
}
.card {
overflow: hidden;
transition: .3s ease-in-out;
border-radius: 8px;
background-color: rgba(180,180,180,0.2);
padding: 1.4px;
}
.ImageInCard img {
padding: 0;
border-radius: 8px;
width:100%;
height:100%;
}
@media (prefers-color-scheme: dark) {
.card {background-color: rgba(180,180,180,0.2);}
}
</style>
<div id="imageTab"></div>
<div class="ImageGrid"></div>

图片设置

  1. 目前采用的是本地加载,主要有 Github 和 Gitee 两个位置托管博客,暂不考虑 Github+picgo搭建图床 ,Github搭建的图床对国内没有梯子的用户不友好,当然有 云对象存储COS 最好了,反正我是走白嫖路线,自己的博客目前用不上。
  2. 把你需要展示的相册文件复制到 photo/images- 目录下,用来存放图片,没有就在 photo 文件夹中新建一个 images- 文件夹(photo里新建的文件夹不能有空的文件夹)。这样做的目的是因为博客中有在很多图片是不需要展示到照片墙中的,所以我们要手动筛选出自己喜欢的图片放上去,这样不仅不会额外占用存储空间也更有意义。
  3. hexo注入器的工作原理是渲染后,在将自行添加的代码注入到博客中的,也就是会说你的相册路径要用渲染后的路径位置。

创建phototool.js文件处理图片

  1. 这是一个生成图片大小,名称,参数信息的文件,内容代码如下:
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
//此文件会用到image-size来输出图片的宽高信息和路径,没有需要安装,在这段之后有说明。
//重点需要注意的是phototool.js的路径要放在图片目录中,然后在npm控制台运行它。

const fs = require('fs-extra');
const path = require('path');
const imageSize = require('image-size');

const rootPath="source/photo/" //相册相对路径,将需要的相册放到photo目录,新建一个images文件夹,即可自动扫描并生成photos.json和photosinfo.json文件

class PhotoExtension {
constructor() {
this.size = 64;
this.offset = [0, 0];
}
}

class Photo {
constructor() {
this.dirName = '';
this.fileName = '';
this.iconID = '';
this.extension = new PhotoExtension();
}
}

class PhotoGroup {
constructor() {
this.name = '';
this.children = [];
}
}

function createPlotIconsData() {
let allPlots = [];
let allPlotGroups = [];

const plotJsonFile = path.join(__dirname, './photosInfo.json');
const plotGroupJsonFile = path.join(__dirname, './photos.json');

if (fs.existsSync(plotJsonFile)) {
allPlots = JSON.parse(fs.readFileSync(plotJsonFile));
}

if (fs.existsSync(plotGroupJsonFile)) {
allPlotGroups = JSON.parse(fs.readFileSync(plotGroupJsonFile));
}

fs.readdirSync(__dirname).forEach(function(dirName) {
const stats = fs.statSync(path.join(__dirname, dirName));
const isDir = stats.isDirectory();
if (isDir) {
const subfiles = fs.readdirSync(path.join(__dirname, dirName));
subfiles.forEach(function(subfileName) {
// 如果已经存在 则不再处理
// if (allPlots.find(o => o.fileName === subfileName && o.dirName === dirName)) {
// return;
// }

// 新增标
const plot = new Photo();
plot.dirName = dirName;
plot.fileName = subfileName;
const imageInfo = imageSize(rootPath+dirName + "/" + subfileName);
plot.iconID = imageInfo.width + '.' + imageInfo.height + ' ' + subfileName;
allPlots.push(plot);
console.log(`RD: createPlotIconsData -> new plot`, plot);

// 为新增标添加分组 暂时以它所处的文件夹为分组
let group = allPlotGroups.find(o => o.name === dirName);
if (!group) {
group = new PhotoGroup();
group.name = dirName;
allPlotGroups.push(group);
console.log(`RD: createPlotIconsData -> new group`, group);
}
group.children.push(plot.iconID);
});
}
});

fs.writeJSONSync(plotJsonFile, allPlots);
fs.writeJSONSync(plotGroupJsonFile, allPlotGroups);
}

createPlotIconsData();
  1. 将这个 phototool.js 代码复制到你的 photo 相册目录。

  2. 接下来要生成 json 文件,别紧张,这里并不难,你需要找到你的 Blog 的目录,然后右键选择 Git Bash Here ,这样会弹出一个控制台窗口,这时候控制台就加载到了你当前的 Blog 的目录,这个控制台可以输入命令,你能搭建这个博客相信你对这个控制台也有一定的了解。

  3. 现在需要获取图片尺寸,这个是第三方包,需要手动安装一个,用来获取第三方文件大小包。

    1
    npm install image-size --save
  4. 接下来输入 node source/photo/phototool.js 这样就会运行这个文件,注意哈,你的路径如果改了这里也要改。

    1
    node source/photo/phototool.js
  5. 如果你运行时出现了如下的报错信息的话,可以看一下第7步,没出现错误的话则直接看第7步。

    1
    2
    3
    4
    5
    6
    7
    $ node source/photo/phototool.js
    node:internal/modules/cjs/loader:1078
    throw err;
    ^

    Error: Cannot find module 'fs-extra'
    Require stack:
  6. 这个错误意味着在运行脚本phototool.js的过程中,Node.js 无法找到名为fs-extra的模块。 fs-extra 是一个第三方模块,用于提供比内置的 Node.js fs 模块更多功能。要解决这个问题,你需要确保在你的项目中安装了 fs-extra 模块。你可以使用 npm 包管理器来安装它,比如运行以下命令:

    1
    npm install fs-extra

    现在重新运行 node source/photo/phototool.js 就正常了

  7. 然后,在你的 photo 目录中,就会多出 photos.jsonphotosInfo.json

  8. 这个 photos.json 文件主要是用来记录图片宽高信息,以及图片的展示顺序,如果你按照我的操作新建了一个 images- 文件夹,那么 name 里的名称就和我的一样(name这个是分类名称,可自行修改。),这是我的图片元数据示例,你的图片名称和我的是不一样的,这段无需复制,仅做展示说明:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    [{
    "name": "images-",
    "children": [

    "1280.3630 Watch_detail_page_display.png",
    "1000.1500 Watch_detail_page_display3.png",
    "1000.1500 Watch_detail_page_display4.png",
    "1000.1500 Watch_detail_page_display6.png",
    "1000.1500 Watch_detail_page_display9.png",
    "800.800 暗影头盔_EV.png",
    "1280.540 龙斧_终了2.png",
    "1920.1080 Snipaste_2023-11-30_14_EV_2.png",
    "1920.1080 Snipaste_2023-11-30_13.png",
    "1920.1080 木桶渲染_4.png",
    "1920.1080 Snipaste_2023-11-30_5.png",
    "1920.1080 202310262210568.png",
    "2560.1080 blender_blue_1.png",
    "1080.1352 A03魔法球_渲染.png",
    "540.676 渲染-火焰.png",
    "1920.1012 【8】最终渲染-夜景+台灯.png",
    "1920.1012 【8】最终渲染-夜景.png",
    "1920.1012 【8】最终渲染-白天.png"
    ]
    }]

编写相册js文件

  1. /source/js/ 目录下创建 photoWall.js ,路径一定要对得上哈,千万别搞错了。(没有 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
var imgDataPath = "/photo/photos.json"; //图片名称高宽信息json文件路径,此配置文件可改变图片顺序   //填入你的路径,注意修改
var imgPath = "/photo/images-/"; //图片访问路径 //填入你的路径,注意修改,后期可自行改到博客的img文件路径 /img/images/
// var imgPath = "https://cdn.jsdelivr.net/gh/Cenergy/images/gallery/"; //网络图片访问路径实例
var imgMaxNum = 50; //图片显示数量

var windowWidth =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
if (windowWidth < 768) {
var imageWidth = 145; //图片显示宽度(手机端)
} else {
var imageWidth = 250; //图片显示宽度
}

const photo = {
page: 1,
offset: imgMaxNum,
init: function () {
var that = this;
$.getJSON(imgDataPath, function (data) {
that.render(that.page, data);
//that.scroll(data);
that.eventListen(data);
});
},
constructHtml(options) {
const {
imageWidth,
imageX,
imageY,
name,
imgPath,
imgName,
imgNameWithPattern,
} = options;
const htmlEle = `<div class="card lozad" style="width:${imageWidth}px">
<div class="ImageInCard" style="height:${
(imageWidth * imageY) / imageX
}px">
<a data-fancybox="gallery" href="${imgPath}${imgNameWithPattern}"
data-caption="${imgName}" title="${imgName}">
<img class="lazyload" data-src="${imgPath}${imgNameWithPattern}"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
onload="lzld(this)"
lazyload="auto">
</a>
</div>
<p>${imgName}</p>
</div>`;
// 如果不希望显示图片名称的话删去<p>${imgName}</p>这一行即可。
return htmlEle;
},
render: function (page, data = []) {
this.data = data;
if (!data.length) return;
var html,
imgNameWithPattern,
imgName,
imageSize,
imageX,
imageY,
li = "";

let liHtml = "";
let contentHtml = "";

data.forEach((item, index) => {
const activeClass = index === 0 ? "active" : "";
liHtml += `<li class="nav-item" role="presentation">
<a class="nav-link ${activeClass} photo-tab" id="home-tab" photo-uuid="${item.name}" data-toggle="tab" href="#${item.name}" role="tab" aria-controls="${item.name}" aria-selected="true">${item.name}</a>
</li>`;
});
const [initData = {}] = data;
const { children = [],name } = initData;
children.forEach((item, index) => {
imgNameWithPattern = item.slice(item.indexOf(" ")+1);
imgName = imgNameWithPattern.split("/").pop();
imageSize = item.split(" ")[0];
imageX = imageSize.split(".")[0];
imageY = imageSize.split(".")[1];
let imgOptions = {
imageWidth,
imageX,
imageY,
name,
imgName,
imgPath,
imgNameWithPattern,
};
li += this.constructHtml(imgOptions);
});
contentHtml += ` <div class="tab-pane fade show active" role="tabpanel" aria-labelledby="home-tab">${li}</div>`;

const ulHtml = `<ul class="nav nav-tabs" id="myTab" role="tablist">${liHtml}</ul>`;
const tabContent = `<div class="tab-content" id="myTabContent">${contentHtml}</div>`;

$("#imageTab").append(ulHtml);
$(".ImageGrid").append(tabContent);
this.minigrid();
},
eventListen: function (data) {
let self = this;
var html,
imgNameWithPattern,
imgName,
imageSize,
imageX,
imageY,
li = "";
$('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {
$(".ImageGrid").empty();
const selectId = $(e.target).attr("photo-uuid");
const selectedData = data.find((data) => data.name === selectId) || {};
const { children,name } = selectedData;
let li = "";
children.forEach((item, index) => {
imgNameWithPattern = item.split(" ")[1];
imgName = imgNameWithPattern.split("/").pop();
imageSize = item.split(" ")[0];
imageX = imageSize.split(".")[0];
imageY = imageSize.split(".")[1];
let imgOptions = {
imageWidth,
imageX,
imageY,
name,
imgName,
imgPath,
imgNameWithPattern,
};
li += self.constructHtml(imgOptions);
});
$(".ImageGrid").append(li);
self.minigrid();
});
},
minigrid: function () {
var grid = new Minigrid({
container: ".ImageGrid",
item: ".card",
gutter: 12,
});
grid.mount();
$(window).resize(function () {
grid.mount();
});
},
};
photo.init();

创建injector.js文件

  1. Blog 目录中新建一个 scripts 文件夹,在文件夹内部在新建一个 injector.js 这是 hexo 注入器,一定要在 Blog 目录新建,这个文件夹和文件不能放到别的目录 ,在 injector.js 输入以下内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//相册页面
const { root: siteRoot = "/" } = hexo.config;
// layout为photo的时候导入这些js与css
hexo.extend.injector.register(
"body_end",
`

<link rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css">
<script src="https://cdn.jsdelivr.net/npm/minigrid@3.1.1/dist/minigrid.min.js"></script>
<script src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/lazyloadjs/3.2.2/lazyload.js"></script>
<script defer src="${siteRoot}js/photoWall.js"></script>
`,
"photo"
);

// CDN加载方案,目前上述文件使用CDN加载方案;
// 本地加载方案,这个方案就是把上面CDN文件一个一个存到 js目录下,仅此而已,我用的这个方案,感觉更靠谱些,不用担心CDN访问时候出现 404
// <link rel="stylesheet" href="/js/jquery.fancybox.min.css">
// <script src="/js/minigrid.min.js"></script>
// <script src="/js/jquery.fancybox.min.js"></script>
// <script src="/js/lazyload.js"></script>
// <script defer src="${siteRoot}js/photoWall.js"></script>
  1. 在你的 Git 控制台中输入 hexo clean && hexo g && hexo s 快速清除缓存,并生成静态文件,然后运行博客
  2. 现在你的相册已经配置完成了。

后期添加图片怎么操作?照片墙图片和img共用一个路径?

  1. 如果你不嫌麻烦可以重新将需要的图片复制到 photo/images- 目录下,然后重新执行获取图片大小信息,这样操作我不太喜欢,而且我需要的是照片墙和博客图片路径保持一样的位置,这样有利于减少博客空间占用。

  2. 找到 /photo/Phtotos.json 图片配置文件,用 HBuilder 等代码编辑工具,重新整理代码格式。

  3. 例如我要添加图片,则在 "1920.1080 复古音箱-渲染2.png", 的上方添加即可,

    注意规律:宽高中间有一个英文的点 .
    宽高的数值要和图片的宽高保持一致
    再按下空格 空格
    接下来是图片的名称和它的格式:复古音箱-渲染2.png
    用英文引号 “” 作为包裹数据段落
    然后你新加的图片在第一个位置,则要在尾部继续加一个英文 , 逗号
    注意:一个分类(这个分类是指:Blender、其它)的图片的最后一张是没有逗号的,如果你在其中调整了顺序别搞错了,逗号也别少加。
    这里的 name 里的参数可自行修改,这个是分类显示的名称。
    然后有多个分类,则如示范所展示参考:
    值得注意的是,这里的分类并不是按文件夹分类,它们都是指向同一个图片目录(路径),也就是说在这个配置文件中修改的分类只会影响渲染后注入网页的效果;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [{
    "name": "Blender",
    "children": [
    "宽.高 图片名称.png",
    "1920.1080 复古音箱-渲染2.png",
    "1920.1080 复古音箱-渲染白膜.png",
    "1280.3630 Watch_detail_page_display.png",
    "1000.1500 Watch_detail_page_display3.png"
    ]
    },
    {
    "name": "其它",
    "children": [
    "800.800 foot1.jpg",
    "800.800 foot2.jpg",
    "1000.1500 13445.jpg"
    ]
    }
    ]
  4. 接下来是将相册的图片和img公用一个路径,我们只需要找到 source/js/photoWall.js 修改图片访问路径即可。

    1
    var imgPath = "/img/images/"; //图片访问路径,填入你需要的路径,注意修改。
  5. 最后看看实际运行效果吧。

  6. 如果你的 hexo注入器 一直加载没反应,注意用 F12 审查元素,看看有没有一个 < 符号的,我之前是这样的问题,一直困扰了很久,最后发现是 aplayer 的锅,于是在 npm 中卸载并删除了 aplayerhexo-tag-aplayer 这两个文件夹,重新执行 hexo clean && hexo g && hexo s 后就可以跑起来了,最后 aplayer 也是用 hexo注入器 执行的。

直接套用

  1. 如果你也是 Fluid 主题,你可以直接套我改好的这个,然后复制到博客的根目录,再稍作修改即可。
  2. 或者你也是在路径关系这里踩了坑,也可以直接参考我改的源码,为避免你的博客配置被覆盖,所以没有提供 _config.yml 文件,你至少需要修改Fluid的导航栏配置
    在这里下载文件 | 文件路径树参考
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
blog
├─ Readme.txt
├─ node_modules
│ └─ hexo-theme-fluid
├─ scripts
│ └─ injector.js #图片框架使用CDN加载,还是本地加载,可在这里配置
└─ source
├─ img
│ ├─ bg
│ └─ images #这里放你的图片,含示例图片
├─ js
│ ├─ jquery.fancybox.min.css #本地加载图片框架的,别动就是
│ ├─ jquery.fancybox.min.js #本地加载图片框架的,别动就是
│ ├─ lazyload.js #本地加载图片框架的,别动就是
│ ├─ minigrid.min.js #本地加载图片框架的,别动就是
│ └─ photoWall.js #配置图片展示样式的文件
└─ photo #相册路径
├─ index.md #相册页
├─ photos.json #图片大小信息和展示顺序
├─ photosInfo.json #记录image-size输出图片元数据的
└─ phototool.js #用image-size来输出图片的宽高信息和路径的脚本



相册页鸣谢:

四维树的博客 - Fluid主题添加相册功能
何十七 - hexo的fluid主题添加相册功能及自定义页面
GISHAI - hexo的fluid主题添加瀑布流懒加载相册功能
魏超 - 为 Hexo + Fluid 博客添加承载相册的页面


为hexo博客Fluid主题添加相册功能
https://fxy5750.github.io/2024/04/05/30-hexo相册功能/
作者
fxy5750
发布于
2024年4月5日
更新于
2024年4月10日
许可协议