Files
reformix-hackaton/mvp/b2c/src/lib/image/crop.ts
Carlos Narro e9637f77ff Integrar recorte y optimización a WebP en las subidas de imagen del panel
Los tres uploaders del panel (logo, "quiénes somos" y galería) ahora abren
un recorte interactivo (zoom + encuadre) y reescalan en el navegador antes
de subir: logo y "quiénes somos" a máx. 500 px, galería a máx. 1200 px,
reencodando a WebP (calidad 0.82). El SVG del logo se sube tal cual para no
rasterizar el vector. Esto reduce el peso de los data URIs base64 que se
guardan en Postgres y se inlinean en el funnel.

Nueva dependencia react-easy-crop: librería de recorte ligera, sin estado
global, compatible con React 19; el reescalado y reencodado se hacen con
canvas nativo (lib/image/crop.ts), sin dependencias extra.
2026-06-01 22:36:55 +02:00

39 lines
1.4 KiB
TypeScript

export type CropArea = { x: number; y: number; width: number; height: number };
// Recorta la región indicada (en píxeles del original) y reescala el lado más
// largo a `maxDimension`, devolviendo un WebP ya optimizado listo para subir.
export async function getCroppedWebp(
src: string,
area: CropArea,
maxDimension: number,
quality = 0.82,
): Promise<Blob> {
const img = await loadImage(src);
const scale = Math.min(1, maxDimension / Math.max(area.width, area.height));
const outW = Math.max(1, Math.round(area.width * scale));
const outH = Math.max(1, Math.round(area.height * scale));
const canvas = document.createElement('canvas');
canvas.width = outW;
canvas.height = outH;
const ctx = canvas.getContext('2d');
if (!ctx) throw new Error('No se pudo crear el lienzo.');
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(img, area.x, area.y, area.width, area.height, 0, 0, outW, outH);
const blob = await new Promise<Blob | null>((resolve) =>
canvas.toBlob(resolve, 'image/webp', quality),
);
if (!blob) throw new Error('No se pudo procesar la imagen.');
return blob;
}
function loadImage(src: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error('No se pudo cargar la imagen.'));
img.src = src;
});
}