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 onload
retorno de chamada antes de definir o src
modo 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 1
a 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 drawImage
chamada. A drawImage
chamada recebe 3 (ou mais) argumentos, a inputImage
, e a posição x
e y
para desenhar a imagem.
Nossa imagem de entrada ainda é desenhada no local 0
. 0
Precisamos 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 400
pixels de largura e nossa imagem de saída tenha 300
pixels de largura. Para centralizá-lo, precisamos mover os 50
pixels da imagem de entrada para a esquerda (50 pixels negativos). -50
pixels é 300
menos 400
dividido 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 x
o y
deslocamento 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 crop
funçã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.