2013-07-03 15 views
18

Używam FileReader do wyświetlania obrazów lokalnych w aplikacji Szkielet. Mniejsze obrazy są wyświetlane natychmiast, bez żadnych problemów, ale gdy zdjęcia mają kilka megabajtów lub więcej, ładowanie obrazu zajmuje dwa lub trzy sekundy (na zupełnie nowym MacBooku Pro z 8 GB pamięci RAM).Problemy z wydajnością podczas ładowania obrazów lokalnych do przeglądarki

Jak wskazano w komentarzach, duże obrazy ładują się natychmiast w this File API tutorial, więc problem nie jest związany z FileReader, ale raczej z moją konkretną implementacją w szkielecie.

Dołączyłem odpowiedni model szkieletu i widok poniżej. Oto zarys logiki:

  1. Model tworzy nowy FileReader, w którym przekazywana jest ścieżka do pliku.
  2. Po załadowaniu pliku zostanie utworzony Image, a src z Image zostanie ustawiony na adres URL danych dostarczony przez FileReader.
  3. Po załadowaniu obrazu model jest aktualizowany o szerokość i wysokość obrazu (używany w innym miejscu w aplikacji), a atrybut initialized modelu jest ustawiony na true (w ten sposób wywołując zdarzenie change w modelu).
  4. Gdy widok wykryje zdarzenie change w modelu, wywoływana jest funkcja render, która zastępuje obraz tła adresu div adresem URL danych.

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 Zobacz

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") + ")"}); 
    }, 
}); 

Poniżej znajduje się zrzut ekranu z osi czasu Chrome. Wydaje się, że największe opóźnienie występuje między żądaniem adresu URL danych a zdarzeniem ładowania (wynosi to około 0,5 sekundy). Nie jestem pewien, o co w tym wszystkim chodzi "Zmień styl". Nie jestem również pewien, dlaczego jest tak wiele zdarzeń malowania (wydaje się, że renderowanie w obszarze paska przewijania).

Chrome Timeline

ROZWIĄZANIE

Przypuszczałem, że przeglądarka będzie w stanie natywnie skuteczniej niż jakakolwiek zmiana rozmiaru obrazów rozwiązanie JavaScript, ale założenie to było błędne. Zmieniając rozmiar obrazów, które są wyświetlane, udało mi się zmniejszyć opóźnienie do około 50 milisekund. Użyłem wtyczki open source o nazwie JavaScript Load Image do obsługi ładowania i zmiany rozmiaru obrazu (używa znacznika HTML5 canvas). Oto mój zmodyfikowany model szkieletu:

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

Odpowiedz

7

Ten sam problem wystąpił podczas ładowania dużych obrazów przy użyciu FileReadera. Jeśli nie planujesz wyświetlania ich w pełnym rozmiarze, wydaje się, że obejście polega na skalowaniu obrazu przed wyświetleniem go za pomocą elementu canvas.

Po sukcesie mieszanym z moją własną implementacją, zdecydowałem się użyć opcji JavaScript Load Image (licencja MIT) i jej opcji maxWidth, która rozwiązała ten i więcej (niewidzianych) problemów.

Wypróbuj the demo i sprawdź, czy ładnie ładują się Twoje zdjęcia.

+1

To był dokładnie problem. Założęłem się, że przeglądarka byłaby bardziej wydajna przy zmianie rozmiaru obrazu natywnie zamiast wywoływania rozwiązania JavaScript. Tak jednak nie jest. Zmieniając rozmiar obrazu przed jego wyświetleniem, całkowicie wyeliminowałem opóźnienie. Wtyczka, którą zasugerowałeś, jest absolutnie idealna do tego scenariusza. Dzięki! –

+0

Cieszę się, że pracował dla Ciebie, kudos do https://github.com/blueimp za ciężką pracę! – lazd

Powiązane problemy