компилируется...
Перейти к основному содержанию
Рекомендовать во Вконтакте Рекомендовать в Фейсбуке Рекомендовать в Твиттере

Кадрирование изображений до определенного соотношения сторон с помощью JavaScript

В этом небольшом руководстве мы напишем крошечную функцию JavaScript, которая поможет нам кадрировать изображения с различными соотношениями сторон. Отлично подходит для обрезки фотографий перед отправкой в социальные сети или загрузкой фотографий профиля, поскольку они часто должны иметь определенное соотношение сторон.

В этом руководстве мы изменим данные изображения. Например, когда пользователь собирается загрузить изображение, мы обрежем его до определенного соотношения сторон.

Загрузка данных изображения

Для начала нам понадобится исходное изображение. Давайте воспользуемся общим URL-адресом изображения в качестве источника.

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

Для кадрирования изображения нам необходимо получить доступ к фактическим данным изображения. Мы можем добраться до этих данных, загрузив URL-адрес в элемент <img>.

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

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

// это изображение будет содержать данные нашего исходного изображения.
const inputImage = new Image();

// мы хотим дождаться, когда наше изображение загрузится.
inputImage.onload = () => {
    // создадим холст, на котором будет представлено выходное изображение.
    const outputImage = document.createElement('canvas');

    // установим его на тот же размер, что и изображение.
    outputImage.width = inputImage.naturalWidth;
    outputImage.height = inputImage.naturalHeight;

    // нарисуем наше изображение в точках 0, 0 на холсте.
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, 0, 0);

    // отображаем и изображение, и canvas.
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// начинаем загружать изображение.
inputImage.src = imageURL;

Выполнение этого кода приводит к появлению <canvas>, который представляет собой то же самое изображение, что и изображение, расположенное в нашем imageURL.

Кадрирование изображения до квадратного соотношения сторон

Теперь, когда мы получили доступ к данным изображения, мы можем начать манипулировать ими.

Давайте начнем с квадратного кадрирования. Квадратное кадрирование имеет соотношение сторон 1:1. Это означает, что каждая сторона выходного изображения имеет одинаковую длину. Соотношение сторон изображения 200x200 равно 1, соотношение сторон изображения 400x300 может быть вычислено путем деления ширины и высоты на 1.333 (400/300).

Давайте отредактируем наш код и посмотрим на результаты.

// желаемое соотношение сторон нашего выходного изображения (ширина/высота)
const outputImageAspectRatio = 1;

// это изображение будет содержать данные нашего исходного изображения.
const inputImage = new Image();

// мы хотим дождаться, когда наше изображение загрузится.
inputImage.onload = () => {
    // давайте сохраним ширину и высоту нашего изображения.
    const inputWidth = inputImage.naturalWidth;
    const inputHeight = inputImage.naturalHeight;
    
    // вычисляем соотношение сторон входного изображения
    const inputImageAspectRatio = inputWidth / inputHeight;
    
    // если он больше, чем заданный коэффициент соотношения сторон.
    let outputWidth = inputWidth;
    let outputHeight = inputHeight;

    if (inputImageAspectRatio > outputImageAspectRatio) {
        outputWidth = inputHeight * outputImageAspectRatio;
    } else if (inputImageAspectRatio < outputImageAspectRatio) {
        outputHeight = inputHeight / outputImageAspectRatio;
    }

    // создадим холст, на котором будет представлено выходное изображение.
    const outputImage = document.createElement('canvas');

    // установим его на тот же размер, что и вычисленные размеры.
    outputImage.width = outputWidth;
    outputImage.height = outputHeight;
    
    // нарисуем наше изображение в точках 0, 0 на холсте.
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, 0, 0);

    // отображаем и изображение, и canvas.
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// начинаем загружать изображение.
inputImage.src = imageURL;

В результате получается квадратное изображение, отлично! Но давайте посмотрим поближе. Похоже, что кадрирование не располагается в центре входного изображения. Это связано с тем, что мы не обновили вызов drawImage. Вызов draImage принимает 3 (или более) аргумента, inputImage и позицию x и y для рисования изображения.

Наше входное изображение все еще нарисовано в точках 0, 0, поэтому нам нужно настроить его таким образом, чтобы получить срез по центру вместо верхней левой рамки.

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

const outputX = (outputWidth - inputWidth) * .5

Обновим фрагмент кода, мы можем использовать один и тот же код как для x, так и для y-смещения.

// желаемое соотношение сторон нашего выходного изображения (ширина/высота)
const outputImageAspectRatio = 1;

