[JS] preloaden van afbeeldingen (geavanceerd)

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik ben op dit moment bezig met een website waarbij de content 99% d.m.v. javascript (ajax) wordt gegenereerd.
Eén van de onderdelen zijn de afbeeldingen die, voordat ze getoond worden, met canvas worden bewerkt.

Het probleem wat ik nu heb is dat de afbeeldingen (nog) niet geladen zijn op het moment dat de functie voor de canvas bewerking wordt aangeroepen, wat uitkomt tot errors.

Nu probeer ik direct nadat ik weet welke afbeeldingen er getoond moeten worden deze direct te laden met
JavaScript:
1
2
3
4
5
// images is een array met uri's van de afbeeldingen
$.each(images, function(i, image) {
   var img = new Image();
   img.src = image;
});


Helaas zijn er teveel afbeeldingen en te grote afbeeldingen om deze manier te gebruiken (of Javascript is te snel), dus heb ik een aantal andere technieken opgezocht voor het laden van afbeeldingen. Helaas werkt er geen een, omdat er onmogelijk lijkt om te zien of een afbeelding ook daadwerkelijk geladen is.

Aantal script die ik heb geprobeerd:
JavaScript:
1
2
3
4
5
6
7
8
9
10
// tweede poging
// images is een jQuery object met alle afbeeldingen in DOM
var loadCount = images.length;
if(loadCount > 0) {
    images.load(function() {
        if(0 === --loadCount) {
            // voer canvas functie uit
        }
    });
}


JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
// derde poging
// images is een jQuery object met alle afbeeldingen in DOM
var loadCount = images.length;
if(loadCount > 0) {
    $.each(images, function(i, image) {
        $(image).load(function() {
            if(0 === --loadCount) {
                // voer canvas functie uit
            }
        });
    }
}


Deze twee scripts werken beide niet omdat loadCount niet of te vroeg op 0 komt, waardoor de canvas functie crashed...

Ook de "fix" die te vinden in de documentatie van jQuery zelf werkt niet.


Kan iemand een goede oplossing verzinnen? Of in ieder geval in de goede richting helpen?

Wanneer er nog vragen zijn m.b.t. het script kan ik die uiteraard beantwoorden, ik kan/mag helaas geen link geven van de site (zowel van Tnet als van klant). Maar mijn uitleg zou voldoende moeten zijn.

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 10-09 08:45
Nasty, maar iframe met de afbeelding creeren en de onload van het iframe uitlezen. Je kunt ook meerdere afbeeldingen in een keer in 1 iframe stoppen natuurlijk om het iets efficienter te maken.

Het load-event afvangen van afbeeldingen is zeer onbetrouwbaar crossbrowser en met caching.

[ Voor 55% gewijzigd door Bosmonster op 18-01-2011 15:20 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op dinsdag 18 januari 2011 @ 15:14:
ik kan/mag helaas geen link geven van de site (zowel van Tnet als van klant)
Dat is natuurlijk onzin; "van Tnet" mag 't prima als het een uitgeklede testcase is (en dus niet vergeven van de merknamen, logo's en zolang het bestaat uit de hoognodige code om je probleem te illustreren / reproduceren en dus niet 1500 regels JS/CSS/HTML/etc). Voor de klant boeit 't ook niet zolang je 't "anonimiseert".

Over je probleem: ik weet dat onload k-met-peren is, maar to be honest weet ik niet hoe goed jQuery zoiets heeft kunnen encapsuleren. Ik heb dan ook niet meteen een antwoord op je vraag.

[ Voor 15% gewijzigd door RobIII op 18-01-2011 15:30 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bosmonster schreef op dinsdag 18 januari 2011 @ 15:19:
Nasty, maar iframe met de afbeelding creeren en de onload van het iframe uitlezen. Je kunt ook meerdere afbeeldingen in een keer in 1 iframe stoppen natuurlijk om het iets efficienter te maken.

Het load-event afvangen van afbeeldingen is zeer onbetrouwbaar crossbrowser en met caching.
Geeft een browser dan geen problemen omdat de afbeelding niet in het document zelf is geladen, maar in een IFrame?
Ben zelf geen van van frames/iframes, maar sinds het in HTML5 gebeurt "mag het weer" ;)

Het caching probleem is wel opgelost met de jQuery fix (geeft netjes een 'complete' terug wanneer het al in de cache staat), maar het werkt helaas niet met nieuwe afbeeldingen.
RobIII schreef op dinsdag 18 januari 2011 @ 15:28:
Dat is natuurlijk onzin; "van Tnet" mag 't prima als het een uitgeklede testcase is (en dus niet vergeven van de merknamen, logo's en zolang het bestaat uit de hoognodige code om je probleem te illustreren / reproduceren en dus niet 1500 regels JS/CSS/HTML/etc). Voor de klant boeit 't ook niet zolang je 't "anonimiseert".
In dit geval is het nodig om live data van de klant te gebruiken, en die is er niet blij mee als het nu al open en bloot op het internet staat (zelfs geen testcase).
Het probleem is er namelijk niet (of zo gering dat men er niets van merkt) bij lokale afbeeldingen.

