139 votes

Redimensionner l'image avec javascript canvas (en douceur)

Je suis en train d'essayer de redimensionner certaines images avec canvas mais je ne sais pas comment les adoucir. Sur photoshop, les navigateurs, etc., il y a quelques algorithmes qu'ils utilisent (par exemple, bicubic, bilinéaire) mais je ne sais pas si ceux-ci sont intégrés à canvas ou non.

Voici mon fiddle: http://jsfiddle.net/EWupT/

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width=300
canvas.height=234
ctx.drawImage(img, 0, 0, 300, 234);
document.body.appendChild(canvas);

Le premier est une balise d'image redimensionnée normale, et le deuxième est canvas. Remarquez comment celui de canvas n'est pas aussi lisse. Comment puis-je obtenir une 'fluidité'?

1voto

webargus Points 131

Voici mon code, que j'espère pourra être utile à quelqu'un dans la communauté SO :

Vous pouvez inclure les dimensions de l'image cible comme un paramètre dans l'appel de votre script. Cela donnera la valeur résultante de la largeur ou de la hauteur de votre image, quelle que soit la plus grande. La dimension la plus petite est redimensionnée en conservant le ratio de votre image inchangé. Vous pouvez également définir votre taille cible par défaut dans le script.

Vous pouvez facilement modifier le script pour répondre à vos besoins spécifiques, comme le type d'image que vous souhaitez (par défaut, "image/png") pour une sortie et décider en combien d'étapes en pourcentage vous voulez redimensionner votre image pour un résultat plus précis (voir const percentStep dans le code).

  const ResizeImage = ( _ => {

const MAX_LENGTH = 260;     // taille cible par défaut de la plus grande dimension, soit la largeur ou la hauteur
const percentStep = .3;     // pas de redimensionnement jusqu'à atteindre la taille cible en pourcentage (30% par défaut)
const canvas = document.createElement("canvas");
const canvasContext = canvas.getContext("2d");
const image = new Image();

const doResize = (callback, maxLength) => {

    // annuler avec une erreur si l'image a une dimension égale à zéro
    if(image.width == 0 || image.height == 0) {
        return {blob: null, error: "soit la largeur soit la hauteur de l'image était égale à zéro "};
    }

    // utiliser la dimension de l'appeleur ou la longueur par défaut si aucune n'est fournie
    const length = maxLength == null  ? MAX_LENGTH : maxLength;

    canvas.width = image.width;
    canvas.height = image.height;
    canvasContext.drawImage(image, 0, 0, image.width, image.height);
    // si la taille de l'image est déjà dans la taille cible, copier simplement et retourner le blob
    if(image.width <= length && image.height <= length) {
        canvas.toBlob( blob => {
            callback({ blob: blob, error: null });
        }, "image/png", 1);
        return;
    }

    var startDim = Math.max(image.width, image.height);
    var startSmallerDim = Math.min(image.width, image.height);

    // écart à diminuer de taille jusqu'à atteindre la taille cible,
    // que ce soit en réduisant la largeur ou la hauteur de l'image,
    // quelle que soit la plus grande
    const gap = startDim - length;
    // pas de chaque itération de redimensionnement
    const step = parseInt(percentStep*gap);
    //  nombre d'itérations
    var nSteps = 0;
    if(step == 0) {
        step = 1;
    } else {
        nSteps = parseInt(gap/step);
    }
    // longueur de la dernière étape de redimensionnement supplémentaire, si nécessaire
    const lastStep = gap % step;
    // rapport hauteur/largeur = valeur par laquelle nous allons multiplier la dimension la plus petite
    // afin de conserver le ratio de l'image inchangé à chaque itération
    const ratio = startSmallerDim/startDim;

    var newDim;          // nouvelle longueur calculée pour la plus grande dimension de l'image, qu'il s'agisse de la largeur ou de la hauteur de l'image
    var smallerDim;     // longueur le long de la dimension la plus petite de l'image, la largeur ou la hauteur
    for(var i = 0; i < nSteps; i++) {
        // diminuer la dimension la plus longue d'un pas en pixels
        newDim = startDim - step;
        // diminuer la dimension la plus courte de manière proportionnelle, afin de conserver le ratio
        smallerDim = parseInt(ratio*newDim);
        // assigner les variables calculées à leur dimension respective du canevas, largeur ou hauteur
        if(image.width > image.height) {
            [canvas.width, canvas.height]  = [newDim, smallerDim];
        } else {
            [canvas.width, canvas.height] = [smallerDim, newDim];
        }
        // dessiner l'image une étape plus petite
        canvasContext.drawImage(canvas, 0, 0, canvas.width, canvas.height);
        // varier la dimension de départ pour la nouvelle boucle
        startDim = newDim;
    }

    // faire la dernière étape de redimensionnement manquante pour atteindre enfin la taille de l'image cible
    if(lastStep > 0) {
        if(image.width > image.height) {
            [canvas.width, canvas.height]  = [startDim - lastStep, parseInt(ratio*(startDim - lastStep))];
        } else {
            [canvas.width, canvas.height] = [parseInt(ratio*(startDim -lastStep)), startDim - lastStep];
        }
        canvasContext.drawImage(image, 0, 0, canvas.width, canvas.height);
    }

    // envoyer le blob à l'appeleur
    canvas.toBlob( blob => {
        callback({blob: blob, error: null});
    }, "image/png", 1);

};

const resize = async (imgSrc, callback, maxLength) => {
    image.src = imgSrc;
    image.onload = _ => {
       doResize(callback, maxLength);
    };
};

return { resize: resize }

})();

Utilisation :

ResizeImage.resize("./chemin/vers/image/ou/blob/octets/à/redimensionner", imageObject => {
    if(imageObject.error != null) {
      // gérer les erreurs ici
      console.log(imageObject.error);
      return;
    }
    // faites ce que vous voulez avec le blob, comme l'assigner à
    // un élément img, ou le télécharger dans une base de données
    // ...
    document.querySelector("#my-image").src = imageObject.blob;
    // ...
}, 300);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X