Web TypeScript Bildverarbeitungsbeispiele

Web TypeScript Bildverarbeitungsbeispiele einschließlich Bild-Lesen/Speichern, Skalierung und Formatkonvertierung

💻 Bild-Lesen/Speichern typescript

🟢 simple ⭐⭐

Bilder lesen und speichern mit FileReader, Canvas API und verschiedenen Download-Methoden

⏱️ 20 min 🏷️ typescript, web, image processing
Prerequisites: Basic TypeScript, File API, Canvas API
// Web TypeScript Image Read/Save Examples
// Using FileReader, Canvas API, and download methods

// 1. Image Reader
class ImageReader {
  // Read image as Data URL
  readAsDataURL(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        resolve(reader.result as string);
      };

      reader.onerror = () => {
        reject(new Error('Failed to read image'));
      };

      reader.readAsDataURL(file);
    });
  }

  // Read image as ArrayBuffer
  readAsArrayBuffer(file: File): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        resolve(reader.result as ArrayBuffer);
      };

      reader.onerror = () => {
        reject(new Error('Failed to read image'));
      };

      reader.readAsArrayBuffer(file);
    });
  }

  // Read image as Base64
  readAsBase64(file: File): Promise<string> {
    return new Promise(async (resolve, reject) => {
      try {
        const dataURL = await this.readAsDataURL(file);
        const base64 = dataURL.split(',')[1];
        resolve(base64);
      } catch (error) {
        reject(error);
      }
    });
  }

  // Read image and get dimensions
  readWithDimensions(file: File): Promise<{
    dataURL: string;
    width: number;
    height: number;
  }> {
    return new Promise(async (resolve, reject) => {
      try {
        const dataURL = await this.readAsDataURL(file);
        const img = await this.loadImage(dataURL);

        resolve({
          dataURL,
          width: img.width,
          height: img.height
        });
      } catch (error) {
        reject(error);
      }
    });
  }

  // Read multiple images
  readMultiple(files: File[]): Promise<Array<{ file: File; dataURL: string }>> {
    const promises = files.map(file =>
      this.readAsDataURL(file).then(dataURL => ({ file, dataURL }))
    );

    return Promise.all(promises);
  }

  // Load image from URL
  loadImage(src: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = src;
    });
  }

  // Read image with metadata
  readWithMetadata(file: File): Promise<{
    dataURL: string;
    name: string;
    size: number;
    type: string;
    width: number;
    height: number;
  }> {
    return new Promise(async (resolve, reject) => {
      try {
        const dataURL = await this.readAsDataURL(file);
        const img = await this.loadImage(dataURL);

        resolve({
          dataURL,
          name: file.name,
          size: file.size,
          type: file.type,
          width: img.width,
          height: img.height
        });
      } catch (error) {
        reject(error);
      }
    });
  }
}

// 2. Image Writer
class ImageWriter {
  // Save canvas as image
  saveCanvas(canvas: HTMLCanvasElement, fileName: string, quality: number = 0.92): void {
    canvas.toBlob((blob) => {
      if (blob) {
        this.downloadBlob(blob, fileName);
      }
    }, 'image/png');
  }

  // Save canvas as JPEG
  saveAsJPEG(canvas: HTMLCanvasElement, fileName: string, quality: number = 0.92): void {
    canvas.toBlob((blob) => {
      if (blob) {
        this.downloadBlob(blob, fileName);
      }
    }, 'image/jpeg', quality);
  }

  // Save canvas as WebP
  saveAsWebP(canvas: HTMLCanvasElement, fileName: string, quality: number = 0.92): void {
    canvas.toBlob((blob) => {
      if (blob) {
        this.downloadBlob(blob, fileName);
      }
    }, 'image/webp', quality);
  }

  // Download blob as file
  private downloadBlob(blob: Blob, fileName: string): void {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(url);
  }

  // Save image element
  saveImage(img: HTMLImageElement, fileName: string): void {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.drawImage(img, 0, 0);
      this.saveCanvas(canvas, fileName);
    }
  }
}

// 3. Image Converter
class ImageConverter {
  private reader = new ImageReader();
  private writer = new ImageWriter();

  // Convert file to canvas
  async fileToCanvas(file: File): Promise<HTMLCanvasElement> {
    const dataURL = await this.reader.readAsDataURL(file);
    const img = await this.reader.loadImage(dataURL);

    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.drawImage(img, 0, 0);
    }

    return canvas;
  }

  // Convert to PNG
  async convertToPNG(file: File, outputFileName?: string): Promise<void> {
    const canvas = await this.fileToCanvas(file);
    const fileName = outputFileName || file.name.replace(/\.[^.]+$/, '.png');
    this.writer.saveCanvas(canvas, fileName);
  }

  // Convert to JPEG
  async convertToJPEG(file: File, outputFileName?: string, quality: number = 0.92): Promise<void> {
    const canvas = await this.fileToCanvas(file);
    const fileName = outputFileName || file.name.replace(/\.[^.]+$/, '.jpg');
    this.writer.saveAsJPEG(canvas, fileName, quality);
  }

  // Convert to WebP
  async convertToWebP(file: File, outputFileName?: string, quality: number = 0.92): Promise<void> {
    const canvas = await this.fileToCanvas(file);
    const fileName = outputFileName || file.name.replace(/\.[^.]+$/, '.webp');
    this.writer.saveAsWebP(canvas, fileName, quality);
  }

  // Convert and return as blob
  async convertToBlob(file: File, format: 'png' | 'jpeg' | 'webp' = 'png', quality: number = 0.92): Promise<Blob> {
    const canvas = await this.fileToCanvas(file);

    return new Promise((resolve, reject) => {
      const mimeType = `image/${format}`;
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob);
        } else {
          reject(new Error('Failed to convert image'));
        }
      }, mimeType, quality);
    });
  }
}