Wat betreft Tweakers; ik dacht altijd dat het niet wenselijk was om alles extern te zetten (dode links etc), daarnaast kan de belangrijke code gemakkelijk in een paar regels worden gezet zonder alle meuk.
RobIII schreef op dinsdag 18 januari 2011 @ 15:28:
Over je probleem: ik weet dat onload k-met-peren is, maar to be honest weet ik niet hoe goed jQuery zoiets heeft kunnen encapsuleren. Ik heb dan ook niet meteen een antwoord op je vraag.
De oplossing hoeft geen jQuery te zijn (het vertraagt zelfs), maar omdat het toch al gebruikt wordt is het geen probleem wanneer het nodig is.

Acties:
  • 0 Henk 'm!

  • OkkE
  • Registratie: Oktober 2000
  • Laatst online: 04-09 08:16

OkkE

CSS influencer :+

Misschien kun je iets met "img.naturalWidth" en "img.naturalWidth" doen? Wanneer je weet elke image het moet zijn, in een loop blijven (met timeout) en pas als die geladen is de Canvas functie?

De functie die ik tegen kwam:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function isImageOk(img) {
    // During the onload event, IE correctly identifies any images
    // that weren't downloaded as not complete. Others should too.
    // Gecko-based browsers act like NS4 in that they report this
    // incorrectly: they always return true.
    if (!img.complete) {
        return false;
    }

    // However, they do have two very useful properties: naturalWidth
    // and naturalHeight. These give the true size of the image. If
    // it failed to load, either of these should be zero.
    if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) {
        return false;
    }

    // No other way of checking: assume it's ok.
    return true;
}


Schijnbaar alleen getest in Gecko en IE. En voor jou dan misschien false laten returnen tot de timeout.




Of wat ik ook nog tegen kwam, maar dat lijkt me iets lelijker, en misschien helemaal niet cross-browser?

HTML:
1
<img onload='loaded(this.id)' src="a_really_big_file.jpg">

[ Voor 8% gewijzigd door OkkE op 18-01-2011 16:29 ]

“The best way to get the right answer on the Internet is not to ask a question, it's to post the wrong answer.”
QA Engineer walks into a bar. Orders a beer. Orders 0 beers. Orders 999999999 beers. Orders a lizard. Orders -1 beers.


Acties:
  • 0 Henk 'm!

  • Klaasvaak
  • Registratie: Maart 2010
  • Laatst online: 12-09 21:10
naturalWidth/-height werkt enkel in Gecko, en geeft de echte hoogte van een afbeelding indien je deze zou scalen

(new Image(320, 240)).src="640x480.jpg" --> naturalWidth=640, naturalHeight=480, width=320, height=240.

