制作文章图片集合
August 10, 2024 by
使用 Vite
插件, 为文档工具制作图片集合
需求整理
- 之前在写文档时, 会把收集的图片选一张放在文档结尾, 会遇到一个问题: 图片可能会重复
- 想把写好的文档, 配合结尾图片, 做一个瀑布流展示
获取图片数据
在每次开发和打包时, 会自动获取图片, 并生成一个图片集合
选择 configResolved
钩子, 在 vite.config.ts
中添加如下代码
同时, 做图片查重
vite.config.ts
{
name: 'ccforeverd:img-urls-gen',
async configResolved() {
logger.info('start plugin img-urls-gen');
imgUrlsGen(
['./docs/pages/**/*.mdx'],
'./docs/generated/imgUrls.json',
);
logger.info('end plugin img-urls-gen');
},
},
具体代码如下
imgUrlsGen.ts
import fs from 'fs-extra';
import { globbySync } from 'globby';
import { LinesAndColumns, type SourceLocation } from 'lines-and-columns';
import { type HeadProps, getMdHead } from './getMdHead';
const urlReg =
/(https?|ftp|file):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/;
const componentName = 'EndOfFile';
const propName = 'imgUrl';
interface DataItem extends HeadProps {
url: string;
location: SourceLocation;
filePath: string;
}
export const imgUrlsGen = (glob: string[], output: string) => {
const files = globbySync(glob);
const images = new Set<string>(); // 用来去重
const data: DataItem[] = [];
for (const filePath of files) {
const content = fs.readFileSync(filePath, 'utf-8');
const componentNameIndex = content.indexOf(componentName);
// 如果不包含 componentName,直接返回
if (componentNameIndex === -1) {
continue;
}
const propNameIndex = content.indexOf(propName);
// 如果不包含 propName,直接返回
if (propNameIndex === -1) {
continue;
}
const start = content.indexOf('="', propNameIndex);
// 如果不是 imgSrc=" 开头的,直接返回
if (start - propNameIndex - propName.length !== 0) {
continue;
}
const end = content.indexOf('"', start + 2);
const imgUrl = content.slice(start + 2, end);
const location = new LinesAndColumns(content).locationForIndex(start);
// 如果不是一个 url,直接返回
if (!urlReg.test(imgUrl)) {
continue;
}
// 重复的图片
if (images.has(imgUrl)) {
console.warn(
`Duplicated image url: ${imgUrl} in ${filePath}:${location.line + 1}:${location.column + 1}`,
);
continue;
}
const head = getMdHead<HeadProps>(content);
// 是一个正经的 md 文件
if (head?.title) {
images.add(imgUrl);
data.push({
url: imgUrl,
location,
filePath,
title: head.title,
description: head.description || '',
date: head.date || '',
});
}
}
fs.writeJSONSync(
output,
data.sort(
(a, b) => new Date(a.date).getTime() - new Date(b.date).getTime(),
),
{ spaces: 2 },
);
};
展示图片集合
使用 column
布局, 做一个简单的瀑布流, 展示图片集合
ImgGrid.tsx
import { Box, Card, Divider, Image, Stack, Text, Title } from '@mantine/core';
import { PhotoProvider, PhotoView } from 'react-photo-view';
import imgUrls from '../generated/imgUrls.json';
export const ImgGrid = () => {
return (
<Box className=" columns-3 gap-4">
<PhotoProvider>
{imgUrls.map((item) => (
<Card key={item.url} className=" mb-4 shadow-xl">
<Card.Section>
<PhotoView src={item.url}>
<Image src={item.url} alt={item.title} />
</PhotoView>
</Card.Section>
<Stack
component="a"
// @ts-ignore
href={item.filePath
.replace('./docs/pages', '')
.replace('.mdx', '')}
className=" gap-2 mt-4"
>
<Title order={4}>{item.title}</Title>
<Divider />
<Text className=" text-base text-gray-500">
{item.description}
</Text>
<Text className=" text-xs text-gray-700">{item.date}</Text>
</Stack>
</Card>
))}
</PhotoProvider>
</Box>
);
};
效果还可以, 可以做加载优化, 并加上一些动画效果, 使得图片集合更加生动