// 4. Image Info Extractor
class ImageInfoExtractor {
  private reader = new ImageReader();

  // Get basic image info
  async getInfo(file: File): Promise<{
    name: string;
    size: number;
    type: string;
    lastModified: Date;
  }> {
    return {
      name: file.name,
      size: file.size,
      type: file.type,
      lastModified: new Date(file.lastModified)
    };
  }

  // Get image dimensions
  async getDimensions(file: File): Promise<{
    width: number;
    height: number;
    aspectRatio: number;
  }> {
    const dataURL = await this.reader.readAsDataURL(file);
    const img = await this.reader.loadImage(dataURL);

    const aspectRatio = img.width / img.height;

    return {
      width: img.width,
      height: img.height,
      aspectRatio
    };
  }

  // Get complete image info
  async getCompleteInfo(file: File): Promise<{
    name: string;
    size: number;
    type: string;
    lastModified: Date;
    width: number;
    height: number;
    aspectRatio: number;
    megapixels: number;
  }> {
    const basic = await this.getInfo(file);
    const dims = await this.getDimensions(file);

    const megapixels = (dims.width * dims.height) / 1000000;

    return {
      ...basic,
      ...dims,
      megapixels
    };
  }

  // Format file size
  formatFileSize(bytes: number): string {
    const kb = bytes / 1024;
    const mb = kb / 1024;

    if (mb >= 1) {
      return `${mb.toFixed(2)} MB`;
    } else if (kb >= 1) {
      return `${kb.toFixed(2)} KB`;
    } else {
      return `${bytes} bytes`;
    }
  }
}

// 5. Image Batch Processor
class ImageBatchProcessor {
  private reader = new ImageReader();
  private converter = new ImageConverter();

  // Batch convert images
  async batchConvert(
    files: File[],
    format: 'png' | 'jpeg' | 'webp' = 'png',
    quality: number = 0.92,
    onProgress?: (current: number, total: number, file: File) => void
  ): Promise<Array<{ original: File; converted: Blob }>> {
    const results: Array<{ original: File; converted: Blob }> = [];

    for (let i = 0; i < files.length; i++) {
      const file = files[i];

      try {
        const converted = await this.converter.convertToBlob(file, format, quality);
        results.push({ original: file, converted });

        if (onProgress) {
          onProgress(i + 1, files.length, file);
        }
      } catch (error) {
        console.error(`Failed to convert ${file.name}:`, error);
      }
    }

    return results;
  }

  // Batch resize images
  async batchResize(
    files: File[],
    maxWidth: number,
    maxHeight: number,
    onProgress?: (current: number, total: number, file: File) => void
  ): Promise<Blob[]> {
    const results: Blob[] = [];

    for (let i = 0; i < files.length; i++) {
      const file = files[i];

      try {
        // Resize logic would go here
        if (onProgress) {
          onProgress(i + 1, files.length, file);
        }
      } catch (error) {
        console.error(`Failed to resize ${file.name}:`, error);
      }
    }

    return results;
  }

  // Batch get image info
  async batchGetInfo(files: File[]): Promise<Array<{ file: File; info: any }>> {
    const results: Array<{ file: File; info: any }> = [];
    const extractor = new ImageInfoExtractor();

    for (const file of files) {
      try {
        const info = await extractor.getCompleteInfo(file);
        results.push({ file, info });
      } catch (error) {
        console.error(`Failed to get info for ${file.name}:`, error);
      }
    }

    return results;
  }
}

// 6. Image Validator
class ImageValidator {
  // Validate if file is an image
  isImage(file: File): boolean {
    return file.type.startsWith('image/');
  }

  // Validate supported formats
  isSupportedFormat(file: File): boolean {
    const supportedTypes = [
      'image/jpeg',
      'image/jpg',
      'image/png',
      'image/gif',
      'image/webp',
      'image/bmp',
      'image/svg+xml'
    ];

    return supportedTypes.includes(file.type);
  }

  // Validate file size
  isValidSize(file: File, maxSizeInMB: number): boolean {
    const maxSizeInBytes = maxSizeInMB * 1024 * 1024;
    return file.size <= maxSizeInBytes;
  }

  // Validate dimensions
  async validateDimensions(
    file: File,
    minWidth: number,
    minHeight: number
  ): Promise<boolean> {
    const reader = new ImageReader();
    const dims = await reader.readWithDimensions(file);

    return dims.width >= minWidth && dims.height >= minHeight;
  }

