dr.Brain

doctor Brain

мир глазами веб-разработчика

Как обрезать картинку

Как обрезать изображение в соответствии с установленными пропорциями с помощью JavaScript

dr.Brain

время чтения 7 мин.

Photo by Bankim Desai on Unsplash

Сегодня мы напишем крошечную JavaScript-функцию, с помощью которой, мы сможем обрезать картинки в соответствии с установленными пропорциями. Эта функция очень полезна, например, при обработке фотографий перед размещением в ленте социальной сети или перед загрузкой изображения для профиля в личном кабинете, то есть в тех случаях, когда необходимые картинки должны иметь строго определенные соотношения сторон (пропорции).

Таким образом, с помощью JavaScript мы изменим исходное изображение.

Если Вам нужно привести картинку в соответствие с установленными пропорциями, не модифицируя исходный файл, обратитесь к статье “Пропорции для IMG”.


Содержание


Загрузка информации об изображении

Для начала нам потребуется источник данных. Пусть это будет ссылка на исходное изображение:

const imageURL = 'path/to/our/image.jpeg';

Чтобы обрезать изображение до нужных пропорций, нужен доступ к его исходным параметрам. Получим эту информацию, загрузив URL в элемент <img>:

const inputImage = new Image();
inputImage.src = imageURL;

Следующим шагом перенесем картинку на холст (canvas), <canvas> позволит нам манипулировать параметрами изображения.

Добавим обработчик события onload до определения src - так мы сможем перехватить момент загрузки изображения.

// this image will hold our source image data
const inputImage = new Image();

// we want to wait for our image to load
inputImage.onload = () => {

    // create a canvas that will present the output image
    const outputImage = document.createElement('canvas');

    // set it to the same size as the image
    outputImage.width = inputImage.naturalWidth;
    outputImage.height = inputImage.naturalHeight;

    // draw our image at position 0, 0 on the canvas
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, 0, 0);

    // show both the image and the canvas
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// start loading our image
inputImage.src = imageURL;

Результат выполнения вышеприведенного кода - создание элемента canvas, являющегося копией изображения, загруженного по ссылке.

Приведение пропорций изображения к квадрату

Итак, мы получили доступ к параметрам изображения. Приступим к их модификации.

Для начала, обрежем картинку до квадрата. Соотношение сторон квадрата 1:1. Это означает, что все стороны изображения должны иметь одинаковые размеры. Иными словами, в данном случае соотношение сторон будет равняться 1. Например, соотношение сторон фотографии с шириной 200px и высотой 200px равно 1 (200200), соотношение сторон 400px и 300px даст результат 1.3333 (400300). Заметьте, во всех случаях для получения соотношения сторон достаточно разделить ширину объекта на его высоту.

Получается вот такой код:

// the desired aspect ratio of our output image (width / height)
const outputImageAspectRatio = 1;

// this image will hold our source image data
const inputImage = new Image();

// we want to wait for our image to load
inputImage.onload = () => {
    // let's store the width and height of our image
    const inputWidth = inputImage.naturalWidth;
    const inputHeight = inputImage.naturalHeight;
    
    // get the aspect ratio of the input image
    const inputImageAspectRatio = inputWidth / inputHeight;
    
    // if it's bigger than our target aspect ratio
    let outputWidth = inputWidth;
    let outputHeight = inputHeight;
    if (inputImageAspectRatio > outputImageAspectRatio) {
        outputWidth = inputHeight * outputImageAspectRatio;
    } else if (inputImageAspectRatio < outputImageAspectRatio) {
        outputHeight = inputHeight / outputImageAspectRatio;
    }

    // create a canvas that will present the output image
    const outputImage = document.createElement('canvas');

    // set it to the same size as the image
    outputImage.width = outputWidth;
    outputImage.height = outputHeight;
    
    // draw our image at position 0, 0 on the canvas
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, 0, 0);

    // show both the image and the canvas
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// start loading our image
inputImage.src = imageURL;

В результате мы получаем квадратное изображение. Это великолепно! Но давайте присмотримся к полученным результатам. Кажется, результирующее изображение расположено не по центру относительно исходного. Так происходит потому, что мы не обновили drawImage. Метод drawImage содержит не менее трех аргументов: исходное изображение inputImage, x и y - координаты стартовых точек отрисовки.

Стартовые точки для нашего изображения: 0, 0. Таким образом, выравнивание идет не по центру, а от левого верхнего края.

Чтобы получить необходимый результат, мы должны сместить изображение немного влево. Предположим, что ширина нашей исходной картинки 400px, а результирующей - 300px. Для выравнивания финального изображения по центру мы должны сделать смещение на 50 пикселей влево (смещение с отрицательным значением). То есть, мы вычитаем из результирующей ширины (300px) исходную (400px) и делим результат на 2 - получаем -50px:

const outputX = (outputWidth - inputWidth) * .5

