web-dev-qa-db-fra.com

Progression du chargement JavaScript d'une image

Existe-t-il un moyen dans JS pour obtenir la progression d'une image de chargement pendant le chargement de l'image? Je souhaite utiliser la nouvelle balise Progress de HTML5 pour afficher la progression du chargement des images.

J'aimerais qu'il y ait quelque chose comme:

var someImage = new Image()
someImage.onloadprogress = function(e) { progressBar.value = e.loaded / e.total };
someImage.src = "image.jpg";
46
Light

Avec cela, vous ajoutez 2 nouvelles fonctions sur l'objet Image ():

 Image.prototype.load = function(url){
        var thisImg = this;
        var xmlHTTP = new XMLHttpRequest();
        xmlHTTP.open('GET', url,true);
        xmlHTTP.responseType = 'arraybuffer';
        xmlHTTP.onload = function(e) {
            var blob = new Blob([this.response]);
            thisImg.src = window.URL.createObjectURL(blob);
        };
        xmlHTTP.onprogress = function(e) {
            thisImg.completedPercentage = parseInt((e.loaded / e.total) * 100);
        };
        xmlHTTP.onloadstart = function() {
            thisImg.completedPercentage = 0;
        };
        xmlHTTP.send();
    };

    Image.prototype.completedPercentage = 0;

Et ici, vous utilisez la fonction de chargement et ajoutez l'image sur un div.

var img = new Image();
img.load("url");
document.getElementById("myDiv").appendChild(img);

Pendant l'état de chargement, vous pouvez vérifier le pourcentage de progression à l'aide de img.completedPercentage.

48

La réponse de Sebastian est excellente, la meilleure que j'ai vue à cette question. Il y a cependant quelques améliorations possibles. J'utilise son code modifié comme ceci:

Image.prototype.load = function( url, callback ) {
    var thisImg = this,
        xmlHTTP = new XMLHttpRequest();

    thisImg.completedPercentage = 0;

    xmlHTTP.open( 'GET', url , true );
    xmlHTTP.responseType = 'arraybuffer';

    xmlHTTP.onload = function( e ) {
        var h = xmlHTTP.getAllResponseHeaders(),
            m = h.match( /^Content-Type\:\s*(.*?)$/mi ),
            mimeType = m[ 1 ] || 'image/png';
            // Remove your progress bar or whatever here. Load is done.

        var blob = new Blob( [ this.response ], { type: mimeType } );
        thisImg.src = window.URL.createObjectURL( blob );
        if ( callback ) callback( this );
    };

    xmlHTTP.onprogress = function( e ) {
        if ( e.lengthComputable )
            thisImg.completedPercentage = parseInt( ( e.loaded / e.total ) * 100 );
        // Update your progress bar here. Make sure to check if the progress value
        // has changed to avoid spamming the DOM.
        // Something like: 
        // if ( prevValue != thisImage completedPercentage ) display_progress();
    };

    xmlHTTP.onloadstart = function() {
        // Display your progress bar here, starting at 0
        thisImg.completedPercentage = 0;
    };

    xmlHTTP.onloadend = function() {
        // You can also remove your progress bar here, if you like.
        thisImg.completedPercentage = 100;
    }

    xmlHTTP.send();
};

J'ai principalement ajouté un type mime et quelques détails mineurs. Utilisez comme Sebastian décrit. Fonctionne bien.

17
Julian Jensen

Juste pour ajouter aux améliorations, j'ai modifié la réponse de Julian (qui à son tour a modifié celle de Sebastian). J'ai déplacé la logique vers une fonction au lieu de modifier l'objet Image. Cette fonction renvoie un Promise qui se résout avec l'objet URL, qui doit uniquement être inséré en tant qu'attribut src d'une balise image.

/**
 * Loads an image with progress callback.
 *
 * The `onprogress` callback will be called by XMLHttpRequest's onprogress
 * event, and will receive the loading progress ratio as an whole number.
 * However, if it's not possible to compute the progress ratio, `onprogress`
 * will be called only once passing -1 as progress value. This is useful to,
 * for example, change the progress animation to an undefined animation.
 *
 * @param  {string}   imageUrl   The image to load
 * @param  {Function} onprogress
 * @return {Promise}
 */