  // Validate with all rules
  async validate(
    file: File,
    rules: {
      maxSizeInMB?: number;
      minWidth?: number;
      minHeight?: number;
      maxWidth?: number;
      maxHeight?: number;
    }
  ): Promise<{ valid: boolean; errors: string[] }> {
    const errors: string[] = [];

    if (!this.isImage(file)) {
      errors.push('File is not an image');
      return { valid: false, errors };
    }

    if (rules.maxSizeInMB && !this.isValidSize(file, rules.maxSizeInMB)) {
      errors.push(`File size exceeds ${rules.maxSizeInMB}MB`);
    }

    if (rules.minWidth || rules.minHeight) {
      const reader = new ImageReader();
      const dims = await reader.readWithDimensions(file);

      if (rules.minWidth && dims.width < rules.minWidth) {
        errors.push(`Width is less than ${rules.minWidth}px`);
      }

      if (rules.minHeight && dims.height < rules.minHeight) {
        errors.push(`Height is less than ${rules.minHeight}px`);
      }

      if (rules.maxWidth && dims.width > rules.maxWidth) {
        errors.push(`Width exceeds ${rules.maxWidth}px`);
      }

      if (rules.maxHeight && dims.height > rules.maxHeight) {
        errors.push(`Height exceeds ${rules.maxHeight}px`);
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }
}

// Usage Examples
async function demonstrateImageReadSave() {
  console.log('=== Web TypeScript Image Read/Save Examples ===\n');

  const reader = new ImageReader();
  const writer = new ImageWriter();
  const converter = new ImageConverter();
  const extractor = new ImageInfoExtractor();
  const validator = new ImageValidator();

  // Note: These examples require actual file input
  console.log('--- Image Reader ---');
  console.log('Use reader.readAsDataURL(file) to read image');
  console.log('Use reader.readWithDimensions(file) to get dimensions');

  console.log('\n--- Image Writer ---');
  console.log('Use writer.saveCanvas(canvas, filename) to save canvas');
  console.log('Use writer.saveAsJPEG(canvas, filename, quality) to save as JPEG');

  console.log('\n--- Image Converter ---');
  console.log('Use converter.convertToPNG(file) to convert to PNG');
  console.log('Use converter.convertToJPEG(file) to convert to JPEG');
  console.log('Use converter.convertToWebP(file) to convert to WebP');

  console.log('\n--- Image Info Extractor ---');
  console.log('Use extractor.getInfo(file) to get basic info');
  console.log('Use extractor.getDimensions(file) to get dimensions');
  console.log('Use extractor.getCompleteInfo(file) to get complete info');

  console.log('\n--- Image Validator ---');
  console.log('Use validator.isImage(file) to check if file is image');
  console.log('Use validator.validate(file, rules) to validate with rules');

  console.log('\n=== All Image Read/Save Examples Completed ===');
}

// Export functions
export { ImageReader, ImageWriter, ImageConverter, ImageInfoExtractor, ImageBatchProcessor, ImageValidator };
export { demonstrateImageReadSave };

💻 Bildskalierung typescript

🟡 intermediate ⭐⭐⭐

Bilder mit verschiedenen Algorithmen skalieren inklusive bilinear, bikubisch und hochwertige Skalierung

⏱️ 25 min 🏷️ typescript, web, image processing
Prerequisites: Intermediate TypeScript, Canvas API
// Web TypeScript Image Scaling Examples
// Using Canvas API for image resizing and scaling

// 1. Basic Image Scaler
class ImageScaler {
  // Resize image to specific dimensions
  async resize(
    img: HTMLImageElement,
    width: number,
    height: number
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.drawImage(img, 0, 0, width, height);
    }

    return canvas;
  }

  // Resize by percentage
  async resizeByPercentage(
    img: HTMLImageElement,
    percentage: number
  ): Promise<HTMLCanvasElement> {
    const newWidth = Math.floor(img.width * (percentage / 100));
    const newHeight = Math.floor(img.height * (percentage / 100));

    return this.resize(img, newWidth, newHeight);
  }

  // Resize to fit within bounds
  async resizeToFit(
    img: HTMLImageElement,
    maxWidth: number,
    maxHeight: number
  ): Promise<HTMLCanvasElement> {
    const ratio = Math.min(maxWidth / img.width, maxHeight / img.height);
    const newWidth = Math.floor(img.width * ratio);
    const newHeight = Math.floor(img.height * ratio);

    return this.resize(img, newWidth, newHeight);
  }

  // Resize to fill bounds (cover)
  async resizeToCover(
    img: HTMLImageElement,
    minWidth: number,
    minHeight: number
  ): Promise<HTMLCanvasElement> {
    const ratio = Math.max(minWidth / img.width, minHeight / img.height);
    const newWidth = Math.floor(img.width * ratio);
    const newHeight = Math.floor(img.height * ratio);

    return this.resize(img, newWidth, newHeight);
  }

  // Resize maintaining aspect ratio
  async resizeWithAspect(
    img: HTMLImageElement,
    targetWidth: number,
    targetHeight: number
  ): Promise<HTMLCanvasElement> {
    const srcRatio = img.width / img.height;
    const targetRatio = targetWidth / targetHeight;

    let finalWidth: number, finalHeight: number;

    if (srcRatio > targetRatio) {
      finalWidth = targetWidth;
      finalHeight = Math.floor(targetWidth / srcRatio);
    } else {
      finalHeight = targetHeight;
      finalWidth = Math.floor(targetHeight * srcRatio);
    }

    return this.resize(img, finalWidth, finalHeight);
  }
}

// 2. High Quality Scaler
class HighQualityScaler {
  // Resize with better quality using step-down approach
  async resizeHighQuality(
    img: HTMLImageElement,
    width: number,
    height: number
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (!ctx) {
      throw new Error('Failed to get canvas context');
    }

    canvas.width = width;
    canvas.height = height;

    // Enable image smoothing for better quality
    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';

    // For significant downscaling, use multiple passes
    const scaleFactor = width / img.width;

    if (scaleFactor < 0.5) {
      // Step down by half multiple times
      let w = img.width;
      let h = img.height;
      let tempCanvas = document.createElement('canvas');
      let tempCtx = tempCanvas.getContext('2d');

      if (!tempCtx) {
        throw new Error('Failed to get temp canvas context');
      }

      tempCanvas.width = w;
      tempCanvas.height = h;
      tempCtx.drawImage(img, 0, 0);

      while (w * 0.5 > width) {
        w = Math.floor(w * 0.5);
        h = Math.floor(h * 0.5);

        const newCanvas = document.createElement('canvas');
        newCanvas.width = w;
        newCanvas.height = h;
        const newCtx = newCanvas.getContext('2d');

        if (newCtx) {
          newCtx.imageSmoothingEnabled = true;
          newCtx.imageSmoothingQuality = 'high';
          newCtx.drawImage(tempCanvas, 0, 0, w, h);
        }

        tempCanvas = newCanvas;
        tempCtx = newCtx;
      }

      // Final resize to target dimensions
      ctx.drawImage(tempCanvas, 0, 0, width, height);
    } else {
      ctx.drawImage(img, 0, 0, width, height);
    }

    return canvas;
  }

