@hemia/image-kit
v0.0.1
Published
Package for image processing
Downloads
6
Readme
@hemia/image-kit
Package profesional para procesamiento de imágenes con Sharp. Incluye compresión, watermarks y extracción de metadatos.
📦 Instalación
npm install @hemia/image-kit🚀 Uso Rápido
import { ImageKit } from '@hemia/image-kit';
// Comprimir imagen
const result = await ImageKit.compress('input.jpg', { quality: 80 }, 'output.jpg');
// Agregar watermark
const watermarked = await ImageKit.watermark('input.jpg', { text: 'Mi Marca' }, 'output.jpg');
// Obtener metadatos
const metadata = await ImageKit.getMetadata('input.jpg');
// Aplicar filtros
const blurred = await ImageKit.blur('photo.jpg', 10, 'blurred.jpg');
const bw = await ImageKit.grayscale('photo.jpg', 'blackwhite.jpg');
// Generar thumbnail inteligente
const thumb = await ImageKit.thumbnail('image.jpg', 300, 300, 'thumb.jpg');
// Obtener color dominante
const color = await ImageKit.getDominantColor('image.jpg'); // "#a3124c"
// Generar placeholder base64
const placeholder = await ImageKit.placeholder('image.jpg');📚 API Reference
compress(input, options, outputPath?)
Comprime una imagen con opciones personalizables de calidad, formato y dimensiones.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen a comprimir |
| options | CompressionOptions | Opciones de compresión (ver tabla abajo) |
| outputPath | string \| null (opcional) | Ruta donde guardar el archivo. Si es null o no se proporciona, retorna un Buffer |
Opciones de Compresión (CompressionOptions)
| Opción | Tipo | Por Defecto | Descripción |
|--------|------|-------------|-------------|
| quality | number | 80 | Calidad de compresión (1-100). Valores más altos = mejor calidad pero mayor tamaño |
| format | 'jpeg' \| 'png' \| 'webp' \| 'avif' | 'jpeg' | Formato de salida de la imagen |
| width | number | - | Ancho deseado en píxeles. Mantiene aspecto si no se especifica height |
| height | number | - | Alto deseado en píxeles. Mantiene aspecto si no se especifica width |
| fit | 'cover' \| 'contain' \| 'fill' \| 'inside' \| 'outside' | 'cover' | Estrategia de redimensionamiento cuando se especifican ambas dimensiones |
| progressive | boolean | true | Habilita carga progresiva (solo JPEG) |
| compressionLevel | number | 9 | Nivel de compresión para PNG (0-9). Mayor = más compresión |
Retorno
- Con
outputPath:Promise<CompressionResult>- Objeto con información del resultado - Sin
outputPath:Promise<Buffer>- Buffer con la imagen comprimida
interface CompressionResult {
success: boolean;
originalSize: number; // Tamaño original en bytes
compressedSize: number; // Tamaño comprimido en bytes
savedPercentage: number; // Porcentaje de reducción
width: number; // Ancho final
height: number; // Alto final
format: string; // Formato final
path?: string; // Ruta del archivo guardado
}Ejemplos
Compresión básica con calidad personalizada:
const result = await compress('photo.jpg', { quality: 85 }, 'photo-compressed.jpg');
console.log(`Reducción: ${result.savedPercentage}%`);Convertir a WebP y redimensionar:
const result = await compress('image.png', {
format: 'webp',
quality: 90,
width: 800,
height: 600,
fit: 'cover'
}, 'image.webp');Trabajar con Buffers (sin guardar archivo):
const imageBuffer = await fs.readFile('input.jpg');
const compressedBuffer = await compress(imageBuffer, { quality: 75 });
// Enviar buffer por API, etc.Redimensionar manteniendo aspecto:
const result = await compress('large.jpg', {
width: 1200, // Solo especificar ancho
quality: 80
}, 'thumbnail.jpg');addWatermark(input, options, outputPath)
Agrega una marca de agua de texto a una imagen.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
| options | WatermarkOptions | Opciones del watermark (ver tabla abajo) |
| outputPath | string | Ruta donde guardar la imagen con watermark. Si no se proporciona, retorna Buffer |
Opciones de Watermark (WatermarkOptions)
| Opción | Tipo | Por Defecto | Descripción |
|--------|------|-------------|-------------|
| text | string | 'Hemia' | Texto de la marca de agua |
| gravity | 'southeast' \| 'southwest' \| 'northeast' \| 'northwest' \| 'center' | 'southeast' | Posición del watermark en la imagen |
| opacity | number | 50 | Opacidad del texto (0-100). 0 = transparente, 100 = opaco |
| fontSize | number | 30 | Tamaño de la fuente en píxeles |
Retorno
- Con
outputPath:Promise<WatermarkResult>- Objeto con información del resultado - Sin
outputPath:Promise<Buffer>- Buffer con la imagen marcada
interface WatermarkResult {
success: boolean;
width: number; // Ancho de la imagen
height: number; // Alto de la imagen
format: string; // Formato de la imagen
path?: string; // Ruta del archivo guardado
}Ejemplos
Watermark básico en esquina inferior derecha:
const result = await addWatermark('photo.jpg', {
text: '© Mi Empresa 2024'
}, 'photo-watermarked.jpg');Watermark centrado con opacidad personalizada:
const result = await addWatermark('banner.png', {
text: 'CONFIDENCIAL',
gravity: 'center',
opacity: 30,
fontSize: 50
}, 'banner-marked.png');Watermark en esquina superior izquierda:
const result = await addWatermark('image.jpg', {
text: 'Draft',
gravity: 'northwest',
opacity: 70,
fontSize: 25
}, 'draft.jpg');Trabajar con Buffer (sin guardar):
const buffer = await addWatermark('input.jpg', {
text: 'Sample',
opacity: 40
});
// Retorna Buffer directamentegetMetadata(input)
Extrae información de metadatos de una imagen.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
Retorno
Promise<Metadata> - Objeto con los metadatos de la imagen:
interface Metadata {
format: string; // Formato de la imagen (jpeg, png, webp, etc.)
width: number; // Ancho en píxeles
height: number; // Alto en píxeles
hasAlpha: boolean; // Si tiene canal alpha (transparencia)
isProgressive: boolean; // Si es progresiva (JPEG)
}Ejemplos
Obtener información básica:
const meta = await getMetadata('photo.jpg');
console.log(`Dimensiones: ${meta.width}x${meta.height}`);
console.log(`Formato: ${meta.format}`);Validar dimensiones antes de procesar:
const meta = await getMetadata('upload.png');
if (meta.width > 4000 || meta.height > 4000) {
console.log('Imagen demasiado grande, comprimiendo...');
await compress('upload.png', { width: 2000 }, 'resized.png');
}Verificar transparencia:
const meta = await getMetadata('logo.png');
if (meta.hasAlpha) {
console.log('La imagen tiene transparencia');
// Usar PNG o WebP para preservar alpha
} else {
// Puede convertirse a JPEG sin pérdida
await compress('logo.png', { format: 'jpeg' }, 'logo.jpg');
}blur(input, sigma, outputPath?)
Aplica un desenfoque Gaussiano a la imagen.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
| sigma | number | Intensidad del desenfoque (0.3 a 1000). Por defecto: 5 |
| outputPath | string (opcional) | Ruta donde guardar el archivo. Si no se proporciona, retorna un Buffer |
Retorno
- Con
outputPath:Promise<FileResult>- Objeto con información del resultado - Sin
outputPath:Promise<Buffer>- Buffer con la imagen procesada
interface FileResult {
success: boolean;
path: string;
width?: number;
height?: number;
format?: string;
size?: number;
}Ejemplos
Desenfoque suave:
const result = await ImageKit.blur('photo.jpg', 3, 'soft-blur.jpg');Desenfoque intenso para efecto artístico:
const result = await ImageKit.blur('background.jpg', 20, 'blurred-bg.jpg');Trabajar con Buffer:
const blurredBuffer = await ImageKit.blur('image.jpg', 10);
// Usar buffer en memoriagrayscale(input, outputPath?)
Convierte la imagen a escala de grises (blanco y negro).
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
| outputPath | string (opcional) | Ruta donde guardar el archivo. Si no se proporciona, retorna un Buffer |
Retorno
- Con
outputPath:Promise<FileResult>- Objeto con información del resultado - Sin
outputPath:Promise<Buffer>- Buffer con la imagen procesada
Ejemplos
Convertir a blanco y negro:
const result = await ImageKit.grayscale('color-photo.jpg', 'bw-photo.jpg');Procesar múltiples imágenes:
const images = ['photo1.jpg', 'photo2.jpg', 'photo3.jpg'];
for (const img of images) {
await ImageKit.grayscale(img, `bw-${img}`);
}Efecto vintage combinado:
// Primero convertir a B&N, luego aplicar desenfoque sutil
const bwBuffer = await ImageKit.grayscale('photo.jpg');
const vintage = await ImageKit.blur(bwBuffer, 1, 'vintage.jpg');thumbnail(input, width, height, outputPath?)
Crea una miniatura recortada inteligentemente usando detección de "atención" para no cortar caras u objetos importantes.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
| width | number | Ancho deseado del thumbnail en píxeles |
| height | number | Alto deseado del thumbnail en píxeles |
| outputPath | string (opcional) | Ruta donde guardar el archivo. Si no se proporciona, retorna un Buffer |
Retorno
- Con
outputPath:Promise<FileResult>- Objeto con información del resultado - Sin
outputPath:Promise<Buffer>- Buffer con la imagen procesada
Características
- Recorte inteligente: Usa
sharp.strategy.attentionpara enfocar la zona más interesante - Fit cover: Recorta lo que sobre para mantener las dimensiones exactas
- Ideal para: Avatares, previews de productos, galerías de imágenes
Ejemplos
Thumbnail cuadrado para avatar:
const thumb = await ImageKit.thumbnail('profile.jpg', 200, 200, 'avatar.jpg');Thumbnails para galería:
const images = await getImagesFromDB();
for (const img of images) {
await ImageKit.thumbnail(img.path, 400, 300, `thumbs/${img.id}.jpg`);
}Generar múltiples tamaños:
const sizes = [
{ w: 150, h: 150, name: 'small' },
{ w: 300, h: 300, name: 'medium' },
{ w: 600, h: 600, name: 'large' }
];
for (const size of sizes) {
await ImageKit.thumbnail(
'original.jpg',
size.w,
size.h,
`thumb-${size.name}.jpg`
);
}placeholder(input)
Genera un string Base64 de muy baja resolución ideal para usar como placeholder mientras carga la imagen original (alternativa a Blurhash).
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
Retorno
Promise<string> - String en formato data:image/jpeg;base64,... listo para usar en src de <img>
Características
- Tamaño mínimo: Redimensiona a 20px con blur
- Carga instantánea: Peso mínimo para carga ultra rápida
- UX mejorada: Evita el "salto" visual cuando carga la imagen real
Ejemplos
Uso básico en HTML:
const placeholderData = await ImageKit.placeholder('large-image.jpg');
// En tu HTML/JSX:
// <img src={placeholderData} data-src="large-image.jpg" />React con lazy loading:
import { useState, useEffect } from 'react';
function LazyImage({ src }) {
const [placeholder, setPlaceholder] = useState('');
const [loaded, setLoaded] = useState(false);
useEffect(() => {
ImageKit.placeholder(src).then(setPlaceholder);
}, [src]);
return (
<img
src={loaded ? src : placeholder}
onLoad={() => setLoaded(true)}
style={{ filter: loaded ? 'none' : 'blur(10px)' }}
/>
);
}Generar placeholders para galería:
const images = ['img1.jpg', 'img2.jpg', 'img3.jpg'];
const placeholders = await Promise.all(
images.map(img => ImageKit.placeholder(img))
);
// Guardar en base de datos o cacheNext.js Image con placeholder:
// En tu API route o getStaticProps
const placeholderData = await ImageKit.placeholder('/public/hero.jpg');
// En tu componente
<Image
src="/hero.jpg"
placeholder="blur"
blurDataURL={placeholderData}
width={1200}
height={600}
/>getDominantColor(input)
Analiza la imagen y devuelve el color predominante en formato hexadecimal.
Parámetros
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| input | string \| Buffer | Ruta del archivo o Buffer de la imagen |
Retorno
Promise<string> - Color en formato hexadecimal (ej: "#a3124c")
Casos de Uso
- Temas dinámicos: Generar paletas de colores basadas en imágenes
- Fondos adaptativos: Crear fondos que combinen con la imagen
- Diseño automático: Ajustar UI según el color dominante de una foto
Ejemplos
Obtener color dominante:
const color = await ImageKit.getDominantColor('product.jpg');
console.log(color); // "#3a7bd5"Generar tema dinámico:
const dominantColor = await ImageKit.getDominantColor('album-cover.jpg');
// Usar en CSS
const theme = {
primary: dominantColor,
background: `${dominantColor}20`, // Con transparencia
border: dominantColor
};Crear fondo adaptativo:
const images = await getGalleryImages();
for (const img of images) {
const color = await ImageKit.getDominantColor(img.path);
await saveToDatabase({
imageId: img.id,
dominantColor: color,
backgroundColor: `linear-gradient(135deg, ${color}, #ffffff)`
});
}React component con color dinámico:
function ProductCard({ imageSrc }) {
const [bgColor, setBgColor] = useState('#f0f0f0');
useEffect(() => {
ImageKit.getDominantColor(imageSrc).then(color => {
setBgColor(color + '30'); // Agregar transparencia
});
}, [imageSrc]);
return (
<div style={{ backgroundColor: bgColor }}>
<img src={imageSrc} alt="Product" />
</div>
);
}Validar contraste para accesibilidad:
const bgColor = await ImageKit.getDominantColor('background.jpg');
// Función helper para calcular luminancia
function getLuminance(hex: string) {
// ... cálculo de luminancia
}
const luminance = getLuminance(bgColor);
const textColor = luminance > 0.5 ? '#000000' : '#ffffff';
console.log(`Usar texto ${textColor} sobre fondo ${bgColor}`);🔄 Flujo de Trabajo Completo
Ejemplo de procesamiento completo de una imagen:
import { ImageKit } from '@hemia/image-kit';
async function processImage(inputPath: string) {
// 1. Obtener metadatos originales
const originalMeta = await ImageKit.getMetadata(inputPath);
console.log(`Original: ${originalMeta.width}x${originalMeta.height}`);
// 2. Comprimir y redimensionar
const compressed = await ImageKit.compress(inputPath, {
format: 'webp',
quality: 85,
width: 1920,
fit: 'inside'
}, 'temp-compressed.webp');
console.log(`Reducción: ${compressed.savedPercentage}%`);
// 3. Agregar watermark
const final = await ImageKit.watermark('temp-compressed.webp', {
text: '© Mi Marca 2024',
gravity: 'southeast',
opacity: 60,
fontSize: 24
}, 'final-output.webp');
console.log(`Imagen final: ${final.width}x${final.height}`);
return final;
}
processImage('input.jpg');🎨 Casos de Uso Avanzados
Galería de Imágenes con Placeholders
async function createGallery(images: string[]) {
const gallery = [];
for (const img of images) {
// Generar thumbnail
const thumb = await ImageKit.thumbnail(img, 400, 300, `thumbs/${img}`);
// Generar placeholder
const placeholder = await ImageKit.placeholder(img);
// Obtener color dominante
const color = await ImageKit.getDominantColor(img);
gallery.push({
original: img,
thumbnail: thumb.path,
placeholder,
dominantColor: color
});
}
return gallery;
}Procesamiento de Imágenes de Productos
async function processProductImage(productImage: string) {
// 1. Crear versión optimizada
await ImageKit.compress(productImage, {
format: 'webp',
quality: 90,
maxWidth: 2000
}, 'product-hd.webp');
// 2. Crear thumbnails en múltiples tamaños
await ImageKit.thumbnail(productImage, 800, 800, 'product-large.webp');
await ImageKit.thumbnail(productImage, 400, 400, 'product-medium.webp');
await ImageKit.thumbnail(productImage, 150, 150, 'product-small.webp');
// 3. Generar placeholder para carga rápida
const placeholder = await ImageKit.placeholder(productImage);
// 4. Extraer color para tema de la página
const themeColor = await ImageKit.getDominantColor(productImage);
return {
images: {
hd: 'product-hd.webp',
large: 'product-large.webp',
medium: 'product-medium.webp',
small: 'product-small.webp'
},
placeholder,
themeColor
};
}Efectos Artísticos
async function createArtisticEffect(input: string) {
// Efecto vintage: B&N + desenfoque sutil
const bwBuffer = await ImageKit.grayscale(input);
await ImageKit.blur(bwBuffer, 0.5, 'vintage.jpg');
// Efecto dramático: B&N + watermark
const dramatic = await ImageKit.grayscale(input);
await ImageKit.watermark(dramatic, {
text: 'DRAMATIC',
gravity: 'center',
opacity: 20,
fontSize: 80
}, 'dramatic.jpg');
// Background blur para destacar sujeto
await ImageKit.blur(input, 15, 'background-blur.jpg');
}📝 Notas Importantes
- Formatos soportados: JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF
- Buffers vs Paths: Todas las funciones aceptan tanto rutas de archivo como Buffers para máxima flexibilidad
- Salida opcional: Si no especificas
outputPath, obtienes un Buffer para procesamiento en memoria - Calidad recomendada:
- JPEG: 80-90 para fotos
- WebP: 80-85 (mejor compresión que JPEG)
- PNG: usar
compressionLevel9 para máxima compresión
- Performance: WebP y AVIF ofrecen mejor compresión que JPEG/PNG pero requieren más procesamiento
🛠️ Tecnologías
- Sharp: Motor de procesamiento de imágenes de alto rendimiento
- TypeScript: Tipado completo para mejor DX
📄 Licencia
ISC
