houser-js-utils
Version:
A comprehensive collection of TypeScript utility functions for common development tasks including array manipulation, string processing, date handling, random number generation, validation, and much more.
576 lines (575 loc) • 19.5 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const base64RegEx = /^data:image\/(png|jpg|jpeg|gif|svg|webp);base64,/;
const ImageUtils = {
/**
* Applies a grayscale filter to an image by converting all pixels to their average RGB value.
* @param file - The image file to convert to grayscale
* @returns Promise resolving to the grayscale image as a Blob
* @example
* ```typescript
* const grayscaleImage = await ImageUtils.applyGrayscale(imageFile);
* // Use the grayscale blob as needed
* ```
*/
async applyGrayscale(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg;
data[i + 1] = avg;
data[i + 2] = avg;
}
ctx.putImageData(imageData, 0, 0);
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error("Failed to create blob"));
}, file.type);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Calculates the aspect ratio of an image from its dimensions.
* @param width - The width of the image in pixels
* @param height - The height of the image in pixels
* @returns The aspect ratio as a decimal number (width/height)
* @example
* ```typescript
* const ratio = ImageUtils.calculateAspectRatio(1920, 1080); // Returns 1.777...
* const isWidescreen = ratio > 1.5; // true for 16:9 ratio
* ```
*/
calculateAspectRatio(width, height) {
return width / height;
},
/**
* Converts a base64 string to a Blob object.
* @param base64 - The base64 string to convert (without data URL prefix)
* @param type - The MIME type of the image (default: "image/jpeg")
* @returns Promise resolving to a Blob object
* @example
* ```typescript
* const blob = await ImageUtils.base64ToBlob(base64String, 'image/png');
* const url = URL.createObjectURL(blob);
* ```
*/
async base64ToBlob(base64, type = "image/jpeg") {
const response = await fetch(`data:${type};base64,${base64}`);
return response.blob();
},
/**
* Converts a Blob to a base64 string (without data URL prefix).
* @param blob - The Blob to convert
* @returns Promise resolving to base64 string
* @example
* ```typescript
* const base64 = await ImageUtils.blobToBase64(imageBlob);
* console.log('data:image/jpeg;base64,' + base64); // Full data URL
* ```
*/
async blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result;
resolve(base64.split(",")[1]);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
},
/**
* Compresses an image file by reducing its quality while maintaining dimensions.
* @param file - The image file to compress
* @param quality - Compression quality from 0 (lowest) to 1 (highest, default: 0.7)
* @returns Promise resolving to the compressed image as a Blob
* @example
* ```typescript
* const compressed = await ImageUtils.compressImage(largeImage, 0.5);
* console.log(`Original: ${largeImage.size} bytes, Compressed: ${compressed.size} bytes`);
* ```
*/
async compressImage(file, quality = 0.7) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
canvas.toBlob(
(blob) => {
if (blob) resolve(blob);
else reject(new Error("Failed to create blob"));
},
file.type,
quality
);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Converts a base64 data URL string to a File object.
* @param base64 - The base64 data URL string (must include data:image/... prefix)
* @param filename - The name for the resulting file
* @returns Promise resolving to File object, or null if base64 is invalid
* @example
* ```typescript
* const file = await ImageUtils.convertBase64ToFile(
* 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
* 'pixel.png'
* );
* ```
*/
convertBase64ToFile(base64, filename) {
if (!base64RegEx.test(base64)) return null;
return this.base64ToBlob(base64).then((blob) => new File([blob], filename));
},
/**
* Creates a thumbnail image with specified maximum dimensions.
* @param file - The image file to create a thumbnail from
* @param maxSize - Maximum size in pixels for the longest dimension (default: 200)
* @returns Promise resolving to thumbnail as base64 data URL string
* @example
* ```typescript
* const thumbnail = await ImageUtils.createThumbnail(imageFile, 150);
* document.getElementById('preview').src = thumbnail;
* ```
*/
async createThumbnail(file, maxSize = 200) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
let width = img.width;
let height = img.height;
if (width > height) {
if (width > maxSize) {
height = Math.round(height * maxSize / width);
width = maxSize;
}
} else {
if (height > maxSize) {
width = Math.round(width * maxSize / height);
height = maxSize;
}
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
resolve(canvas.toDataURL("image/jpeg", 0.7));
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Crops an image to specified rectangular dimensions.
* @param file - The image file to crop
* @param x - Starting x coordinate for the crop (pixels from left)
* @param y - Starting y coordinate for the crop (pixels from top)
* @param width - Width of the crop area in pixels
* @param height - Height of the crop area in pixels
* @returns Promise resolving to the cropped image as a Blob
* @example
* ```typescript
* // Crop a 200x200 square from the center of the image
* const cropped = await ImageUtils.cropImage(imageFile, 100, 100, 200, 200);
* ```
*/
async cropImage(file, x, y, width, height) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, x, y, width, height, 0, 0, width, height);
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error("Failed to create blob"));
}, file.type);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Extracts metadata information from an image file.
* @param file - The image file to analyze
* @returns Promise resolving to object containing width, height, type, and size
* @example
* ```typescript
* const metadata = await ImageUtils.getImageMetadata(imageFile);
* console.log(`Image: ${metadata.width}x${metadata.height}, ${metadata.type}, ${metadata.size} bytes`);
* ```
*/
async getImageMetadata(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve({
width: img.width,
height: img.height,
type: file.type,
size: file.size
});
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Analyzes an image to determine its most dominant color.
* @param file - The image file to analyze
* @returns Promise resolving to the dominant color as a hex string
* @example
* ```typescript
* const dominantColor = await ImageUtils.getDominantColor(imageFile);
* console.log(`Dominant color: ${dominantColor}`); // e.g., "#ff5733"
* document.body.style.backgroundColor = dominantColor;
* ```
*/
async getDominantColor(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
const scale = Math.min(1, 100 / Math.max(img.width, img.height));
canvas.width = img.width * scale;
canvas.height = img.height * scale;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
const colorCounts = {};
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const color = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
colorCounts[color] = (colorCounts[color] || 0) + 1;
}
let maxCount = 0;
let dominantColor = "#000000";
for (const color in colorCounts) {
if (colorCounts[color] > maxCount) {
maxCount = colorCounts[color];
dominantColor = color;
}
}
resolve(dominantColor);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Calculates scaled dimensions for an image while maintaining aspect ratio.
* @param img - The HTML image element to scale
* @param maxWidth - Optional maximum width constraint
* @param maxHeight - Optional maximum height constraint
* @returns Object containing the calculated scaled width and height
* @example
* ```typescript
* const scaled = ImageUtils.getScaledDimensions(imageElement, 800, 600);
* console.log(`Scaled dimensions: ${scaled.width}x${scaled.height}`);
* ```
*/
getScaledDimensions(img, maxWidth, maxHeight) {
const originalWidth = img.width;
const originalHeight = img.height;
let newWidth = originalWidth;
let newHeight = originalHeight;
if (maxWidth && maxHeight) {
const aspectRatio = originalWidth / originalHeight;
if (originalWidth > maxWidth || originalHeight > maxHeight) {
if (aspectRatio > 1) {
newWidth = maxWidth;
newHeight = maxWidth / aspectRatio;
} else {
newHeight = maxHeight;
newWidth = maxHeight * aspectRatio;
}
}
} else if (maxWidth) {
const aspectRatio = originalWidth / originalHeight;
if (originalWidth > maxWidth) {
newWidth = maxWidth;
newHeight = maxWidth / aspectRatio;
}
} else if (maxHeight) {
const aspectRatio = originalWidth / originalHeight;
if (originalHeight > maxHeight) {
newHeight = maxHeight;
newWidth = maxHeight * aspectRatio;
}
}
return {
width: Math.round(newWidth),
height: Math.round(newHeight)
};
},
/**
* Checks if an image exists and is accessible at the given URL.
* @param url - The URL to check for image availability
* @returns Promise resolving to true if the image exists and is accessible
* @example
* ```typescript
* const exists = await ImageUtils.imageExists('https://example.com/image.jpg');
* if (exists) {
* console.log('Image is available');
* }
* ```
*/
async imageExists(url) {
try {
const response = await fetch(url);
return response.ok;
} catch (e) {
return false;
}
},
/**
* Validates whether a file is an image based on its MIME type.
* @param file - The file to check
* @returns True if the file is an image, false otherwise
* @example
* ```typescript
* const isImage = ImageUtils.isImageFile(selectedFile);
* if (!isImage) {
* alert('Please select an image file');
* }
* ```
*/
isImageFile(file) {
return file.type.startsWith("image/");
},
/**
* Loads an image from a URL and returns the HTMLImageElement.
* @param url - The URL of the image to load
* @returns Promise resolving to HTMLImageElement when image loads successfully
* @throws {Error} If the image fails to load
* @example
* ```typescript
* try {
* const img = await ImageUtils.loadImage('https://example.com/image.jpg');
* console.log(`Loaded image: ${img.width}x${img.height}`);
* } catch (error) {
* console.error('Failed to load image:', error);
* }
* ```
*/
loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
},
/**
* Loads and decodes an image from a URL with error handling.
* @param url - The URL of the image to load
* @returns Promise resolving to HTMLImageElement or null if loading fails
* @example
* ```typescript
* const img = await ImageUtils.loadImageElement('https://example.com/image.jpg');
* if (img) {
* document.body.appendChild(img);
* } else {
* console.log('Failed to load image');
* }
* ```
*/
async loadImageElement(url) {
const img = new Image();
img.src = url;
try {
await img.decode();
} catch (e) {
console.error(e);
return null;
}
return img;
},
/**
* Resizes an image file to fit within specified dimensions while maintaining aspect ratio.
* @param file - The image file to resize
* @param options - Resize options including max dimensions, quality, and output format
* @returns Promise resolving to the resized image as a Blob
* @example
* ```typescript
* const resized = await ImageUtils.resizeImage(originalFile, {
* maxWidth: 800,
* maxHeight: 600,
* quality: 0.9,
* format: 'jpeg'
* });
* ```
*/
async resizeImage(file, options = {}) {
const {
maxWidth = 1920,
maxHeight = 1080,
quality = 0.8,
format = "jpeg"
} = options;
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
let width = img.width;
let height = img.height;
if (width > height) {
if (width > maxWidth) {
height = Math.round(height * maxWidth / width);
width = maxWidth;
}
} else {
if (height > maxHeight) {
width = Math.round(width * maxHeight / height);
height = maxHeight;
}
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error("Failed to create blob"));
}
},
`image/${format}`,
quality
);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Rotates an image by the specified number of degrees around its center.
* @param file - The image file to rotate
* @param degrees - Degrees to rotate (0-360, positive for clockwise)
* @returns Promise resolving to the rotated image as a Blob
* @example
* ```typescript
* const rotated90 = await ImageUtils.rotateImage(imageFile, 90);
* const rotatedMinus45 = await ImageUtils.rotateImage(imageFile, -45);
* ```
*/
async rotateImage(file, degrees) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
reject(new Error("Could not get canvas context"));
return;
}
const rad = degrees * Math.PI / 180;
const sin = Math.abs(Math.sin(rad));
const cos = Math.abs(Math.cos(rad));
const newWidth = img.width * cos + img.height * sin;
const newHeight = img.width * sin + img.height * cos;
canvas.width = newWidth;
canvas.height = newHeight;
ctx.translate(newWidth / 2, newHeight / 2);
ctx.rotate(rad);
ctx.drawImage(img, -img.width / 2, -img.height / 2);
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error("Failed to create blob"));
}, file.type);
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
},
/**
* Converts a URL to a File object by downloading the content.
* @param url - The URL to convert to a file
* @param filename - The name for the resulting file
* @param mimeType - The MIME type for the file
* @returns Promise resolving to a File object
* @example
* ```typescript
* const file = await ImageUtils.urlToFile(
* 'https://example.com/image.jpg',
* 'downloaded-image.jpg',
* 'image/jpeg'
* );
* ```
*/
async urlToFile(url, filename, mimeType) {
const response = await fetch(url);
const blob = await response.blob();
return new File([blob], filename, { type: mimeType });
},
/**
* Validates that image dimensions are within specified limits.
* @param width - The width to validate
* @param height - The height to validate
* @param maxWidth - The maximum allowed width
* @param maxHeight - The maximum allowed height
* @returns True if dimensions are within limits, false otherwise
* @example
* ```typescript
* const isValid = ImageUtils.validateImageDimensions(1920, 1080, 2000, 2000);
* if (!isValid) {
* console.log('Image is too large');
* }
* ```
*/
validateImageDimensions(width, height, maxWidth, maxHeight) {
return width <= maxWidth && height <= maxHeight;
}
};
exports.ImageUtils = ImageUtils;
//# sourceMappingURL=ImageUtils.js.map