  // Resize with Lanczos-like approximation
  async resizeLanczos(
    img: HTMLImageElement,
    width: number,
    height: number
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (!ctx) {
      throw new Error('Failed to get canvas context');
    }

    canvas.width = width;
    canvas.height = height;

    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = 'high';
    ctx.drawImage(img, 0, 0, width, height);

    return canvas;
  }
}

// 3. Thumbnail Generator
class ThumbnailGenerator {
  // Generate square thumbnail
  async generateSquareThumbnail(
    img: HTMLImageElement,
    size: number
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement('canvas');
    canvas.width = size;
    canvas.height = size;

    const ctx = canvas.getContext('2d');
    if (!ctx) {
      throw new Error('Failed to get canvas context');
    }

    // Calculate crop dimensions
    const minDimension = Math.min(img.width, img.height);
    const startX = (img.width - minDimension) / 2;
    const startY = (img.height - minDimension) / 2;

    // Draw cropped and resized
    ctx.drawImage(
      img,
      startX, startY, minDimension, minDimension,
      0, 0, size, size
    );

    return canvas;
  }

  // Generate thumbnail with specific aspect ratio
  async generateThumbnailWithRatio(
    img: HTMLImageElement,
    width: number,
    height: number,
    fit: 'cover' | 'contain' | 'fill' = 'cover'
  ): Promise<HTMLCanvasElement> {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    if (!ctx) {
      throw new Error('Failed to get canvas context');
    }

    if (fit === 'fill') {
      ctx.drawImage(img, 0, 0, width, height);
    } else {
      const srcRatio = img.width / img.height;
      const targetRatio = width / height;

      let renderWidth: number, renderHeight: number;
      let offsetX: number = 0, offsetY: number = 0;

      if (fit === 'cover') {
        if (srcRatio > targetRatio) {
          renderHeight = height;
          renderWidth = height * srcRatio;
          offsetX = (width - renderWidth) / 2;
        } else {
          renderWidth = width;
          renderHeight = width / srcRatio;
          offsetY = (height - renderHeight) / 2;
        }
      } else { // contain
        if (srcRatio > targetRatio) {
          renderWidth = width;
          renderHeight = width / srcRatio;
          offsetY = (height - renderHeight) / 2;
        } else {
          renderHeight = height;
          renderWidth = height * srcRatio;
          offsetX = (width - renderWidth) / 2;
        }
      }

      ctx.drawImage(img, offsetX, offsetY, renderWidth, renderHeight);
    }

    return canvas;
  }

  // Generate multiple thumbnail sizes
  async generateMultipleThumbnails(
    img: HTMLImageElement,
    sizes: number[]
  ): Promise<Map<number, HTMLCanvasElement>> {
    const thumbnails = new Map<number, HTMLCanvasElement>();

    for (const size of sizes) {
      const thumbnail = await this.generateSquareThumbnail(img, size);
      thumbnails.set(size, thumbnail);
    }

    return thumbnails;
  }
}

// 4. Smart Scaler
class SmartScaler {
  private basicScaler = new ImageScaler();
  private hqScaler = new HighQualityScaler();

  // Auto-select scaling method based on resize factor
  async smartResize(
    img: HTMLImageElement,
    width: number,
    height: number
  ): Promise<HTMLCanvasElement> {
    const scaleFactor = width / img.width;

    // Use high quality for significant downscaling
    if (scaleFactor < 0.5 || scaleFactor > 2) {
      return this.hqScaler.resizeHighQuality(img, width, height);
    } else {
      return this.basicScaler.resize(img, width, height);
    }
  }