Давайте обновим наш код, применив только что полученные знания как к ширине (x), так и к высоте (y):

// the desired aspect ratio of our output image (width / height)
const outputImageAspectRatio = 1;

// this image will hold our source image data
const inputImage = new Image();

// we want to wait for our image to load
inputImage.onload = () => {
    // let's store the width and height of our image
    const inputWidth = inputImage.naturalWidth;
    const inputHeight = inputImage.naturalHeight;
    
    // get the aspect ratio of the input image
    const inputImageAspectRatio = inputWidth / inputHeight;
    
    // if it's bigger than our target aspect ratio
    let outputWidth = inputWidth;
    let outputHeight = inputHeight;
    if (inputImageAspectRatio > outputImageAspectRatio) {
        outputWidth = inputHeight * outputImageAspectRatio;
    } else if (inputImageAspectRatio < outputImageAspectRatio) {
        outputHeight = inputHeight / outputImageAspectRatio;
    }
    
    // calculate the position to draw the image at
    const outputX = (outputWidth - inputWidth) * .5;
    const outputY = (outputHeight - inputHeight) * .5;

    // create a canvas that will present the output image
    const outputImage = document.createElement('canvas');

    // set it to the same size as the image
    outputImage.width = outputWidth;
    outputImage.height = outputHeight;
    
    // draw our image at position 0, 0 on the canvas
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, outputX, outputY);

    // show both the image and the canvas
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// start loading our image
inputImage.src = imageURL;

Теперь результирующее изображение находится по центру относительно исходного.

Функция преобразования пропорций изображения с изменяемыми параметрами

Наконец, преобразуем наш код в функцию преобразования изображения с изменяемыми входными параметрами. Мы сможем изменять соотношение сторон в соответствии с нашими потребностями. Кстати, наш код уже можно использовать именно для этих целей, а не только для создания квадратных картинок:

/**
 * @param {string} url - The source image
 * @param {number} aspectRatio - The aspect ratio
 * @return {Promise<HTMLCanvasElement>} A Promise that resolves with the resulting image as a canvas element
 */

function crop(url, aspectRatio) {
    
    // we return a Promise that gets resolved with our canvas element
    return new Promise(resolve => {

        // this image will hold our source image data
        const inputImage = new Image();

        // we want to wait for our image to load
        inputImage.onload = () => {

            // let's store the width and height of our image
            const inputWidth = inputImage.naturalWidth;
            const inputHeight = inputImage.naturalHeight;

            // get the aspect ratio of the input image
            const inputImageAspectRatio = inputWidth / inputHeight;

            // if it's bigger than our target aspect ratio
            let outputWidth = inputWidth;
            let outputHeight = inputHeight;
            if (inputImageAspectRatio > aspectRatio) {
                outputWidth = inputHeight * aspectRatio;
            } else if (inputImageAspectRatio < aspectRatio) {
                outputHeight = inputHeight / aspectRatio;
            }

            // calculate the position to draw the image at
            const outputX = (outputWidth - inputWidth) * .5;
            const outputY = (outputHeight - inputHeight) * .5;

            // create a canvas that will present the output image
            const outputImage = document.createElement('canvas');

            // set it to the same size as the image
            outputImage.width = outputWidth;
            outputImage.height = outputHeight;

            // draw our image at position 0, 0 on the canvas
            const ctx = outputImage.getContext('2d');
            ctx.drawImage(inputImage, outputX, outputY);
            resolve(outputImage);
        };

        // start loading our image
        inputImage.src = url;
    })
    
}

Теперь мы можем вызвать полученную функцию и получить квадратную картинку:

crop('path/to/our/image.jpeg', 1)

или установить соотношение сторон 16:9:

crop('path/to/our/image.jpeg', 16/9)

так как функция возвращает промисы (Promise) мы можем воспользоваться и таким синтаксисом:

crop('path/to/our/image.jpeg', 16/9).then(canvas => {
  // `canvas` is the resulting image
})

или испоьзовать async/await:

const canvas = await crop('path/to/our/image.jpeg', 16/9)

И вот наш результат:

Заключение

С помощью HTML canvas API и базовых математических знаний мы создали функцию, которая значительно облегчает приведение изображений к различных пропорциям. Данная функция полезна при подготовке фотографий и иллюстраций для размещения в социальных сетях, профиле пользователя, документах и, конечно же, для других целей.

Следует заметить, что данное решение не охватывает следующие случаи:

  1. могут возникнуть затруднения, если браузер мобильного устройства считывает параметры изображения из EXIF заголовков,
  2. при использовании слишком большого изображения происходит переполнение памяти для элемента canvas,
  3. изменение размеров маленьких картинок приводит к значительному ухудшению их качества.

Спасибо за внимание.


Перевод статьи Rik Schennink “Cropping Images to a specific Aspect Ratio with JavaScript”

Новые публикации

Далее

Категории

О нас

Frontend & Backend. Статьи, обзоры, заметки, код, уроки.