本文是基于Hexo 6.3.0, Fluid 1.9.4进行开发,主要借鉴于hexo的fluid主题添加瀑布流懒加载相册功能,其中基于jsDelivr+Github 实现免费CDN加速可以参考这篇文章搭建自己的CDN。
1.创建相册页面
- 在自己的博客项目下,新建相册页
hexo new page photos
- 编辑 /source/photos/index.md,输入以下内容
--- title: photos date: 2020-12-30 19:04:03 layout: photo --- <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: #efefef; padding: 1.4px; } .ImageInCard img { padding: 0; border-radius: 8px; width:100%; height:100%; } @media (prefers-color-scheme: dark) { .card {background-color: #333;} } </style> <div id="imageTab"></div> <div class="ImageGrid"></div>
2.处理图片信息
这里需要创建自己的github相册,可参考这篇文章jsDelivr+Github 实现免费CDN加速。
这里主要是为了加快图片的加载速度, 利用GitHub +jsDelivr的方式,此处不再演示。文件夹结构如图:
这里没有安装 image-size,需要npm安装下
npm i -S image-size
在github相册目录下,执行createjs,
[zyxelva@192 11:34:28 ~/source/photos]$ node createjs.js
脚本如下:
const fs = require('fs-extra');
const path = require('path');
const imageSize = require('image-size');
const rootPath="gallery/" //相册相对路径,也可填绝对路径,指向相册的目录,目的为了生成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();
将photos.json拷贝到博客目录下的photos
3.加载 js和css文件
在 /source/js/ 目录下创建 photoWall.js
var imgDataPath = "/blog/photos/photos.json"; //图片名称高宽信息json文件路径
var imgPath = "https://gcore.jsdelivr.net/gh/Cenergy/images/gallery/"; //图片访问路径, 这里参考文章开头提到的CDN加速
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}${name}/${imgNameWithPattern}"
data-caption="${imgName}" title="${imgName}">
<img class="lazyload" data-src="${imgPath}${name}/${imgNameWithPattern}"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
onload="lzld(this)"
lazyload="auto">
</a>
</div>
</div>`;
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.split(" ")[1];
imgName = imgNameWithPattern.split(".")[0];
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(".")[0];
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();
然后使用注册器将需要的js,css注入,在scripts/injector.js(如没有,则创建)中输入以下内容
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="//gcore.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"
);
最后 hexo clean && hexo g && hexo s
,本地部署看看效果。
提示:
GitHub +jsDelivr 期初加载有点慢,要等好几分钟,所有图才有加速,不要误以为是其他问题。