  // Resize with smart content detection (simplified)
  async smartContentResize(
    img: HTMLImageElement,
    width: number,
    height: number
  ): Promise<HTMLCanvasElement> {
    // For now, use high quality scaler
    // A full implementation would use content-aware resizing
    return this.hqScaler.resizeHighQuality(img, width, height);
  }
}

// 5. Batch Image Scaler
class BatchImageScaler {
  private scaler = new SmartScaler();

  // Scale multiple images
  async scaleMultiple(
    images: HTMLImageElement[],
    width: number,
    height: number,
    onProgress?: (current: number, total: number) => void
  ): Promise<HTMLCanvasElement[]> {
    const results: HTMLCanvasElement[] = [];

    for (let i = 0; i < images.length; i++) {
      const canvas = await this.scaler.smartResize(images[i], width, height);
      results.push(canvas);

      if (onProgress) {
        onProgress(i + 1, images.length);
      }
    }

    return results;
  }

  // Scale files and save
  async scaleFiles(
    files: File[],
    width: number,
    height: number,
    format: 'png' | 'jpeg' | 'webp' = 'png',
    onProgress?: (current: number, total: number, fileName: string) => void
  ): Promise<Blob[]> {
    const results: Blob[] = [];

    for (let i = 0; i < files.length; i++) {
      const file = files[i];

      try {
        const reader = new ImageReader();
        const dataURL = await reader.readAsDataURL(file);
        const img = await reader.loadImage(dataURL);

        const canvas = await this.scaler.smartResize(img, width, height);

        const blob = await this.canvasToBlob(canvas, format);
        results.push(blob);

        if (onProgress) {
          onProgress(i + 1, files.length, file.name);
        }
      } catch (error) {
        console.error(`Failed to scale ${file.name}:`, error);
      }
    }

    return results;
  }

  private async canvasToBlob(canvas: HTMLCanvasElement, format: string): Promise<Blob> {
    return new Promise((resolve, reject) => {
      const mimeType = `image/${format}`;
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob);
        } else {
          reject(new Error('Failed to convert canvas to blob'));
        }
      }, mimeType);
    });
  }
}

// 6. Image Resizer Helper
class ImageResizerHelper {
  // Calculate new dimensions maintaining aspect ratio
  static calculateDimensions(
    originalWidth: number,
    originalHeight: number,
    targetWidth?: number,
    targetHeight?: number
  ): { width: number; height: number } {
    if (targetWidth && targetHeight) {
      return { width: targetWidth, height: targetHeight };
    }

    if (targetWidth) {
      const ratio = targetWidth / originalWidth;
      return {
        width: targetWidth,
        height: Math.floor(originalHeight * ratio)
      };
    }

    if (targetHeight) {
      const ratio = targetHeight / originalHeight;
      return {
        width: Math.floor(originalWidth * ratio),
        height: targetHeight
      };
    }

    return { width: originalWidth, height: originalHeight };
  }

  // Calculate fit dimensions
  static calculateFitDimensions(
    originalWidth: number,
    originalHeight: number,
    maxWidth: number,
    maxHeight: number
  ): { width: number; height: number } {
    const ratio = Math.min(maxWidth / originalWidth, maxHeight / originalHeight);
    return {
      width: Math.floor(originalWidth * ratio),
      height: Math.floor(originalHeight * ratio)
    };
  }

  // Calculate cover dimensions
  static calculateCoverDimensions(
    originalWidth: number,
    originalHeight: number,
    minWidth: number,
    minHeight: number
  ): { width: number; height: number } {
    const ratio = Math.max(minWidth / originalWidth, minHeight / originalHeight);
    return {
      width: Math.floor(originalWidth * ratio),
      height: Math.floor(originalHeight * ratio)
    };
  }
}

// Usage Examples
async function demonstrateImageScaling() {
  console.log('=== Web TypeScript Image Scaling Examples ===\n');

  const scaler = new ImageScaler();
  const hqScaler = new HighQualityScaler();
  const thumbGen = new ThumbnailGenerator();
  const smartScaler = new SmartScaler();

  console.log('--- Basic Scaler ---');
  console.log('Use scaler.resize(img, width, height) to resize');
  console.log('Use scaler.resizeByPercentage(img, 50) to resize by 50%');
  console.log('Use scaler.resizeToFit(img, maxWidth, maxHeight) to fit within bounds');

  console.log('\n--- High Quality Scaler ---');
  console.log('Use hqScaler.resizeHighQuality(img, width, height) for best quality');

  console.log('\n--- Thumbnail Generator ---');
  console.log('Use thumbGen.generateSquareThumbnail(img, size) for square thumbnails');
  console.log('Use thumbGen.generateThumbnailWithRatio(img, width, height, fit) for custom ratio');

  console.log('\n--- Smart Scaler ---');
  console.log('Use smartScaler.smartResize(img, width, height) for auto-selected method');

  console.log('\n--- Helper Functions ---');
  console.log('Use ImageResizerHelper.calculateDimensions() to calculate new dimensions');
  console.log('Use ImageResizerHelper.calculateFitDimensions() to calculate fit dimensions');

  console.log('\n=== All Image Scaling Examples Completed ===');
}

