Você pode usar a redução para obter melhores resultados. A maioria dos navegadores parece usar interpolação linear em vez de bi-cúbica ao redimensionar imagens.
( Atualização foi adicionada uma propriedade de qualidade às especificações, imageSmoothingQuality
que atualmente está disponível apenas no Chrome.)
A menos que não se escolha nenhuma suavização ou vizinho mais próximo, o navegador sempre interpola a imagem após redimensioná-la, pois esta função é um filtro passa-baixo para evitar o aliasing.
Bi-linear usa 2x2 pixels para fazer a interpolação enquanto bi-cúbico usa 4x4 então fazendo isso em etapas você pode chegar perto do resultado bi-cúbico enquanto usa interpolação bi-linear como visto nas imagens resultantes.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.onload = function () {
canvas.height = canvas.width * (img.height / img.width);
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = img.width * 0.5;
oc.height = img.height * 0.5;
octx.drawImage(img, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
}
img.src = "//i.imgur.com/SHo6Fub.jpg";
<img src="//i.imgur.com/SHo6Fub.jpg" width="300" height="234">
<canvas id="canvas" width=300></canvas>
Dependendo de quão drástico é o seu redimensionamento, você pode pular a etapa 2 se a diferença for menor.
Na demonstração, você pode ver que o novo resultado agora é muito semelhante ao elemento de imagem.
Como o violino de Trung Le Nguyen Nhat não está correto (ele apenas usa a imagem original na última etapa)
, escrevi meu próprio violino geral com comparação de desempenho:
FIDDLE
Basicamente é:
img.onload = function() { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"), oc = document.createElement('canvas'), octx = oc.getContext('2d'); canvas.width = width; // destination canvas size canvas.height = canvas.width * img.height / img.width; var cur = { width: Math.floor(img.width * 0.5), height: Math.floor(img.height * 0.5) } oc.width = cur.width; oc.height = cur.height; octx.drawImage(img, 0, 0, cur.width, cur.height); while (cur.width * 0.5 > width) { cur = { width: Math.floor(cur.width * 0.5), height: Math.floor(cur.height * 0.5) }; octx.drawImage(oc, 0, 0, cur.width * 2, cur.height * 2, 0, 0, cur.width, cur.height); } ctx.drawImage(oc, 0, 0, cur.width, cur.height, 0, 0, canvas.width, canvas.height); }
fonte
Criei um serviço Angular reutilizável para lidar com redimensionamento de imagens / telas de alta qualidade para qualquer pessoa interessada: https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983
O serviço inclui duas soluções porque ambas têm suas próprias vantagens / desvantagens. A abordagem de convolução de lanczos é de qualidade superior ao custo de ser mais lenta, enquanto a abordagem de redução gradual produz resultados razoavelmente suavizados e é significativamente mais rápida.
Exemplo de uso:
angular.module('demo').controller('ExampleCtrl', function (imageService) { // EXAMPLE USAGE // NOTE: it's bad practice to access the DOM inside a controller, // but this is just to show the example usage. // resize by lanczos-sinc filter imageService.resize($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) // resize by stepping down image size in increments of 2x imageService.resizeStep($('#myimg')[0], 256, 256) .then(function (resizedImage) { // do something with resized image }) })
fonte
Embora alguns desses trechos de código sejam curtos e funcionem, eles não são triviais de seguir e entender.
Como não sou um fã de "copiar e colar" do estouro de pilha, gostaria que os desenvolvedores entendessem o código que eles colocam no software, espero que você ache o seguinte útil.
DEMO : redimensionando imagens com violador JS e HTML Canvas Demo.
Você pode encontrar 3 métodos diferentes para fazer esse redimensionamento, que o ajudarão a entender como o código está funcionando e por quê.
https://jsfiddle.net/1b68eLdr/93089/
O código completo da demonstração e do método TypeScript que você pode usar em seu código pode ser encontrado no projeto GitHub.
https://github.com/eyalc4/ts-image-resizer
Este é o código final:
export class ImageTools { base64ResizedImage: string = null; constructor() { } ResizeImage(base64image: string, width: number = 1080, height: number = 1080) { let img = new Image(); img.src = base64image; img.onload = () => { // Check if the image require resize at all if(img.height <= height && img.width <= width) { this.base64ResizedImage = base64image; // TODO: Call method to do something with the resize image } else { // Make sure the width and height preserve the original aspect ratio and adjust if needed if(img.height > img.width) { width = Math.floor(height * (img.width / img.height)); } else { height = Math.floor(width * (img.height / img.width)); } let resizingCanvas: HTMLCanvasElement = document.createElement('canvas'); let resizingCanvasContext = resizingCanvas.getContext("2d"); // Start with original image size resizingCanvas.width = img.width; resizingCanvas.height = img.height; // Draw the original image on the (temp) resizing canvas resizingCanvasContext.drawImage(img, 0, 0, resizingCanvas.width, resizingCanvas.height); let curImageDimensions = { width: Math.floor(img.width), height: Math.floor(img.height) }; let halfImageDimensions = { width: null, height: null }; // Quickly reduce the dize by 50% each time in few iterations until the size is less then // 2x time the target size - the motivation for it, is to reduce the aliasing that would have been // created with direct reduction of very big image to small image while (curImageDimensions.width * 0.5 > width) { // Reduce the resizing canvas by half and refresh the image halfImageDimensions.width = Math.floor(curImageDimensions.width * 0.5); halfImageDimensions.height = Math.floor(curImageDimensions.height * 0.5); resizingCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, halfImageDimensions.width, halfImageDimensions.height); curImageDimensions.width = halfImageDimensions.width; curImageDimensions.height = halfImageDimensions.height; } // Now do final resize for the resizingCanvas to meet the dimension requirments // directly to the output canvas, that will output the final image let outputCanvas: HTMLCanvasElement = document.createElement('canvas'); let outputCanvasContext = outputCanvas.getContext("2d"); outputCanvas.width = width; outputCanvas.height = height; outputCanvasContext.drawImage(resizingCanvas, 0, 0, curImageDimensions.width, curImageDimensions.height, 0, 0, width, height); // output the canvas pixels as an image. params: format, quality this.base64ResizedImage = outputCanvas.toDataURL('image/jpeg', 0.85); // TODO: Call method to do something with the resize image } }; }}
fonte
Eu criei uma biblioteca que permite reduzir qualquer porcentagem enquanto mantém todos os dados de cores.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
Esse arquivo você pode incluir no navegador. Os resultados serão parecidos com photoshop ou image magick, preservando todos os dados de cores, fazendo a média dos pixels, ao invés de pegar os próximos e descartar outros. Ele não usa uma fórmula para adivinhar as médias, mas sim a média exata.
fonte
Com base na resposta K3N, eu reescrevo o código geralmente para quem quiser
var oc = document.createElement('canvas'), octx = oc.getContext('2d'); oc.width = img.width; oc.height = img.height; octx.drawImage(img, 0, 0); while (oc.width * 0.5 > width) { oc.width *= 0.5; oc.height *= 0.5; octx.drawImage(oc, 0, 0, oc.width, oc.height); } oc.width = width; oc.height = oc.width * img.height / img.width; octx.drawImage(img, 0, 0, oc.width, oc.height);
ATUALIZAR DEMO JSFIDDLE
Aqui está minha DEMO ONLINE
fonte
Não entendo por que ninguém está sugerindo
createImageBitmap
.createImageBitmap( document.getElementById('image'), { resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' } ) .then(imageBitmap => document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0) );
funciona perfeitamente (assumindo que você definiu ids para imagem e tela).
fonte
Eu escrevi um pequeno utilitário js para cortar e redimensionar a imagem no front-end. Aqui está o link do projeto GitHub. Além disso, você pode obter o blob da imagem final para enviá-lo.
import imageSqResizer from './image-square-resizer.js' let resizer = new imageSqResizer( 'image-input', 300, (dataUrl) => document.getElementById('image-output').src = dataUrl; ); //Get blob let formData = new FormData(); formData.append('files[0]', resizer.blob); //get dataUrl document.getElementById('image-output').src = resizer.dataUrl;
fonte