Maar in een loop kijken of image.width/image.height niet 0 is, lijkt wel een mogelijkheid.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ok, ik het een oplossing gevonden (met dank aan mijn collega's).

Hier een beknopte versie:
JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var returnData; // object met data
var countLoader = countLoaded = 0; // tellertjes met aantal te laden en aantal geladen

function getFile() {
   // haal dmv ajax de feed op
   // bij 'success' roep functie 'preloader()' aan
}

function preloader( data ) {
   returnData = data; // globaal maken

   $.each(data, function(x, itemObj) {
      var images = itemObj.images;
      $.each(images, function(y, image) {
          countLoader++;
          imageLoader( image );
      });
   });
}

function imageLoader( image ) {
    var img = new Image();
    img.src = image;
    img.onload = function() {
       countLoaded++

       if(countLoaded === countLoader) {
           myFunc();
       }
    }
}

function myFunc() {
   data = returnData; // haal data weer op

   // voer script uit
}


De functie myFunc() wordt nu alleen uitgevoerd wanneer alle afbeeldingen zijn geladen.

Ik weet alleen nog niet wat er gebeurd wanneer een afbeelding niet bestaat (of andere HTTP error), maar dat merk ik (en de klant) nog wel.

Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 10-09 08:45
img.onload is onbetrouwbaar en triggered vaak in z'n geheel niet als een afbeelding uit de cache komt (en je app hangt vervolgens).

Ik dacht dat daar het hele topic over ging, maar nu is dat ineens onderdeel van je oplossing :?

[ Voor 38% gewijzigd door Bosmonster op 18-01-2011 18:05 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bosmonster schreef op dinsdag 18 januari 2011 @ 18:05:
img.onload is onbetrouwbaar en triggered vaak in z'n geheel niet als een afbeelding uit de cache komt (en je app hangt vervolgens).

Ik dacht dat daar het hele topic over ging, maar nu is dat ineens onderdeel van je oplossing :?
Het probleem is/was niet dat onload niet werkte, maar dat het script niet wachtte totdat alle afbeeldingen geladen waren.

Ik zie alleen op verschillende sites dat het wel handig is om de source pas te zetten NA de onload handler (ivm cache), en om een onerror handler bij te zetten (just in case).

Het cache probleem kan ik overigens ook oplossen door eerst te kijken of de afbeelding(en) al bestaat en in het ergste geval de afbeeldingen helemaal niet laten cachen (dmv .htaccess).


Gelukkig moet het alleen werken in IE9, de rest is mooi meegenomen... En sinds onload/onerror IE-eigen zijn, zou het geen probleem mogen zijn (zelfs icm cache). En anders ga ik klagen bij Microsoft (heb een direct nummer ;) )

Acties:
  • 0 Henk 'm!

  • pieturp
  • Registratie: April 2004
  • Laatst online: 27-08 14:18

pieturp

gaffa!

Edit: nevermind, zie hierboven...

@Bosmonster:

Ik wil in geen geval je bewering onderuithalen, maar ik heb veel gewerkt met een vergelijkbare methode, en nooit problemen gehad met het onload event. Zelfs met IE6 was dit nooit een probleem met deze benadering. Ook niet als de images lokaal al gecached waren.

Wel zie ik 't verschil dat hier de eventHandler ná de image load wordt gebind, terwijl ik dat bewust niet deed:

[code=js]
var i = new Image();
i.onload = function(){};
i.onerror = function(){};
i.src = 'http://somewhere/img.ext';
[/code=js]

Kun je een concreet voorbeeld (of bron) geven m.b.t. 't door jou geschetste probleem?

[ Voor 3% gewijzigd door pieturp op 19-01-2011 01:09 ]

... en etcetera en zo


Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 10-09 08:45
Ik had TS verkeerd geinterpreteerd. Zolang je idd het load event koppelt VOOR je de src zet, is het geen probleem. Issue zit hem voornamelijk als je het laden bestaande afbeeldingen in een pagina wilt detecteren.

Acties:
  • 0 Henk 'm!

  • Ravefiend
  • Registratie: September 2002
  • Laatst online: 12-09 13:56

Ravefiend

Carpe diem!

Veroorzaakt die genoemde oplossing geen extra vertraging doordat de images nu serieel worden geladen?

Acties:
  • 0 Henk 'm!

  • noes
  • Registratie: Augustus 2006
  • Niet online

noes

gek op benzine.

Makkelijker was wellicht deze oplossing:
http://api.jquery.com/load-event/

Example: Run a function when the page is fully loaded including graphics.
$(window).load(function () {
// run code.
});

Werkt goed cross-browser (niet getest op IE7). Hiermee kan je alles inladen, evt op de achtergrond, en hierna je code kickstarten.

K54/R1250RS | K48/K1600GT | E61/550i


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Bosmonster schreef op woensdag 19 januari 2011 @ 08:59:
Ik had TS verkeerd geinterpreteerd. Zolang je idd het load event koppelt VOOR je de src zet, is het geen probleem. Issue zit hem voornamelijk als je het laden bestaande afbeeldingen in een pagina wilt detecteren.
Mijn TS was hier misschien ook niet helemaal duidelijk in...
Ravefiend schreef op woensdag 19 januari 2011 @ 09:08:
Veroorzaakt die genoemde oplossing geen extra vertraging doordat de images nu serieel worden geladen?
De extra vertraging is niet heel erg, en zelfs een beetje wenselijk (lang verhaal, maar "moest" van de klant 8)7).


@mod: mijn vraag is beantwoord en topic kan gesloten worden indien nodig.
@de rest: jullie zien de site vanzelf op Tweakers.net verschijnen ;)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
noes schreef op woensdag 19 januari 2011 @ 10:39:
Makkelijker was wellicht deze oplossing:
http://api.jquery.com/load-event/

