写了一个瀑布流相册vue3+node express

48 阅读1分钟

需求:做一个瀑布流相册,要求能读到移动硬盘的大量照片,并滚动加载;
技术:前端vue3;后端node+express
地址:瀑布流
效果:

image.png 前端

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");
});