Puede utilizar la reducción de pasos para lograr mejores resultados. La mayoría de los navegadores parecen utilizar interpolación lineal en lugar de bicúbica al cambiar el tamaño de las imágenes.
( Actualización Se ha agregado una propiedad de calidad a las especificaciones, imageSmoothingQuality
que actualmente solo está disponible en Chrome).
A menos que uno elija sin suavizado o vecino más cercano, el navegador siempre interpolará la imagen después de reducirla, ya que esta función es un filtro de paso bajo para evitar el alias.
Bi-linear usa 2x2 píxeles para hacer la interpolación mientras que bi-cubic usa 4x4 por lo que al hacerlo en pasos puede acercarse al resultado bi-cubic mientras usa la interpolación bi-lineal como se ve en las imágenes 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>
Dependiendo de cuán drástico sea su cambio de tamaño, puede omitir el paso 2 si la diferencia es menor.
En la demostración, puede ver que el nuevo resultado ahora es muy similar al elemento de imagen.
Dado que el violín de Trung Le Nguyen Nhat no es correcto en absoluto (solo usa la imagen original en el último paso)
escribí mi propio violín general con comparación de rendimiento:
VIOLÍN
Básicamente es:
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); }
fuente
Creé un servicio Angular reutilizable para manejar el cambio de tamaño de imágenes / lienzos de alta calidad para cualquiera que esté interesado: https://gist.github.com/transitive-bullshit/37bac5e741eaec60e983
El servicio incluye dos soluciones porque ambas tienen sus pros / contras. El enfoque de convolución de lanczos es de mayor calidad a costa de ser más lento, mientras que el enfoque de reducción de escala escalonada produce resultados razonablemente antialias y es significativamente más rápido.
Uso de ejemplo:
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 }) })
fuente
Si bien algunos de esos fragmentos de código son breves y funcionan, no son triviales de seguir y comprender.
Como no soy un fanático de "copiar y pegar" de stack-overflow, me gustaría que los desarrolladores entendieran el código que están insertando en su software, espero que encuentre útil lo siguiente.
DEMO : Cambiar el tamaño de las imágenes con JS y HTML Canvas Demo fiddler.
Puede encontrar 3 métodos diferentes para hacer este cambio de tamaño, que lo ayudarán a comprender cómo funciona el código y por qué.
https://jsfiddle.net/1b68eLdr/93089/
El código completo de la demostración y el método TypeScript que puede que desee utilizar en su código se puede encontrar en el proyecto GitHub.
https://github.com/eyalc4/ts-image-resizer
Este es el 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 } }; }}
fuente
Creé una biblioteca que le permite reducir cualquier porcentaje manteniendo todos los datos de color.
https://github.com/danschumann/limby-resize/blob/master/lib/canvas_resize.js
Ese archivo lo puedes incluir en el navegador. Los resultados se verán como photoshop o magia de imágenes, conservando todos los datos de color, promediando píxeles, en lugar de tomar los cercanos y eliminar otros. No usa una fórmula para adivinar los promedios, toma el promedio exacto.
fuente
Basado en la respuesta de K3N, reescribo el código generalmente para cualquiera que quiera
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);
ACTUALIZAR LA DEMO DE JSFIDDLE
Aquí está mi DEMO ONLINE
fuente
No entiendo por qué nadie lo sugiere
createImageBitmap
.createImageBitmap( document.getElementById('image'), { resizeWidth: 300, resizeHeight: 234, resizeQuality: 'high' } ) .then(imageBitmap => document.getElementById('canvas').getContext('2d').drawImage(imageBitmap, 0, 0) );
funciona muy bien (suponiendo que establezca identificadores para la imagen y el lienzo).
fuente
Escribí una pequeña utilidad js para recortar y cambiar el tamaño de la imagen en el front-end. Aquí hay un enlace sobre el proyecto GitHub. También puede obtener blob de la imagen final para enviarlo.
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;
fuente