// это изображение будет содержать данные нашего исходного изображения.
const inputImage = new Image();

// мы хотим дождаться, когда наше изображение загрузится.
inputImage.onload = () => {
    // давайте сохраним ширину и высоту нашего изображения.
    const inputWidth = inputImage.naturalWidth;
    const inputHeight = inputImage.naturalHeight;
    
    // получить соотношение сторон входного изображения
    const inputImageAspectRatio = inputWidth / inputHeight;
    
    // если он больше, чем целевой коэффициент соотношения сторон.
    let outputWidth = inputWidth;
    let outputHeight = inputHeight;

    if (inputImageAspectRatio > outputImageAspectRatio) {
        outputWidth = inputHeight * outputImageAspectRatio;
    } else if (inputImageAspectRatio < outputImageAspectRatio) {
        outputHeight = inputHeight / outputImageAspectRatio;
    }
    
    // рассчитать положение для рисования изображения в точке
    const outputX = (outputWidth - inputWidth) * .5;
    const outputY = (outputHeight - inputHeight) * .5;

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

    // установить его на тот же размер, что и изображение.
    outputImage.width = outputWidth;
    outputImage.height = outputHeight;
    
    // нарисуем наше изображение в точках 0, 0 на холсте.
    const ctx = outputImage.getContext('2d');
    ctx.drawImage(inputImage, outputX, outputY);

    // показать и изображение, и холст.
    document.body.appendChild(inputImage);
    document.body.appendChild(outputImage);
};

// начать загружать наше изображение
inputImage.src = imageURL;

В этом обновлении кадрирование теперь происходит по центру входного изображения.

Создание многократно используемой функции кадрирования в JavaScript

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

/**
 * @param {string} url - Исходное изображение
 * @param {number} aspectRatio - Соотношение сторон
 * @return {Promise<HTMLCanvasElement>} Promise, который разрешается с изображением в виде холста.
 */
function crop(url, aspectRatio) {
    // мы возвращаем Promise, который разрешается с помощью нашего холста.
    return new Promise(resolve => {

        // это изображение будет содержать данные нашего исходного изображения.
        const inputImage = new Image();

        // мы хотим дождаться, когда наше изображение загрузится.
        inputImage.onload = () => {

            // давайте сохраним ширину и высоту нашего изображения.
            const inputWidth = inputImage.naturalWidth;
            const inputHeight = inputImage.naturalHeight;

            // получить соотношение сторон входного изображения
            const inputImageAspectRatio = inputWidth / inputHeight;

            // если он больше, чем целевой коэффициент соотношения сторон.
            let outputWidth = inputWidth;
            let outputHeight = inputHeight;

            if (inputImageAspectRatio > aspectRatio) {
                outputWidth = inputHeight * aspectRatio;
            } else if (inputImageAspectRatio < aspectRatio) {
                outputHeight = inputHeight / aspectRatio;
            }

            // рассчитать положение для рисования изображения в точке
            const outputX = (outputWidth - inputWidth) * .5;
            const outputY = (outputHeight - inputHeight) * .5;

            // создать холст, на котором будет представлено выходное изображение.
            const outputImage = document.createElement('canvas');

            // установить его на тот же размер, что и изображение.
            outputImage.width = outputWidth;
            outputImage.height = outputHeight;

            // нарисуем наше изображение в точках 0, 0 на холсте.
            const ctx = outputImage.getContext('2d');
            ctx.drawImage(inputImage, outputX, outputY);
            resolve(outputImage);
        };

        // начать загружать наше изображение
        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-интерфейс API canvas и некоторые базовые математические вычисления, мы создаем крошечную вспомогательную функцию кадрирования, которая позволяет быстро обрезать изображения в различных соотношениях сторон. Это поможет нам подготовить изображения для постов в социальных сетях, профилей, знакомых форматов документов или других популярных форматов СМИ.

Вкратце говоря, наше нынешнее решение не охватывает эти крайние случаи:

  • Браузеры запутываются заголовком EXIF ориентации мобильных фотографий.
  • Переполнение памяти холста на мобильных устройствах для очень больших изображений.
  • Низкое качество изображения при уменьшении масштаба изображения.

Комментарии

Пока что нет комментариев, вы можете быть первым.
Войти или Регистрация , чтобы оставлять комментарии.

Лучшие публикации

Популярные теги

Наш сайт использует куки. Узнайте больше о нашем использовании куки: политика в отношении файлов cookie
Наш сайт существует только благодаря показу онлайн-рекламы нашим посетителям.
Пожалуйста, поддержите нас, отключив блокировку рекламы.