最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Performance issues when loading local images into the browser - Stack Overflow

programmeradmin0浏览0评论

I'm using FileReader to display local images in a Backbone application. Smaller images are displayed immediately without any issues, but when the images are several megabytes or larger, it takes Chrome 2 or 3 seconds to load the image (on a brand new MacBook Pro with 8 GB RAM).

As pointed out in the ments, large images load instantaneously in this File API tutorial, so the issue is not with FileReader but rather with my particular implementation in Backbone.

I've included the relevant Backbone model and view below. Here's an outline of the logic:

  1. The model creates a new FileReader into which the file path is passed.
  2. Once the file is loaded, an Image is created and the src of the Image is set to the data URL provided by the FileReader.
  3. Once the image has loaded, the model is updated with the width and height of the image (used elsewhere in the application) and the initialized attribute of the model is set to true (thus firing a change event on the model).
  4. When the view detects the change event on the model, the render function is called, which replaces the background image of a div with the data URL.

Backbone Model

var PhotoModel = Backbone.Model.extend({

  initialize: function() {
    this.set({"available": true});
    this.initialized = false;
    if (this.get("file")) { 
      this.uploadPhoto();
    }
  },

  uploadPhoto: function() {
    var file = this.get("file");
    var reader = new FileReader();
    var model = this;
    reader.onload = function(event) {
      var image = new Image();
      image.onload = function() {
        model.set({width: this.width, height: this.height});
        model.set("initialized", true);
      };
      image.src = event.target.result; 
      model.set("dataURL", event.target.result);
    }
    reader.readAsDataURL(file);
  },
});

Backbone View

var PhotoFormView = Backbone.View.extend({

  className: "photo-form",

  initialize: function() {
    this.listenTo(this.model, "change", this.render);
    this.render();
  },

  render: function() {
    $(this.el).find(".photo").css({"background-image": "url(" + this.model.get("dataURL") + ")"});
  },
});

Below is a screenshot of the Chrome timeline. The largest delay seems to happen between the data URL request and the load event (this accounts for approximately 0.5 seconds). I'm not sure what "Recalculate style" is all about. I'm also not sure why there are so many paint events (it appears to be rendering in the scrollbar area).

SOLUTION

I had assumed that the browser would be able to natively resize images more efficiently than any JavaScript solution, but this assumption was wrong. By resizing the images before they are displayed, I was able to reduce the delay to about 50 milliseconds. I used an open source plugin called JavaScript Load Image to handle the image loading and resizing (it uses the HTML5 canvas tag). Here's my modified Backbone model:

var PhotoModel = Backbone.Model.extend({

  initialize: function() {
    this.set({available: true});
    this.set({initialized: false});
    if (this.get("file")) { 
      this.uploadPhoto();
    }
  },

  uploadPhoto: function() {
    var model = this;
    loadImage(this.get("file"), function (img) {
      model.set({img: img});
      model.set({initialized: true});
    }, {maxHeight: 344});
  },
});

I'm using FileReader to display local images in a Backbone application. Smaller images are displayed immediately without any issues, but when the images are several megabytes or larger, it takes Chrome 2 or 3 seconds to load the image (on a brand new MacBook Pro with 8 GB RAM).

As pointed out in the ments, large images load instantaneously in this File API tutorial, so the issue is not with FileReader but rather with my particular implementation in Backbone.

I've included the relevant Backbone model and view below. Here's an outline of the logic:

  1. The model creates a new FileReader into which the file path is passed.
  2. Once the file is loaded, an Image is created and the src of the Image is set to the data URL provided by the FileReader.
  3. Once the image has loaded, the model is updated with the width and height of the image (used elsewhere in the application) and the initialized attribute of the model is set to true (thus firing a change event on the model).
  4. When the view detects the change event on the model, the render function is called, which replaces the background image of a div with the data URL.

Backbone Model

var PhotoModel = Backbone.Model.extend({

  initialize: function() {
    this.set({"available": true});
    this.initialized = false;
    if (this.get("file")) { 
      this.uploadPhoto();
    }
  },

  uploadPhoto: function() {
    var file = this.get("file");
    var reader = new FileReader();
    var model = this;
    reader.onload = function(event) {
      var image = new Image();
      image.onload = function() {
        model.set({width: this.width, height: this.height});
        model.set("initialized", true);
      };
      image.src = event.target.result; 
      model.set("dataURL", event.target.result);
    }
    reader.readAsDataURL(file);
  },
});

Backbone View

var PhotoFormView = Backbone.View.extend({

  className: "photo-form",

  initialize: function() {
    this.listenTo(this.model, "change", this.render);
    this.render();
  },

  render: function() {
    $(this.el).find(".photo").css({"background-image": "url(" + this.model.get("dataURL") + ")"});
  },
});

Below is a screenshot of the Chrome timeline. The largest delay seems to happen between the data URL request and the load event (this accounts for approximately 0.5 seconds). I'm not sure what "Recalculate style" is all about. I'm also not sure why there are so many paint events (it appears to be rendering in the scrollbar area).

SOLUTION

I had assumed that the browser would be able to natively resize images more efficiently than any JavaScript solution, but this assumption was wrong. By resizing the images before they are displayed, I was able to reduce the delay to about 50 milliseconds. I used an open source plugin called JavaScript Load Image to handle the image loading and resizing (it uses the HTML5 canvas tag). Here's my modified Backbone model:

var PhotoModel = Backbone.Model.extend({

  initialize: function() {
    this.set({available: true});
    this.set({initialized: false});
    if (this.get("file")) { 
      this.uploadPhoto();
    }
  },

  uploadPhoto: function() {
    var model = this;
    loadImage(this.get("file"), function (img) {
      model.set({img: img});
      model.set({initialized: true});
    }, {maxHeight: 344});
  },
});
Share Improve this question edited Jul 3, 2013 at 9:12 David Jones asked Jul 3, 2013 at 3:36 David JonesDavid Jones 10.2k30 gold badges93 silver badges145 bronze badges 0
Add a ment  | 

1 Answer 1

Reset to default 7

I was encountering the same problem when loading large images using FileReader. If you're not planning on displaying them at full size, it seems the workaround involves scaling the image before displaying it using a canvas element.

After mixed success with my own implementation, I opted to use JavaScript Load Image (MIT license) and its maxWidth option, which solved this and more (unforseen) problems.

Try the the demo and see if your images load nicely.

发布评论

评论列表(0)

  1. 暂无评论