Работа с изображениями: resize, crop на TypeScript

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с изображениями на TypeScript: изменение размера (resize) и обрезка (crop)

В современной веб-разработке обработка изображений на стороне клиента — частая задача. TypeScript, благодаря строгой типизации и интеграции с современными API браузера, позволяет выполнять операции resize (изменение размера) и crop (обрезка) безопасно и эффективно. В этой статье мы разберём два подхода: использование нативного Canvas API и работу с серверными библиотеками sharp и jimp для Node.js.

1. Canvas API: клиентская обработка без зависимостей

Браузерный HTMLCanvasElement — самый лёгкий способ манипулировать изображениями. Он не требует установки дополнительных пакетов и работает в любом современном браузере.

1.1. Изменение размера (resize) через Canvas

Основная идея — нарисовать исходное изображение на canvas с новыми размерами, а затем экспортировать результат.

function resizeImage(
  source: HTMLImageElement,
  targetWidth: number,
  targetHeight: number
): Promise<Blob> {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas');
    canvas.width = targetWidth;
    canvas.height = targetHeight;
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      reject(new Error('Canvas context not supported'));
      return;
    }
    // Рисуем изображение с новыми размерами
    ctx.drawImage(source, 0, 0, targetWidth, targetHeight);
    // Экспортируем в Blob (формат по умолчанию - PNG)
    canvas.toBlob((blob) => {
      if (blob) resolve(blob);
      else reject(new Error('Failed to create blob'));
    }, 'image/jpeg', 0.9); // можно указать формат и качество
  });
}

Важно: При увеличении изображения (upscale) качество может ухудшиться — canvas использует билинейную интерполяцию. Для превью это приемлемо, для печати — нет.

1.2. Обрезка (crop) с координатами

Обрезка требует указать прямоугольную область исходного изображения.

function cropImage(
  source: HTMLImageElement,
  x: number,
  y: number,
  width: number,
  height: number
): Promise<Blob> {
  return new Promise((resolve, reject) => {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      reject(new Error('Canvas not supported'));
      return;
    }
    // Вырезаем фрагмент из исходного изображения
    ctx.drawImage(source, x, y, width, height, 0, 0, width, height);
    canvas.toBlob((blob) => {
      if (blob) resolve(blob);
      else reject(new Error('Blob creation failed'));
    }, 'image/png');
  });
}

Совет: Всегда проверяйте, что координаты x, y, width, height не выходят за границы исходного изображения, иначе canvas вернёт пустые области.

2. Серверная обработка: sharp и jimp

Если вы работаете в среде Node.js и TypeScript, библиотеки sharp (C++ native, высокая производительность) и jimp (чистый JS, кроссплатформенность) — стандартные решения.

2.1. Установка и импорт

// Установка
// npm install sharp
// npm install jimp

import sharp from 'sharp';
import Jimp from 'jimp';

2.2. Изменение размера (resize) с sharp

async function resizeWithSharp(
  inputPath: string,
  outputPath: string,
  width: number,
  height: number
): Promise<void> {
  await sharp(inputPath)
    .resize(width, height, {
      fit: 'cover',      // 'cover' - обрезает лишнее, 'contain' - вписывает
      position: 'center' // точка отсчёта при обрезке
    })
    .toFile(outputPath);
}

Параметр fit определяет стратегию вписывания:

  • cover — масштабирует и обрезает, чтобы полностью заполнить заданные размеры;
  • contain — вписывает изображение целиком, возможны пустые полосы;
  • fill — искажает пропорции, растягивая до точных размеров.

2.3. Обрезка (crop) с sharp

async function cropWithSharp(
  inputPath: string,
  outputPath: string,
  left: number,
  top: number,
  width: number,
  height: number
): Promise<void> {
  await sharp(inputPath)
    .extract({ left, top, width, height })
    .to                    

Рекомендаций