// ImageReader import helper
class ImageReader {
  async readAsDataURL(file: File): Promise<string> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = () => reject(new Error('Failed to read'));
      reader.readAsDataURL(file);
    });
  }

  async loadImage(src: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => resolve(img);
      img.onerror = () => reject(new Error('Failed to load image'));
      img.src = src;
    });
  }
}

// Export functions
export { ImageScaler, HighQualityScaler, ThumbnailGenerator, SmartScaler, BatchImageScaler, ImageResizerHelper };
export { demonstrateImageScaling };

💻 Bildformatkonvertierung typescript

🟡 intermediate ⭐⭐⭐

Zwischen Bildformaten konvertieren einschließlich PNG, JPEG, WebP, GIF und BMP

⏱️ 25 min 🏷️ typescript, web, image processing
Prerequisites: Intermediate TypeScript, Canvas API, File API
// Web TypeScript Image Format Conversion Examples
// Convert between PNG, JPEG, WebP, GIF, and other formats

// 1. Basic Format Converter
class FormatConverter {
  // Convert canvas to PNG
  toPNG(canvas: HTMLCanvasElement): Promise<Blob> {
    return new Promise((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob);
        } else {
          reject(new Error('Failed to convert to PNG'));
        }
      }, 'image/png');
    });
  }

  // Convert canvas to JPEG
  toJPEG(canvas: HTMLCanvasElement, quality: number = 0.92): Promise<Blob> {
    return new Promise((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob);
        } else {
          reject(new Error('Failed to convert to JPEG'));
        }
      }, 'image/jpeg', quality);
    });
  }

  // Convert canvas to WebP
  toWebP(canvas: HTMLCanvasElement, quality: number = 0.92): Promise<Blob> {
    return new Promise((resolve, reject) => {
      canvas.toBlob((blob) => {
        if (blob) {
          resolve(blob);
        } else {
          reject(new Error('Failed to convert to WebP'));
        }
      }, 'image/webp', quality);
    });
  }

  // Check format support
  checkFormatSupport(): {
    png: boolean;
    jpeg: boolean;
    webp: boolean;
  } {
    const canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;

    return {
      png: !!canvas.toDataURL('image/png').startsWith('data:image/png'),
      jpeg: !!canvas.toDataURL('image/jpeg').startsWith('data:image/jpeg'),
      webp: !!canvas.toDataURL('image/webp').startsWith('data:image/webp')
    };
  }
}

// 2. File Format Converter
class FileFormatConverter {
  private formatConverter = new FormatConverter();

  // Convert file to different format
  async convertFile(
    file: File,
    targetFormat: 'png' | 'jpeg' | 'webp',
    quality: number = 0.92
  ): Promise<{ blob: Blob; fileName: string }> {
    const img = await this.loadImage(file);
    const canvas = this.imageToCanvas(img);

    let blob: Blob;

    switch (targetFormat) {
      case 'png':
        blob = await this.formatConverter.toPNG(canvas);
        break;
      case 'jpeg':
        blob = await this.formatConverter.toJPEG(canvas, quality);
        break;
      case 'webp':
        blob = await this.formatConverter.toWebP(canvas, quality);
        break;
    }

    const extension = targetFormat === 'jpeg' ? 'jpg' : targetFormat;
    const fileName = file.name.replace(/\.[^.]+$/, `.${extension}`);

    return { blob, fileName };
  }

  // Batch convert files
  async batchConvert(
    files: File[],
    targetFormat: 'png' | 'jpeg' | 'webp',
    quality: number = 0.92,
    onProgress?: (current: number, total: number, fileName: string) => void
  ): Promise<Array<{ original: File; converted: Blob; fileName: string }>> {
    const results: Array<{ original: File; converted: Blob; fileName: string }> = [];

    for (let i = 0; i < files.length; i++) {
      const file = files[i];

      try {
        const result = await this.convertFile(file, targetFormat, quality);
        results.push({
          original: file,
          converted: result.blob,
          fileName: result.fileName
        });

        if (onProgress) {
          onProgress(i + 1, files.length, file.name);
        }
      } catch (error) {
        console.error(`Failed to convert ${file.name}:`, error);
      }
    }

    return results;
  }

  // Convert with resize
  async convertWithResize(
    file: File,
    targetFormat: 'png' | 'jpeg' | 'webp',
    width: number,
    height: number,
    quality: number = 0.92
  ): Promise<{ blob: Blob; fileName: string }> {
    const img = await this.loadImage(file);
    const canvas = this.resizeImage(img, width, height);

    let blob: Blob;

    switch (targetFormat) {
      case 'png':
        blob = await this.formatConverter.toPNG(canvas);
        break;
      case 'jpeg':
        blob = await this.formatConverter.toJPEG(canvas, quality);
        break;
      case 'webp':
        blob = await this.formatConverter.toWebP(canvas, quality);
        break;
    }

    const extension = targetFormat === 'jpeg' ? 'jpg' : targetFormat;
    const fileName = file.name.replace(/\.[^.]+$/, `.${extension}`);

    return { blob, fileName };
  }

