Aguarde...

20 de novembro de 2019

Cortando imagens para uma proporção específica com JavaScript

Cortando imagens para uma proporção específica com JavaScript

Neste tutorial de 3 minutos, escreveremos uma pequena função JavaScript que nos ajuda a cortar imagens para várias proporções. Super útil para cortar fotos antes de postar nas linhas de tempo das mídias sociais ou fazer upload de fotos de perfil, pois geralmente é necessário que elas tenham uma certa proporção.

Neste tutorial, modificaremos os dados da imagem. Por exemplo, quando um usuário está prestes a enviar uma imagem, a recortamos para uma determinada proporção. Se apenas queremos apresentar imagens em uma determinada proporção , podemos usar uma solução somente CSS .

Carregando os dados da imagem

Para começar, precisamos de uma imagem de origem. Vamos usar um URL de imagem genérico como fonte.

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

Para cortar uma imagem, precisamos acessar os dados reais da imagem. Podemos acessar esses dados carregando o URL em um <img>elemento.

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

Nosso próximo passo é desenhar a imagem em a <canvas>, a tela nos permitirá modificar os dados da imagem. Adicionaremos o onloadretorno de chamada antes de definir o srcmodo para capturar o momento em que a imagem foi carregada.

// 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;

A execução desse código resulta em uma <canvas>que apresenta a mesma imagem da imagem localizada em nossa imageURL.

Cortando a imagem em uma proporção quadrada

Agora que obtivemos acesso aos dados da imagem, podemos começar a manipulá-los.

Vamos começar com um corte quadrado. Um corte quadrado tem uma proporção de 1: 1. Isso significa que cada lado da imagem de saída tem o mesmo comprimento. Podemos representá-lo numericamente como uma proporção de 1. A proporção de uma imagem de 200×200 é que 1a proporção de uma imagem de 400×300 pode ser calculada dividindo-se a largura e a altura, iguais a 1.333(400/300).

Vamos editar nosso código e visualizar os resultados.

// 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;

O resultado é uma imagem quadrada, ótimo! Mas vamos dar uma olhada. Parece que nossa colheita não está posicionada no centro da imagem de entrada. Isso ocorre porque não atualizamos a drawImagechamada. A drawImagechamada recebe 3 (ou mais) argumentos, a inputImage, e a posição xypara desenhar a imagem.

Nossa imagem de entrada ainda é desenhada no local 00Precisamos ajustar isso para obter um recorte central em vez de um recorte superior esquerdo.

Para fazer isso, precisamos mover a imagem ligeiramente para a esquerda. Suponha que nossa imagem de entrada tenha 400pixels de largura e nossa imagem de saída tenha 300pixels de largura. Para centralizá-lo, precisamos mover os 50pixels da imagem de entrada para a esquerda (50 pixels negativos). -50pixels é 300menos 400dividido por 2. Isso resulta no seguinte snippet de código.

const outputX = (outputWidth - inputWidth) * .5

Vamos atualizar o trecho de código, podemos usar o mesmo código para xydeslocamento e o deslocamento.

// 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;

Com esta atualização, nossa colheita agora está centralizada na imagem de entrada.

Criando uma função de corte JavaScript reutilizável

Como etapa final, vamos transformar nosso código em uma função reutilizável, para que possamos cortar rapidamente imagens com várias proporções de aspecto de corte. Nosso snippet JavaScript já é adequado para ser usado para qualquer proporção, não apenas quadrados.

/**
 * @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;
    })
    
}

Nossa nova e brilhante cropfunção pode ser chamada assim:

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

Ou, para obter um corte “16: 9”:

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

À medida que a função retorna a Promise, podemos obter resultados como este:

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

Ou, usando async / waitit:

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

Veja uma demonstração do resultado final no CodePen

Conclusão

Usando a API da tela HTML e algumas contas básicas, criamos uma pequena função auxiliar de corte que facilita o corte rápido de imagens em várias proporções. Isso nos ajuda a preparar imagens para postagens de mídia social, fotos de perfil, tamanhos familiares de documentos ou outros formatos populares de mídia.

Para manter o artigo conciso, nossa solução atual não cobre estes casos extremos:

  • Navegadores confusos com o cabeçalho de orientação EXIF ​​de fotos para dispositivos móveis.
  • Memória de tela transbordando em dispositivos móveis para imagens muito grandes.
  • Baixa qualidade de imagem ao reduzir as imagens.

Se você precisar de uma solução para esses problemas, poderá explorar o Doka , um editor de imagens fácil de usar que resolve esses casos extremos e apresenta uma ampla gama de funcionalidades adicionais.

Postado em Blog
Escreva um comentário