【问题标题】:JavaScript loading progress of an image图片的 JavaScript 加载进度
【发布时间】:2012-12-22 12:28:24
【问题描述】:

在加载图像时,JS 中有没有办法获取加载图像的进度? 我想用 HTML5 新的 Progress 标签来显示加载图片的进度。

我希望有类似的东西:

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

【问题讨论】:

标签: javascript image html progress


【解决方案1】:

这样,您在 Image() 对象上添加了 2 个新函数:

 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;

在这里您使用加载功能并将图像附加到 div 上。

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

在加载状态期间,您可以使用img.completedPercentage 查看进度百分比。

【讨论】:

  • 你可以使用img对象作为img标签的src。
  • 这条线是做什么用的? parseInt(thisImg.completedPercentage = (e.loaded / e.total) * 100);是不是写错了?
  • 哦,它是,应该可以,但它错了,让我编辑这个
  • 这似乎不适用于 Safari v10。它似乎并没有真正将新的load 函数添加到对象原型中,img.load() 被列为undefined
  • @duhaime 我做到了。我有时对 SO 的短视性质感到非常沮丧,因为我被要求从上面的链接中删除我的答案。据说它“不太可能对其他人有用”。我对此提出异议,但是..我离题了。无论如何,Safari 的标准 uBlock 插件过滤器会阻止 Image.prototype.load 完成,而 Chrome 和 Firefox 中则没有。 fork 开发人员尚未确认我的错误报告,所以我不知道它是否会得到解决。如果您没有使用 uBlock,请禁用所有插件,看看是否有助于查明原因。
【解决方案2】:

塞巴斯蒂安的回答非常好,这是我见过的最好的回答。但是,有一些可能的改进。我使用他的代码修改如下:

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();
};

主要是我添加了一个哑剧类型和一些小细节。按照塞巴斯蒂安的描述使用。效果很好。

【讨论】:

  • 你知道哪些浏览器会支持这个吗?以及是否移动?下面的答案说并非所有浏览器都支持 XHR 解决方案……
  • 同意@manakor 会很高兴看到这个答案中提到的浏览器支持
  • @manakor:我发现 caniuse.com 对于像您这样的问题非常宝贵。
  • 您不应该在xmlHTTP.onload 上进行回调,因为naturalWidthnaturalHeight 等图像属性尚不可用。你应该做一个单独的thisImg.onload = function() {if ( callback ) callback();};
  • 使用XMLHttpRequest 的一个缺点是没有在请求上设置 Origin 标头 - 但您只需在 Image 对象上直接设置 src 属性(至少在 Chrome 中)
【解决方案3】:

对于 xmlhttpreq v2 检查,使用:

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

【讨论】:

    【解决方案4】:

    实际上,在最新的 chrome 中你可以使用它。

    $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>

    【讨论】:

    • Христо:我建议您定义什么是“最新”和“它”。这样你也可能会得到更多的支持。
    • 这兼容所有浏览器吗?
    • 这是 IMO 的最佳答案。工作原理:发送资源的 XHR 请求,并监控进度。请求完成后,您可以使用浏览器中的缓存资源来创建图像,它应该立即准备就绪。
    【解决方案5】:

    这里是 Julian Jensen 代码的一个小更新,以便在加载后能够在 Canvas 中绘制图像:

    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 );
                };
        };
    

    【讨论】:

      【解决方案6】:

      如果要处理加载的图像,则必须添加一个更多功能,因为

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

      刚刚开始将图像作为线程处理。

      你必须在加载原型的主体中添加一个新的函数,比如

        this.onload = function(e)
        {
          var canvas = document.createElement('canvas')
      
          canvas.width = this.width
          canvas.height = this.height
      
          canvas.getContext('2d').drawImage(this, 0, 0)
         }
      

      这让我很头疼:)

      【讨论】:

        【解决方案7】:

        只是为了增加改进,我修改了 Julian 的答案(这反过来又修改了 Sebastian 的)。我已将逻辑移至函数,而不是修改 Image 对象。该函数返回一个Promise,它与URL对象解析,只需要作为image标签的src属性插入即可。

        /**
         * 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" />

        【讨论】:

        • 这个可以转换成sn-p吗?
        • (老实说,我不知道您是否甚至可以在 SO 中使用 XMLHttpRequest - 但与其他答案相比,这将是一个很好的答案)
        • 嗯,它正在工作。如果图像被浏览器缓存,请尝试在开发工具上检查“禁用缓存”(如果加载速度过快,请使用限制)。
        • 谢谢~ 我意识到如果进度不可计算,我可以通过-1 而不是false。这将使参数始终保持为数字。我会进行编辑。
        • 漂亮的答案!
        猜你喜欢
        • 1970-01-01
        • 2014-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-04
        • 1970-01-01
        • 2012-02-28
        • 1970-01-01
        相关资源
        最近更新 更多