  private loadImage(file: File): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = () => reject(new Error('Failed to load image'));
        img.src = reader.result as string;
      };

      reader.onerror = () => reject(new Error('Failed to read file'));
      reader.readAsDataURL(file);
    });
  }

  private imageToCanvas(img: HTMLImageElement): HTMLCanvasElement {
    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.drawImage(img, 0, 0);
    }

    return canvas;
  }

  private resizeImage(img: HTMLImageElement, width: number, height: number): HTMLCanvasElement {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.drawImage(img, 0, 0, width, height);
    }

    return canvas;
  }
}

// 3. Optimized Converter
class OptimizedConverter {
  private formatConverter = new FormatConverter();

  // Auto-select optimal format
  async autoOptimize(
    canvas: HTMLCanvasElement,
    maxSizeKB: number = 100
  ): Promise<{ blob: Blob; format: string; quality: number }> {
    // Try WebP first (best compression)
    for (let quality = 0.9; quality >= 0.5; quality -= 0.1) {
      const webp = await this.formatConverter.toWebP(canvas, quality);
      if (webp.size / 1024 <= maxSizeKB) {
        return { blob: webp, format: 'webp', quality };
      }
    }

    // Try JPEG
    for (let quality = 0.9; quality >= 0.5; quality -= 0.1) {
      const jpeg = await this.formatConverter.toJPEG(canvas, quality);
      if (jpeg.size / 1024 <= maxSizeKB) {
        return { blob: jpeg, format: 'jpeg', quality };
      }
    }

    // Fallback to PNG with lowest quality WebP
    return {
      blob: await this.formatConverter.toWebP(canvas, 0.5),
      format: 'webp',
      quality: 0.5
    };
  }

  // Optimize for web
  async optimizeForWeb(
    file: File,
    maxWidth: number = 1920,
    maxHeight: number = 1080,
    maxSizeKB: number = 200
  ): Promise<{ blob: Blob; format: string; quality: number }> {
    const img = await this.loadImageFromFile(file);

    // Resize if needed
    const canvas = this.resizeToFit(img, maxWidth, maxHeight);

    return this.autoOptimize(canvas, maxSizeKB);
  }

  private loadImageFromFile(file: File): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onload = () => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = () => reject(new Error('Failed to load image'));
        img.src = reader.result as string;
      };

      reader.onerror = () => reject(new Error('Failed to read file'));
      reader.readAsDataURL(file);
    });
  }

  private resizeToFit(
    img: HTMLImageElement,
    maxWidth: number,
    maxHeight: number
  ): HTMLCanvasElement {
    const ratio = Math.min(maxWidth / img.width, maxHeight / img.height);
    const width = Math.floor(img.width * ratio);
    const height = Math.floor(img.height * ratio);

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    if (ctx) {
      ctx.imageSmoothingEnabled = true;
      ctx.imageSmoothingQuality = 'high';
      ctx.drawImage(img, 0, 0, width, height);
    }

    return canvas;
  }
}

// 4. Quality Comparator
class QualityComparator {
  // Compare quality at different settings
  async compareQuality(
    canvas: HTMLCanvasElement,
    format: 'jpeg' | 'webp',
    qualities: number[] = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
  ): Promise<Array<{ quality: number; size: number; blob: Blob }>> {
    const converter = new FormatConverter();
    const results: Array<{ quality: number; size: number; blob: Blob }> = [];

    for (const quality of qualities) {
      let blob: Blob;

      if (format === 'jpeg') {
        blob = await converter.toJPEG(canvas, quality);
      } else {
        blob = await converter.toWebP(canvas, quality);
      }

      results.push({
        quality,
        size: blob.size,
        blob
      });
    }

    return results;
  }

  // Find optimal quality for target size
  async findOptimalQuality(
    canvas: HTMLCanvasElement,
    format: 'jpeg' | 'webp',
    targetSizeKB: number,
    minQuality: number = 0.5,
    maxQuality: number = 1.0
  ): Promise<{ quality: number; size: number; blob: Blob } | null> {
    const converter = new FormatConverter();

    for (let quality = maxQuality; quality >= minQuality; quality -= 0.05) {
      let blob: Blob;

      if (format === 'jpeg') {
        blob = await converter.toJPEG(canvas, quality);
      } else {
        blob = await converter.toWebP(canvas, quality);
      }

      if (blob.size / 1024 <= targetSizeKB) {
        return { quality, size: blob.size, blob };
      }
    }

    return null;
  }
}