Example: Run a function when the page is fully loaded including graphics.
$(window).load(function () {
// run code.
});

Werkt goed cross-browser (niet getest op IE7). Hiermee kan je alles inladen, evt op de achtergrond, en hierna je code kickstarten.
Zoals in de TS wordt verteld werkt dit niet vanwege een fout in jQuery.
$.load geeft geen succes terug wanneer de afbeelding al gecached is (er hoeft immers niets geladen te worden), en de "fix" die dit zou moeten oplossen geeft meer problemen dan oplossingen.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Verwijderd schreef op woensdag 19 januari 2011 @ 10:39:
@mod: mijn vraag is beantwoord en topic kan gesloten worden indien nodig.
Een slotje is niet nodig op een topic als je je oplossing hebt. Zie daarvoor ook onze faq betreffende topiceinde.
Verwijderd schreef op woensdag 19 januari 2011 @ 10:39:
@de rest: jullie zien de site vanzelf op Tweakers.net verschijnen ;)
Hou je rekening met ons spam beleid? ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Bosmonster
  • Registratie: Juni 2001
  • Laatst online: 10-09 08:45
Verwijderd schreef op woensdag 19 januari 2011 @ 11:05:
[...]


Zoals in de TS wordt verteld werkt dit niet vanwege een fout in jQuery.
$.load geeft geen succes terug wanneer de afbeelding al gecached is (er hoeft immers niets geladen te worden), en de "fix" die dit zou moeten oplossen geeft meer problemen dan oplossingen.
Ten eerste, dit is geen fout in jQuery, maar in bepaalde browsers. Ten tweede betreft dit niet het load-event van het window, maar van images.

Deze methode zou prima werken, maar geeft je weinig flexibiliteit in het dynamisch laden van afbeeldingen. Je zou dit wel kunnen combineren met een iframe, zoals ik eerder al aangaf.

Ik weet echter niet hoe browsers omgaan met het load-event op het window in het geval dat afbeeldingen via CSS op onzichtbaar staan. Kan me voorstellen dat browsers dit ook optimaliseren en deze afbeeldingen niet direct laden.

[ Voor 27% gewijzigd door Bosmonster op 19-01-2011 11:19 ]


Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Ravefiend schreef op woensdag 19 januari 2011 @ 09:08:
Veroorzaakt die genoemde oplossing geen extra vertraging doordat de images nu serieel worden geladen?
Kun je nog afvangen door een parallel aantal queues te starten. Als je toch al jQuery gebruikt kun je daarvoor $({}).queue() gebruiken. (Ja, de queuing functionaliteit werkt ook op plain javascript objects. Maakt die hele feature ineens stukken bruikbaarder en overzichtelijker.)

Overigens is in jQuery 1.5 de hele $.ajax functionaliteit herbouwt en werkt deze voortaan met definieerbare transport types, waaronder ook images.

[ Voor 13% gewijzigd door R4gnax op 20-01-2011 09:14 ]


Acties:
  • 0 Henk 'm!

  • noes
  • Registratie: Augustus 2006
  • Niet online

noes

gek op benzine.

Verwijderd schreef op woensdag 19 januari 2011 @ 11:05:
[...]


Zoals in de TS wordt verteld werkt dit niet vanwege een fout in jQuery.
$.load geeft geen succes terug wanneer de afbeelding al gecached is (er hoeft immers niets geladen te worden), en de "fix" die dit zou moeten oplossen geeft meer problemen dan oplossingen.
Daarom hang je m aan de window, niet aan de images. Mooie is dus dat je ook meteen je hele inlaad proces kan verbergen met een loadertje.

K54/R1250RS | K48/K1600GT | E61/550i


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
noes schreef op donderdag 20 januari 2011 @ 10:13:
[...]

Daarom hang je m aan de window, niet aan de images. Mooie is dus dat je ook meteen je hele inlaad proces kan verbergen met een loadertje.
De reden dat het niet aan de window gehangen kan worden is omdat de afbeeldingen geladen moeten worden nadat de window al een load event heeft gegeven. De site bestaat uit 1 pagina die dynamisch (ajax) wordt gevuld en er dynamisch weer extra content bij kan komen.

De afbeeldingen kunnen overigens ook niet "on-the-fly" worden geladen, omdat er een canvas bewerking overheen gaat, dus de afbeelding (per stuk) moet geladen zijn voordat de functie wordt gestart.
Pagina: 1