تولید خودکار عکس پاسخگو با قلاب Astro

تصاویر تغییر اندازه، فشرده و فرمت شده. از قلاب Astro برای فرمت خودکار تصاویر استفاده کنید. من در این پست از تایپ اسکریپت استفاده می کنم. کد کامل نوشته شده در این پست را می توانید در GitHub Gist بیابید: ImageCompressorIntegration.ts.
معرفی
از تصاویر سایت خود بیشترین بهره را ببرید. من ریز عکسها را در وبلاگ شخصی خود بهینه کردم، این قسمت تولید تصویر آن است.
روش های مختلفی که برای قالب بندی تصاویر استفاده کردم:
- تغییر اندازه به اندازه های مختلف
- تبدیل به فرمت WebP
- فشرده کردن
- تمام ابرداده ها را حذف کنید
برای تولید متدها، من یک هوک Astro ساختم که در build اجرا می شود.
پیاده سازی روش ها
برپایی
بیایید از یک کلاس برای این استفاده کنیم. اسمش را هر طور که می خواهید بگذارید، من استفاده کردم ImageGenerator
.
class ImageGenerator {}
یک نوع سفارشی خاص مورد نیاز است. اندازه های مربوط به تصویر اغلب استفاده می شود.
type imageSize = { width: number, height: number };
برای تغییر اندازه تصاویر باید یک قرارداد نامگذاری ایجاد شود، زیرا چندین تصویر تولید می شود. نمونه ای که استفاده می شود این است: [NAME]-[WIDTH]-[HEIGHT].[EXTENSION]
. برای مثال اگر اندازه آن تغییر کند image.png
به عرض 600 و ارتفاع 315 و تغییر فرمت آن به WebP نام تصویر خواهد بود image-600-315.webp
. این نسل نام اغلب استفاده می شود، بنابراین بیایید یک روش برای آن ایجاد کنیم. این NAME
، WIDTH
و HEIGHT
، EXTENSION
پارامترها هستند. زیرا در پایان فقط استفاده خواهد شد wepb
، بجای EXTENSION
من از یک بولین استفاده خواهم کرد که نشان می دهد پسوند فایل نتیجه است یا خیر webp
یا نه.
import path from 'path';
class ImageGenerator {
static generateName(filePath: string, size: imageSize, isWebp: boolean = false) {
const extension = (isWebp ? ".webp" : path.extname(filePath));
const justName = path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath))); // /directory/file.txt -> /directory/file
return [justName, String(size.width), String(size.height)].join("-") + extension; // join the 3 parts and add extension
}
}
من میخواهم اندازههای ریز عکسهایم را از پیش تعریف کنم.
class ImageGenerator {
public static readonly thumbnailMetaSizes = [
{ width: 1200, height: 630 },
{ width: 600, height: 315 },
{ width: 360, height: 189 },
{ width: 736, height: 414 },
];
}
برای ویرایش واقعی تصاویر، ما قصد داریم از آن استفاده کنیم sharp
. شارپ یک کتابخانه پردازش تصویر با کارایی بالا است. شما می توانید تمام کارهای جالبی که ما می خواهیم با آن انجام دهیم. کتابخانه را نصب کنید سپس آن را با آن وارد کنید import sharp from 'sharp'
.
روند
بیایید تصاویر را ویرایش کنیم.
ابتدا تصویر را با شارپ بارگذاری کنید.
class ImageGenerator {
static processImage(filepath: string) { // without a filepath we can't open it :D
const image = sharp(filepath);
}
}
باید اندازه تصویر را به چندین اندازه تغییر دهیم و هر کدام را ویرایش کنیم. به همین دلیل است که الف imageSize[]
پارامتر مورد نیاز است. تغییر اندازه تصاویر با وضوح بسیار آسان است، همچنین امکان انتخاب شیء متناسب تصویر را فراهم می کند. تناسب شی نشان می دهد که اگر نسبت تغییر اندازه با نسبت اصلی نباشد، تصویر چگونه باید باشد. با فیت شی، می توانم تعریف کنم که همیشه قسمت مرکزی تصویر را نشان می دهد. ما باید قبل از انجام هر کاری تصویر را کلون کنیم، بنابراین تغییر اندازه همیشه با تصویر اصلی انجام می شود.
class ImageGenerator {
static processImage(filepath: string, sizes: imageSize[]) {
const image = sharp(filepath);
for (const size of sizes) {
const resizedImage = image.clone().resize(size.width, size.height, { fit: 'cover', withoutEnlargement: true });
// compress, remove metadata, save to png and webp
}
}
}
سپس فایل را به WebP فرمت کنید. من می خواهم این را اختیاری بگذارم، بنابراین needWebp
آرگومان به تعریف متد ما اضافه خواهد شد. toFormat
روش برای تبدیل بین فرمت ها استفاده می شود.
class ImageGenerator {
static async processImage(filepath: string, sizes: imageSize[], needWebp: boolean = false) {
const image = //...
for (const size of sizes) {
const resizedImage = //...
for (const isWebp of (needWebp ? [true, false] : [false])) { // we are going to run compress and metadata modification for each format
await resizedImage.toFormat((isWebp ? "webp" : path.extname(filepath).slice(1) as any));
}
}
}
}
برای فشرده سازی، اضافه کنید {quality: 100}
به عنوان پارامتری برای toFormat
روش. برای حذف فراداده تماس بگیرید withMetadata
بعد از اینکه قالب بندی انجام شد
await resizedImage.toFormat((isWebp ? "webp" : path.extname(filepath).slice(1) as any), { quality: 100 }).withMetadata();
آخرین چیز این است که تصاویر جدید را ذخیره کنید. قبلا اجرا کردیم generateName
، فقط باید از آن استفاده کنیم.
class ImageGenerator {
static async processImage(filepath: string, sizes: imageSize[], needWebp: boolean = false) {
const image = //...
const basePath = path.dirname(filepath) + path.sep;
for (const size of sizes) {
//...
for (const isWebp of (needWebp ? [true, false] : [false])) {
const outPath = ImageCompressorIntegration.generateName(filepath, size, isWebp);
await resizedImage.toFormat((isWebp ? "webp" : path.extname(filepath).slice(1) as any), { quality: 100 }).withMetadata().toFile(outPath);
}
}
}
}
من به تصویر اصلی نیازی ندارم، بنابراین گزینه ای برای حذف خودکار آن اضافه کردم.
import fs from 'fs';
class ImageGenerator {
static async processImage(filepath: string, sizes: imageSize[], needWebp: boolean = false, removeOriginal: boolean = false) {
//...
if (removeOriginal) {
fs.rm(filepath, () => { }); // rm to remove
}
}
}
کارمان تمام شد ImageGenerator
کلاس
قلاب Astro
برای تولید خودکار تصاویر یک ادغام Astro سفارشی مورد نیاز است.
3 قلاب مربوط به ساخت وجود دارد: start
، setup
، generated
. قرار است استفاده کنیم astro:build:generated
، پس از اتمام کامل ساخت Astro اجرا می شود.
داخل همان فایل TypeScript ما ImageGenerator
class Export تابعی است که تنظیمات ادغام را برمی گرداند.
import type { AstroIntegration } from "astro";
export default function createPlugin(): AstroIntegration {
return {
name: "ImageCompressorIntegration",
hooks: {
"astro:build:generated": async (options: { dir: URL }) => {} // make it async because our processImage is async too
}
};
}
تنها کاری که باید انجام دهیم این است که تماس بگیریم processImage
برای تمام تصاویری که می خواهیم پردازش کنیم. من میخواهم تمام فایلهای موجود در آن را پردازش کنم thumbnail
فهرست راهنما. مسیر پروژه ساخته شده را می توان از طریق options
dir
بحث و جدل.
export default function createPlugin(): AstroIntegration {
return {
name: "ImageCompressorIntegration",
hooks: {
"astro:build:generated": async (options: { dir: URL }) => {
const distPath = options.dir.pathname;
const allFiles = readDirectoryRecursive(path.join(distPath, "thumbnail"));
for (const [i, imgPath] of allFiles.entries()) {
await ImageGenerator.processImage(imgPath, ImageGenerator.thumbnailMetaSizes, true, true);
console.log(`${path.basename(imgPath)} image sizes generated (${i+1}/${allFiles.length})`);
}
}
}
};
}
readDirectoryRecursive
کد تابع را می توان در GitHub Gist پیدا کرد: ImageCompressorIntegration.ts
برای تکمیل آن، از ادغام خود در استفاده کنید astro.config.mjs
.
در قسمت بعدی نحوه استفاده از تصاویر سایت را نشان خواهم داد.