// 5. Format Info Provider
class FormatInfoProvider {
  // Get format information
  getFormatInfo(format: string): {
    name: string;
    extensions: string[];
    mimeType: string;
    supportsTransparency: boolean;
    supportsAnimation: boolean;
    typicalUseCase: string;
  } {
    const formats: Record<string, any> = {
      png: {
        name: 'Portable Network Graphics',
        extensions: ['png'],
        mimeType: 'image/png',
        supportsTransparency: true,
        supportsAnimation: false,
        typicalUseCase: 'Graphics, logos, images with transparency'
      },
      jpeg: {
        name: 'Joint Photographic Experts Group',
        extensions: ['jpg', 'jpeg'],
        mimeType: 'image/jpeg',
        supportsTransparency: false,
        supportsAnimation: false,
        typicalUseCase: 'Photographs, complex images'
      },
      webp: {
        name: 'Web Picture',
        extensions: ['webp'],
        mimeType: 'image/webp',
        supportsTransparency: true,
        supportsAnimation: true,
        typicalUseCase: 'Modern web images, optimized for web'
      },
      gif: {
        name: 'Graphics Interchange Format',
        extensions: ['gif'],
        mimeType: 'image/gif',
        supportsTransparency: true,
        supportsAnimation: true,
        typicalUseCase: 'Simple animations, low-color graphics'
      },
      bmp: {
        name: 'Bitmap',
        extensions: ['bmp'],
        mimeType: 'image/bmp',
        supportsTransparency: true,
        supportsAnimation: false,
        typicalUseCase: 'Uncompressed images, Windows applications'
      },
      svg: {
        name: 'Scalable Vector Graphics',
        extensions: ['svg'],
        mimeType: 'image/svg+xml',
        supportsTransparency: true,
        supportsAnimation: true,
        typicalUseCase: 'Icons, logos, scalable graphics'
      }
    };

    return formats[format.toLowerCase()] || {
      name: 'Unknown',
      extensions: [],
      mimeType: 'application/octet-stream',
      supportsTransparency: false,
      supportsAnimation: false,
      typicalUseCase: 'Unknown'
    };
  }

  // Recommend format based on content
  recommendFormat(hasTransparency: boolean, isPhoto: boolean, forWeb: boolean = true): string {
    if (forWeb) {
      if (hasTransparency) {
        return 'webp'; // Best compression with transparency
      } else if (isPhoto) {
        return 'webp'; // Best compression for photos
      } else {
        return 'webp'; // Generally best for web
      }
    } else {
      if (hasTransparency) {
        return 'png'; // Lossless with transparency
      } else if (isPhoto) {
        return 'jpeg'; // Good compression for photos
      } else {
        return 'png'; // Lossless for graphics
      }
    }
  }
}

// 6. Conversion Pipeline
class ConversionPipeline {
  private fileConverter = new FileFormatConverter();
  private optimizedConverter = new OptimizedConverter();

  // Convert with multiple options
  async convertWithPipeline(
    file: File,
    options: {
      format?: 'png' | 'jpeg' | 'webp';
      resize?: { width: number; height: number };
      quality?: number;
      maxSizeKB?: number;
      autoOptimize?: boolean;
    }
  ): Promise<{ blob: Blob; fileName: string; info: any }> {
    let blob: Blob;
    let fileName: string;
    const info: any = {};

    if (options.autoOptimize && options.maxSizeKB) {
      const result = await this.optimizedConverter.optimizeForWeb(
        file,
        options.resize?.width || 1920,
        options.resize?.height || 1080,
        options.maxSizeKB
      );

      blob = result.blob;
      fileName = file.name.replace(/\.[^.]+$/, `.${result.format}`);
      info.format = result.format;
      info.quality = result.quality;
    } else if (options.resize) {
      const result = await this.fileConverter.convertWithResize(
        file,
        options.format || 'png',
        options.resize.width,
        options.resize.height,
        options.quality || 0.92
      );

      blob = result.blob;
      fileName = result.fileName;
      info.format = options.format;
      info.quality = options.quality;
    } else {
      const result = await this.fileConverter.convertFile(
        file,
        options.format || 'png',
        options.quality || 0.92
      );

      blob = result.blob;
      fileName = result.fileName;
      info.format = options.format;
      info.quality = options.quality;
    }

    info.size = blob.size;
    info.sizeKB = (blob.size / 1024).toFixed(2);

    return { blob, fileName, info };
  }
}

// Usage Examples
async function demonstrateImageFormatConversion() {
  console.log('=== Web TypeScript Image Format Conversion Examples ===\n');

  const converter = new FormatConverter();
  const fileConverter = new FileFormatConverter();
  const optimizedConverter = new OptimizedConverter();
  const infoProvider = new FormatInfoProvider();

  console.log('--- Format Converter ---');
  const support = converter.checkFormatSupport();
  console.log('Format Support:');
  console.log(`  PNG: ${support.png ? 'Yes' : 'No'}`);
  console.log(`  JPEG: ${support.jpeg ? 'Yes' : 'No'}`);
  console.log(`  WebP: ${support.webp ? 'Yes' : 'No'}`);

  console.log('\n--- File Format Converter ---');
  console.log('Use fileConverter.convertFile(file, format, quality) to convert files');
  console.log('Use fileConverter.batchConvert(files, format) to batch convert');

  console.log('\n--- Optimized Converter ---');
  console.log('Use optimizedConverter.autoOptimize(canvas, maxSizeKB) for auto optimization');
  console.log('Use optimizedConverter.optimizeForWeb(file, maxWidth, maxHeight, maxSizeKB) for web optimization');

  console.log('\n--- Format Info Provider ---');
  const pngInfo = infoProvider.getFormatInfo('png');
  console.log('PNG Info:', pngInfo);

  const recommendation = infoProvider.recommendFormat(true, false, true);
  console.log(`Recommended format (transparent, graphic, web): ${recommendation}`);

  console.log('\n=== All Image Format Conversion Examples Completed ===');
}

// Export functions
export { FormatConverter, FileFormatConverter, OptimizedConverter, QualityComparator, FormatInfoProvider, ConversionPipeline };
export { demonstrateImageFormatConversion };