需求:做一个瀑布流相册,要求能读到移动硬盘的大量照片,并滚动加载;
技术:前端vue3;后端node+express
地址:瀑布流
效果:
前端
const loading = ref<boolean>(false)
let imgWidth = ref(0)
const imgList = ref<HTMLElement>()
onMounted(() => {
const width = (imgList.value as HTMLElement).offsetWidth;
imgWidth.value = (width - 56) / 5;
queryImage(pageSize, page)
})
const columnHeights = new Array(5).fill(0)
const pageSize = 10;
let page = 1
const queryImage = (pageSize: number, pageNum: number) => {
if (loading.value) return
axios.post("http://localhost:1234/getImages", {
pageSize,
pageNum
}).then(response => {
page++
response.data.data.forEach((n: any) => {
const int8Array = new Uint8Array(n.data)
const blob = new Blob([int8Array], { type: 'image/jpeg' })
const objectURL = URL.createObjectURL(blob)
const img = document.createElement('img');
img.src = objectURL;
img.style.width = imgWidth.value + 'px';
(imgList.value as HTMLElement).appendChild(img)
img.onload = function () {
const distance = 10;
const offsetDistance = 8;
const minIndex = columnHeights.indexOf(Math.min(...columnHeights))
const left = (imgWidth.value + distance) * minIndex;
const top = columnHeights[minIndex];
img.style.top = offsetDistance + top + 'px';
img.style.left = offsetDistance + left + 'px';
columnHeights[minIndex] = columnHeights[minIndex] + img.offsetHeight + distance;
}
});
})
.catch(error => {
console.error(error);
});
}
document.addEventListener('scroll', throttle(() => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;;
const height = Math.min(...columnHeights);
if (scrollTop + window.innerHeight + 800 > height) {
queryImage(pageSize, page)
}
}, 300))
后端:
const fs = require("fs");
const path = require("path");
const sharp = require("sharp");
const imageFolderPath = path.join("F:/相册");
let imageNames = [];
const cors = require("cors");
function getFilePath(imageFolderPath) {
fs.readdir(imageFolderPath, (err, files) => {
if (err) {
console.err("读取文件夹失败", err);
return;
}
files.forEach(file => {
const filePath = path.join(imageFolderPath, file);
const fileOption = fs.statSync(filePath);
if (fileOption.isDirectory()) {
// 如果是文件夹
getFilePath(filePath);
} else {
imageNames.push(filePath);
}
});
});
}
process.on("exit", () => {
console.log(imageNames, "exit");
});
const express = require("express");
const app = express();
app.use(cors());
// 读取json数据
app.use(express.json());
getFilePath(imageFolderPath);
const port = 1234;
app.post("/getImages", async (req, res) => {
const total = imageNames.length;
const { pageSize, pageNum } = req.body;
const startNum = pageSize * (pageNum - 1);
const endNum = startNum + pageSize;
let image = [];
const arr = imageNames.slice(startNum, endNum).map(n =>
sharp(n)
.resize({ width: 400 }) // 调整图片尺寸
.jpeg({ quality: 70 }) // 调整图片质量
.toBuffer()
);
await Promise.all(arr).then(data => {
image = data;
});
res.json({ data: image });
});
app.listen(port, () => {
console.log("启动1");
});