Spaces:
Runtime error
Runtime error
| /** | |
| * Client-side image optimization. | |
| * | |
| * Resizes images and converts them to WebP before upload, | |
| * reducing bandwidth and storage costs significantly. | |
| */ | |
| interface OptimizedVariant { | |
| blob: Blob; | |
| width: number; | |
| height: number; | |
| suffix: string; | |
| } | |
| export interface OptimizedImage { | |
| original: { width: number; height: number }; | |
| variants: OptimizedVariant[]; | |
| } | |
| const VARIANTS = [ | |
| { suffix: "thumb", maxWidth: 400, quality: 0.7 }, | |
| { suffix: "medium", maxWidth: 1000, quality: 0.8 }, | |
| { suffix: "full", maxWidth: 2000, quality: 0.85 }, | |
| ] as const; | |
| /** | |
| * Optimize an image file: resize to 3 WebP variants + extract dimensions. | |
| * Returns the variants sorted small to large. | |
| */ | |
| export async function optimizeImage(file: File): Promise<OptimizedImage> { | |
| const bitmap = await createImageBitmap(file); | |
| const { width: origW, height: origH } = bitmap; | |
| const variants: OptimizedVariant[] = []; | |
| for (const { suffix, maxWidth, quality } of VARIANTS) { | |
| // Skip variant if original is smaller than this tier | |
| if (origW <= maxWidth && suffix !== "full") continue; | |
| const scale = Math.min(1, maxWidth / origW); | |
| const w = Math.round(origW * scale); | |
| const h = Math.round(origH * scale); | |
| const canvas = new OffscreenCanvas(w, h); | |
| const ctx = canvas.getContext("2d"); | |
| if (!ctx) throw new Error("Canvas 2D context unavailable"); | |
| ctx.drawImage(bitmap, 0, 0, w, h); | |
| const blob = await canvas.convertToBlob({ | |
| type: "image/webp", | |
| quality, | |
| }); | |
| variants.push({ blob, width: w, height: h, suffix }); | |
| } | |
| // Always ensure at least one variant (the full size) | |
| if (variants.length === 0) { | |
| const canvas = new OffscreenCanvas(origW, origH); | |
| const ctx = canvas.getContext("2d"); | |
| if (!ctx) throw new Error("Canvas 2D context unavailable"); | |
| ctx.drawImage(bitmap, 0, 0); | |
| const blob = await canvas.convertToBlob({ | |
| type: "image/webp", | |
| quality: 0.85, | |
| }); | |
| variants.push({ blob, width: origW, height: origH, suffix: "full" }); | |
| } | |
| bitmap.close(); | |
| return { | |
| original: { width: origW, height: origH }, | |
| variants, | |
| }; | |
| } | |
| /** | |
| * Check if the browser supports WebP encoding via OffscreenCanvas. | |
| */ | |
| export function supportsWebpOptimization(): boolean { | |
| try { | |
| return typeof OffscreenCanvas !== "undefined"; | |
| } catch { | |
| return false; | |
| } | |
| } | |