function loadImage(imageUrl, onprogress) {
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest();
    var notifiedNotComputable = false;

    xhr.open('GET', imageUrl, true);
    xhr.responseType = 'arraybuffer';

    xhr.onprogress = function(ev) {
      if (ev.lengthComputable) {
        onprogress(parseInt((ev.loaded / ev.total) * 100));
      } else {
        if (!notifiedNotComputable) {
          notifiedNotComputable = true;
          onprogress(-1);
        }
      }
    }

    xhr.onloadend = function() {
      if (!xhr.status.toString().match(/^2/)) {
        reject(xhr);
      } else {
        if (!notifiedNotComputable) {
          onprogress(100);
        }

        var options = {}
        var headers = xhr.getAllResponseHeaders();
        var m = headers.match(/^Content-Type\:\s*(.*?)$/mi);

        if (m && m[1]) {
          options.type = m[1];
        }

        var blob = new Blob([this.response], options);

        resolve(window.URL.createObjectURL(blob));
      }
    }

    xhr.send();
  });
}

/*****************
 * Example usage
 */

var imgContainer = document.getElementById('imgcont');
var progressBar = document.getElementById('progress');
var imageUrl = 'https://placekitten.com/g/2000/2000';

loadImage(imageUrl, (ratio) => {
  if (ratio == -1) {
    // Ratio not computable. Let's make this bar an undefined one.
    // Remember that since ratio isn't computable, calling this function
    // makes no further sense, so it won't be called again.
    progressBar.removeAttribute('value');
  } else {
    // We have progress ratio; update the bar.
    progressBar.value = ratio;
  }
})
.then(imgSrc => {
  // Loading successfuly complete; set the image and probably do other stuff.
  imgContainer.src = imgSrc;
}, xhr => {
  // An error occured. We have the XHR object to see what happened.
});
<progress id="progress" value="0" max="100" style="width: 100%;"></progress>

<img id="imgcont" />
11
Parziphal

En fait, dans la dernière chrome vous pouvez l'utiliser.

$progress = document.querySelector('#progress');

var url = 'https://placekitten.com/g/2000/2000';

var request = new XMLHttpRequest();
request.onprogress = onProgress;
request.onload = onComplete;
request.onerror = onError;

function onProgress(event) {
  if (!event.lengthComputable) {
    return;
  }
  var loaded = event.loaded;
  var total = event.total;
  var progress = (loaded / total).toFixed(2);

  $progress.textContent = 'Loading... ' + parseInt(progress * 100) + ' %';

  console.log(progress);
}

function onComplete(event) {
  var $img = document.createElement('img');
  $img.setAttribute('src', url);
  $progress.appendChild($img);
  console.log('complete', url);
}

function onError(event) {
  console.log('error');
}


$progress.addEventListener('click', function() {
  request.open('GET', url, true);
  request.overrideMimeType('text/plain; charset=x-user-defined');
  request.send(null);
});
<div id="progress">Click me to load</div>

Voici une petite mise à jour du code de Julian Jensen afin de pouvoir dessiner l'image dans un Canvas après son chargement:

xmlHTTP.onload = function( e ) {
        var h = xmlHTTP.getAllResponseHeaders(),
            m = h.match( /^Content-Type\:\s*(.*?)$/mi ),
            mimeType = m[ 1 ] || 'image/png';
            // Remove your progress bar or whatever here. Load is done.

        var blob = new Blob( [ this.response ], { type: mimeType } );
        thisImg.src = window.URL.createObjectURL( blob );

         thisImg.onload = function()
            {
                if ( callback ) callback( this );
            };
    };
1
kamel B

pour la vérification xmlhttpreq v2, utilisez:

var xmlHTTP = new XMLHttpRequest();
if ('onprogress' in xmlHTTP) {
 // supported 
} else {
 // isn't supported
}
1
Pavel Zheliba

Si vous souhaitez traiter votre image chargée, vous devez ajouter une fonction supplémentaire, car

thisImg.src = window.URL.createObjectURL(blob)

commence juste à traiter l'image comme un fil.

Vous devez ajouter une nouvelle fonction au corps du prototype de charge, comme

  this.onload = function(e)
  {
    var canvas = document.createElement('canvas')

    canvas.width = this.width
    canvas.height = this.height

    canvas.getContext('2d').drawImage(this, 0, 0)
   }

Cela me fait mal à la tête de réaliser :)

0