web-dev-qa-db-fra.com

Redimensionner une image base 64 en JavaScript sans utiliser de toile

J'ai besoin d'un moyen de redimensionner les images en JavaScript sans utiliser d'élément HTML.

Mon application HTML mobile prend des photos puis les convertit en chaînes base64. Par la suite, je dois les redimensionner avant de les envoyer à l'API afin d'économiser de l'espace de stockage.

Je cherche un moyen différent et plus approprié de les redimensionner que d'utiliser un élément de toile. Y a-t-il un moyen?

55
rilar

Un moyen d'éviter que le code HTML principal soit affecté consiste à créer un canevas hors écran qui reste en dehors de l'arborescence DOM.

Cela fournira un tampon bitmap et un code natif compilé pour coder les données d'image. C'est simple à faire:

function imageToDataUri(img, width, height) {

    // create an off-screen canvas
    var canvas = document.createElement('canvas'),
        ctx = canvas.getContext('2d');

    // set its dimension to target size
    canvas.width = width;
    canvas.height = height;

    // draw source image into the off-screen canvas:
    ctx.drawImage(img, 0, 0, width, height);

    // encode image to data-uri with base64 version of compressed image
    return canvas.toDataURL();
}

Si vous voulez produire un format différent de PNG (valeur par défaut), spécifiez simplement le type comme ceci:

return canvas.toDataURL('image/jpeg', quality);  // quality = [0.0, 1.0]

Il est à noter que CORS les restrictions s'appliquent à toDataURL().

Si votre application fournit uniquement des images codées en base64 (je suppose qu'il s'agit de data-uri contenant des données en base64?), Vous devez d'abord "charger" l'image:

var img = new Image;

img.onload = resizeImage;
img.src = originalDataUriHere;

function resizeImage() {
    var newDataUri = imageToDataUri(this, targetWidth, targetHeight);
    // continue from here...
}

Si la source est pure chaîne base 64, ajoutez-lui simplement un en-tête pour en faire un data-uri:

function base64ToDataUri(base64) {
    return 'data:image/png;base64,' + base64;
}

Il suffit de remplacer la partie image/png Par le type représenté par la chaîne base64 (c.-à-d. En faire un argument facultatif).

68
user1693593

La réponse de Ken est la bonne, mais son code ne fonctionne pas. J'ai fait quelques ajustements et cela fonctionne maintenant parfaitement. Pour redimensionner un URI de données:

// Takes a data URI and returns the Data URI corresponding to the resized image at the wanted size.
function resizedataURL(datas, wantedWidth, wantedHeight)
    {
        // We create an image to receive the Data URI
        var img = document.createElement('img');

        // When the event "onload" is triggered we can resize the image.
        img.onload = function()
            {        
                // We create a canvas and get its context.
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');

                // We set the dimensions at the wanted size.
                canvas.width = wantedWidth;
                canvas.height = wantedHeight;

                // We resize the image with the canvas method drawImage();
                ctx.drawImage(this, 0, 0, wantedWidth, wantedHeight);

                var dataURI = canvas.toDataURL();

                /////////////////////////////////////////
                // Use and treat your Data URI here !! //
                /////////////////////////////////////////
            };

        // We put the Data URI in the image's src attribute
        img.src = datas;
    }
// Use it like that : resizedataURL('yourDataURIHere', 50, 50);
16

Pierrick Martellière est de loin la meilleure réponse, je voulais juste souligner que vous devriez implémenter cela avec une fonction asynchrone. Une fois que cela, vous pourriez faire quelque chose comme:

var newDataUri = await resizedataURL(datas,600,600);

Cela attendra le résultat de la fonction avant de passer à l'étape suivante. C'est une façon plus propre d'écrire du code. Voici la fonction de Pierrick avec le petit montage:

// Takes a data URI and returns the Data URI corresponding to the resized image at the wanted size.
function resizedataURL(datas, wantedWidth, wantedHeight){
    return new Promise(async function(resolve,reject){

        // We create an image to receive the Data URI
        var img = document.createElement('img');

        // When the event "onload" is triggered we can resize the image.
        img.onload = function()
        {        
            // We create a canvas and get its context.
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');

            // We set the dimensions at the wanted size.
            canvas.width = wantedWidth;
            canvas.height = wantedHeight;

            // We resize the image with the canvas method drawImage();
            ctx.drawImage(this, 0, 0, wantedWidth, wantedHeight);

            var dataURI = canvas.toDataURL();

            // This is the return of the Promise
            resolve(dataURI);
        };

        // We put the Data URI in the image's src attribute
        img.src = datas;

    })
}// Use it like : var newDataURI = await resizedataURL('yourDataURIHere', 50, 50);

Pour plus de détails, vous pouvez consulter les documents MDN: https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Promise

6
user8094363

Oui, vous pouvez. Ces solutions sont bonnes pour redimensionner et pas seulement convertir une image en base64.

  1. Vous pouvez convertir un fichier js en image bitmap par jpg-js . Et vous pouvez redimensionner uniquement par cette lib Pour obtenir des images haute résolution, il faut convertir le fichier en bitmap par jpg-js, puis redimensionner ce bitmap par Pica lib.
  2. Vous pouvez obtenir des données d'image à partir d'un fichier par jpg-js (ou dessiner une image sur une toile), puis redimensionner canvasImageData en redimensionnant lib pica . (bon pour les images haute résolution, sans restriction de taille de la toile)
  3. Vous pouvez utiliser un canevas hors écran, sans l'attacher à un corps, ni redimensionner une image. Cette solution sera plus rapide mais sera la pire solution pour les images haute résolution, par exemple 6000 x 6000 pixels. Dans ce cas, le canevas de résultat peut être de mauvaise qualité ou tout simplement vide, ou le navigateur peut tomber avec une exception de limite de mémoire. (bon pour les images normales et petites)

Jpg-js et Pica n'utiliseront pas du tout d'éléments dom. Ces bibliothèques ne fonctionnent qu'avec des données d'image, sans éléments dom (canvas et image).

A propos de la toile, restriction de taille voir ceci post

6
Alex Nikulin

Vous pouvez utiliser une manipulation d'image Base64 brute, comme indiqué ici.

Semble difficile (et pour png seulement) et pourquoi beaucoup choisissent d'utiliser canvas

http://blog.calyptus.eu/seb/2009/05/png-parser-in-javascript/

1
user3105359