Как обрезать картинку
Как обрезать изображение в соответствии с установленными пропорциями с помощью JavaScript
 
  
  Сегодня мы напишем крошечную 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 (200⁄200), соотношение сторон 400px и 300px даст результат 1.3333 (400⁄300). Заметьте, во всех случаях для получения соотношения сторон достаточно разделить ширину объекта на его высоту.
Получается вот такой код:
// 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 и базовых математических знаний мы создали функцию, которая значительно облегчает приведение изображений к различных пропорциям. Данная функция полезна при подготовке фотографий и иллюстраций для размещения в социальных сетях, профиле пользователя, документах и, конечно же, для других целей.
Следует заметить, что данное решение не охватывает следующие случаи:
- могут возникнуть затруднения, если браузер мобильного устройства считывает параметры изображения из EXIF заголовков,
- при использовании слишком большого изображения происходит переполнение памяти для элемента canvas,
- изменение размеров маленьких картинок приводит к значительному ухудшению их качества.
Спасибо за внимание.
Перевод статьи Rik Schennink “Cropping Images to a specific Aspect Ratio with JavaScript”

 
   
   
   
  