Nerdblog.pl - Image "Progressbar"

Reklama na blogach - Blogvertising.pl

Image "Progressbar"

Dodano: 12.04.2009

Obudziłem się dzisiaj rano i wiedziałem momentalnie co chcę zrobić - uruchomiłem komputer i zacząłem pisać skrypt, który już od dawna siedział mi po głowie. Efekty mojej pracy przerwane jedynie wielkanocnym śniadaniem można ujrzeć poniżej:

/*
 * Image progressbar (rewizja 7)
 * by Michał "D4rky" Matyas (http://nerdblog.pl)
 */
 
Progressbar = 
	{
	/*
	 * (int) image_count - ilość załadowanych obrazków
	 * (int) image_max - ilość obrazków możliwych do załadowania
	 * (int) image_percentage - %
	 *
	 * (array) to_load - obrazki do załadowania
	 * (array) callback_element - elementy, które mają być aktualizowane
	 * (array) callback_css - jaki atrybut ma zostać zmieniony w wyżej wym.
	 * (array) callback_postfix - jednostka do atrybutu
	 */
	image_count: 0,
	image_max: 0,
	image_percentage: 0,
	to_load: [],
	callback_element: [],
	callback_css: [],
	callback_postfix: [],
 
	/*
	 * Wewnętrzna funkcja ładująca obrazek.
	 * Po załadowaniu obrazka dodaje +1 do image_count
	 */
	_load: function(image)
		{
		var img = document.createElement("img");
		img.onload = function() { this.parent._addToCounter(); }
 
		img.parent = this;
		img.src = image;
		},
 
	/*
	 * Dodanie +1 do image_count, ponowne przeliczenie procentów i
	 * zaktualizowanie liczników na stronie (wizualnych)
	 */
	_addToCounter: function()
		{
		this.image_count++;
		this.image_percentage = Math.round((this.image_count / this.image_max) * 100);
		this.callback();
		},
 
	/*
	 * Callback, do nadpisania przez korzystającego ze skryptu
	 */
	callback: {},
 
	/*
	 * Ustawianie obrazków do ładowania.
	 * Przyjmuje array z listą linków lub pojedynczy plik
	 */
	add: function(image)
		{
		/*
		 * Uwaga, brak idiotoodporności. NIE, nie ruszam już tego.
		 */
		if(typeof image == "object")
			{
 			for(var i = 0, l = image.length; i < l; i++)
				{
				this.to_load.push(image[i]);
				}
			}
		else this.to_load.push(image);
		},
 
	/* 
	 * Uruchomienie skryptu, rozpoczęcie ładowania obrazków
	 */
	init: function()
		{
		this.image_max = this.to_load.length;
		for(var i = 0; i < this.image_max; i++)
			{
			this._load(this.to_load[i]);
			}
		}
	}

Skrypt testowy, żeby sprawdzić czy działa:

window.onload = function() 
	{ 
	/* obrazki losowe, skopiowałem ze stron, na których sprawdzałem różne pierdoły */
	Progressbar.addImage([
	"http://css.dzone.com/sites/all/themes/dzone/images/mh_logo_builder.gif",
	"http://stuff.nerdblog.pl/dowodzik.jpg",
	"http://www.w3schools.com/images/gsbx1.jpg"
	]); 
	Progressbar.callback = function() 
		{
		document.getElementById("loader").style.width = this.image_percentage+"px"; 
		}
 
	Progressbar.init();
}

Skrypt jest dostępny na licencji BSD. Początkowo miał korzystać z jQuery, ale gdy zauważyłem, że zaledwie w dwóch miejscach naprawdę mi się przydało, przepisałem całość "na czysto" dzięki czemu jest bardziej przenośna. Moje umiejętności Javascriptowe są dość skromne, ale jestem zadowolony z efektów mojej pracy, chociaż jak zawsze "można było zrobić lepiej".

Przykład wykorzystania? Sprzężenie z AJAXem i zrobienie ładnego paska ładowania w JS zamiast paskudnego, binarnego Flasha ;)

Edit: Zrobione na bardzo szybko (właśnie wychodzę) demko przedstawiające działanie skryptu - dodałem tam więcej obrazków, żeby trochę dłużej ładowało.

Edit (2): Podziękowania dla: RaVbakera za ogólne porady, Wasacza za ważne poprawki z zakresem zmiennych, BTMa i Michała Górnego za opieprz za .constructor, za typeof i za wskazanie zbędności dokładnego sprawdzania typu oraz dla Rafała Chłodnickiego za znalezienie i poprawkę do błędu w Operze.

56 komentarzy

Reklamy: sklep komputerowy ,

Jakieś demko skryptu?
A sama idea podoba mi się, bo mam wyłączoną animację gifów, i czasami wydaje mi się że skrypty wiszą, bo mi takiego ładnego gifa postępowego nie puszczają :)

12.04.2009, 15:20


Michał Górny

Daj jakaś stronę przykładową, coby można było w akcji zobaczyć.

12.04.2009, 15:22

demo plox

12.04.2009, 15:24

pecet – uploadowałem plik, na którym testowałem, ładne „demko” będzie dopiero jak skończę mój najnowszy projekt :P

12.04.2009, 15:29

Oczywiście dzięki temu, że jest callback zamiast zrobionego rozwiązania „na stałe”, można dać coś ładniejszego, np obrazki z postępem ładowania, zmieniającą się jasność strony i inne tego typu bajery – dałem tylko najprostszy przykład.

12.04.2009, 15:30

To, że po odświeżeniu cały progressbar nie działa, to bug czy ficzer? ;>

12.04.2009, 15:38

Ciupak – jeśli chodzi ci o to, że od razu jest 100% to to się nazywa ‘cache’, może kiedyś słyszałeś :P

12.04.2009, 15:45

Nie działa pod operą?

12.04.2009, 15:45

Tylko po odświeżeniu nie pokazuje 100%, tylko 0%.

12.04.2009, 15:46

A czemu miałoby pokazywać 100%? Troll ;S

12.04.2009, 15:46

Bo 100% oznacza, że „wszystko załadowane”?

12.04.2009, 15:47

A jak odświeżasz, to wszystko ładuje się od początku – na tym polega odświeżanie. Awesome!

12.04.2009, 15:48

Najwyraźniej Opera ma znowu problemy ze sobą. Potestuje jak wrócę.

12.04.2009, 15:48

Chociaż nie obrażę się jeśli ktoś podeśle do tego czasu patcha, bo jadę do rodziny i będę dopiero wieczorem.

12.04.2009, 15:50


Michał Górny

> img.parent = this;

Z jakiej racji?

> image.constructor == Array

Paskudztwo.

12.04.2009, 15:51

ad 1, żeby w onload móc się odwołać do klasy bezpośrednio, a nie przez nazwę.
Ad 2, podaj lepszy sposób, bo mi się po 4 kombinacjach skończyły pomysły. Instanceof?

12.04.2009, 15:55

ad 2. `typeof` i masz „object” lub „string” ?

12.04.2009, 16:03

BTM – a co mi da Object, skoro chodzi mi o Arr… a w sumie masz rację, w końcu i tak nie filtruje typów, więc mi to nie robi czy ktoś używający skryptu zrobi coś głupiego. Dzięki, poprawię :)

12.04.2009, 16:14

No to zostaje punkt pierwszy – czy przypisanie sobie przez referencję klasy jako atrybutu elementu, żeby potem odwołać się do niej podczas eventu bez korzystania z jego nazwy to zło? Discuss.

12.04.2009, 16:16

Nie ZUO. Tak się niestety robi w JS ;-)

12.04.2009, 16:17

IMO to przydatne, ale masz jakieś lepsze rozwiązanie?

12.04.2009, 16:17

Działa, działa… (Opera 10)

BTW, zdaje mi się że jest taki obiekt jak Image() w javascript. Nie wiem jak się z tego korzysta, bo sam teog nigdy nie robiłem, ale chyba nie jest to element drzewa DOM.
Takie coś jest łatwe do wdrożenia do jakiejś aplikacji opartej o canvas (chodzi mi po głowie takie coś, żeby zamiast flasha użyć w 100% canvas i javascript, z preloaderem i bajerami, na stronę domową, ale… afff, nie mam pomysłu co to miałoby być :P )

12.04.2009, 17:56


Michał Górny

Fluxid: To daj jakąś przykładową stronę, bo ja dalej chciałbym zobaczyć, jak to w praktyce wygląda.

12.04.2009, 17:57

Albo mi się zdaje, albo Progressbar nie jest globalny, ale wszystkie zadeklarowane zmienne wewnątrz ma globalne.

12.04.2009, 17:58

@Michał Górny: Z googla wziąłem to: http://articles.techrepublic.com.com/5100-10878_11-5214317.html i tez obsługuje onload z tego co pamiętam, mogę poszukać tego gdzie spotkałem się z tym po raz pierwszy

12.04.2009, 18:00


Michał Górny

fluxid: znaczy 404?

12.04.2009, 18:02

@Michał Górny:
That’s unexpected… teraz też mam 404. No to tutaj: http://www.webreference.com/programming/javascript/gr/column3/ ... w google wyszukałem: image javascript preloader onload

12.04.2009, 18:07


Michał Górny

fluxid: Tu za to 404 na demo.

12.04.2009, 18:09

Michał Górny: Ale kod mniej więcej pokazuje jak to działa… w zasadzie różni się tylko linijką w której tworzony jest obiekt, nie wiem jednak czym się to różni w funkcjonalności, nie wgłębiałem się w temat. Mam jednak obawy że przeglądarka ma prawo nie załadować obrazu, jeżeli img nie został wstawiony do drzewa DOM (jednak nie rozwieję teraz swoich obaw, bo mam gości w domu :P )

12.04.2009, 18:12

Fluxid – o to się nie martw, technikę którą użyłem stosuje się czasami do robienia XHRów. A co do canvas – IE. Chyba, że masz jakiś framework to podlinkuj

12.04.2009, 19:22

Wasacz – mógłbyś sprawdzić?

12.04.2009, 19:23

E tam, <canvas> obsysa. Prawdziwi mężczyźni robią wszystko za pomocą div-ów 1×1px pozycjonowanych absolutnie względem kontenera pozycjonowanego relatywnie ;P.

12.04.2009, 19:24

D4rky:
No najchętniej bym IE zupełnie zignorował. Na stronie domowej mogę tak postąpić i ewentualnie zrobić uproszczoną wersję html+css-only, ale w poważniejszych zastosowaniach pojawia się problem. Słyszałem można używać jakiejś flashki która udostępnia API podobne do tego z canvas, ale nie interesowałem się tym specjalnie… Mam nadzieję że canvas znajdzie swoje miejsce również w IE, bo to ma szansę zastąpić flasha w wielu zastosowaniach (a i API ma proste i przyjemne, podobne do tego z Cairo)

12.04.2009, 19:28

F. – tylko wydajność ma słabą, pogooglaj demka

12.04.2009, 19:29

Na pewno wolniejszą niż flash, ale obczaiłem wiele demek ( http://www.chromeexperiments.com/ http://www.p01.org/releases/ ) i na firefoxie działają bardzo szybko, w operze 10 trochę wolniej. Na pewno nadaje się do takich zastosowań jak animowane chmury tagów czy różne takie bzdety. Ja sam się bawiłem tym kilka tygodni temu i wyskrobałem coś takiego: http://fluxid.pl/canvas/test3.html (okazało się jednak że wygląda to inaczej na każdej przeglądarce, i nie wiem z czego to wynika. na fx wygląda najlepiej _ )

12.04.2009, 19:33

F. – no teraz to raczej nie zobaczę, z komórki jestem (dlatego twojej ksywki nie piszę, za długo to trwa). Znając życie zrobiłeś gdzieś pobożne założenie, że Opera to dobrze zaimplementowała :P Btw, podsumowanie proszę – co właściwie zepsułem w kodzie (o .constructor wiem) i czy działa w Operze 9.5 czy jaką tam jest najnowsza (za szybko numerki im skaczą, ale to dobrze)

12.04.2009, 19:36

D4rky: Opera 10: działa; Opera 9.6: działa; Firefox 3.0.8: działa; (A teraz windows na maszynie wirtualnej) Safari 4 beta: działa; Google Chrome 1.0.1: Działa; IE8 beta: działa.
Objawy działania: niebieski pasek zwiększa szerokość aż do momentu gdy wszystkie wskazówki w oknie przeglądarki pokażą że strona się załadowała.
A co do tego czy coś spieprzone to nie moja działka, specem od js nie jestem _
BTW: Zrobiłem pobożne założenie że cokolwiek mi zadziała, nieważne gdzie, a Tobie napisanie tego w nawiasie zajęło więcej niż zajęłoby napisanie nicka :)

12.04.2009, 19:45

Jeżeli chodzi o Image():

newImg = new Image();
newImg.src = img;
newImg.onload = function() {}

12.04.2009, 20:10

Fluxid – wierz mi, że nie – T9 ;)
Sajam – może mi ktoś powiedzieć, dlaczego tak bardzo – przepraszam za wyrażenie – masturbujecie się nad przestarzałą klasą do obsługi obrazka zachowaną chyba tylko dla wstecznej kompatybilności? new Regexp też używacie? Oo”
Przed chwilą odpaliłem kod wykonujący po 2000 razy proste przypisanie src do obrazka, raz wywołanego za pomocą createElement (1697 ms) i raz za pomocą new Image (1666 ms). Wyniki w obu przypadkach wahają się raz na korzyść jednego, raz drugiego więc zgaduję, że po n próbach wyszłoby uśrednione prawie tyle samo, a przynajmniej nie mam inicjalizacji zewnętrznej klasy, która tyle mi potrzebna co rybie rower, skoro chodzi tylko o załadowanie obrazka :|

12.04.2009, 20:37

Wasacz – o ile pod ‘globalność’ rozumiesz ‘możliwość wykorzystania w innej funkcji lub klasie’ to Progressbar jest globalny, a jego zmienne nie są (odwołać przez Progressbar.image_max możesz oczywiście, ale samo image_max „nie pyknie”)
Fluxid – ładne to demko z canvasami, pod Konquerorem i Firefoksem wygląda chyba identycznie, nie wiem jak z Operą ;)

12.04.2009, 20:46

D4rky:

>>> location.href 
"http://stuff.nerdblog.pl/progress-bar-demko.htm"
>>> toString.call(window.img) 
"[object HTMLImageElement]"
>>> toString.call(window.i) 
"[object Number]"

Globalne jak w mordę strzelił :P

12.04.2009, 21:01

Wasacz – /me rozkłada ręce. Jakiś pomysł?

12.04.2009, 21:02

Aha, i jeszcze odnosnie Image (nie czytałem komci, tak właściwie). Takie cuś:

var img = new Image();
img.src = 'pr0n.jpg';

Załaduje obrazek w momencie przypisywania atrybutu src.

Zapis new RegExp/Array/whatever wcale nie jest przestarzały, a wynika z tego, że JS jest (odkrycie) obiektowy.

12.04.2009, 21:04

Ad. „pomysł” – var zmienna, nie: zmienna.

12.04.2009, 21:05

Ten at tam to specjalnie?
A co do i – uh, czyli for (var i /* itd */) ? Oo”

12.04.2009, 21:07

Małpa poprawiona, reszta się zgadza ;]

12.04.2009, 21:09

Wydaje mi sie, że w init z pętli możesz wywalić this.image_max = this.to_load.length; Chyba długość tablicy nie zmieni się w czasie działania pętli :p

12.04.2009, 22:17

jkostrz – przeoczenie, dzięki :)

12.04.2009, 22:21

Usabiliy fail :/

13.04.2009, 02:43

Chris, ponieważ?

13.04.2009, 02:46

Z miliona powodow. Poczawszy od indeksowania, przez wyszukiwanie, po obsluge na urzadzedniach mobilnych i przy wylaczonym js. Dublujesz mozliwosci przegladarki – nie na tym poziomie takie rzeczy sie robi. Zabijasz wlasciwosci progressive i non-progressive images. Co innego flash, z ktorym browsery sobie nei radza, a co innego grafiki, ktore sa calkiem niezle obslugiwane.

13.04.2009, 02:57

Chris, źle zrozumiałeś intencje :) Ten skrypt nie ma być używany do preloadowania obrazków na statycznej stronie – to by było idiotyzmem. Ma z kolei zapewniać progressbar przy stosowaniu AJAXu, którego i tak ani rpzez wyszukiwanie, ani na urządzeniach mobilnych, ani tym bardizej przy wyłączonym JS nie używasz.
Poza tym siejesz trochę FUD – żaden fragment kodu nie sugeruje jego inwazyjnego wykorzystania (to tylko preloader – jeśli nie ma JS to i tak się nie włączy, więc co za strata?), progressive images my ass, że się tak wyrażę, bo doładowywanie się obrazka w trakcie czytania jest jedną z najbardziej wkurzających rzeczy przy przeglądaniu internetu, a tekstu z dublowaniem przeglądarki nie rouzmiem ni w ząb.
Ogólnie: przeczytaj ten wpis i przejrzyj kod jeszcze raz, jutro, o ludzkiej porze to sam zaczniesz się zastanawiać co właściwie za bzdury wygadujesz w komentarzach :P

13.04.2009, 03:02

Tak odnośnie tego dumania nad wykrywaniem tablicy, nie lepiej zrobić tego odwrotnie:

if (typeof image == "string") {}
else {}

Zamiast:

if (typeof image == "object") {}
else {}

I wydaje się bardziej przejrzyście, imho ;)

Nazwa metody addImage jest myląca, bo można dodać jeden albo więcej obrazków.

A na koniec:

img.parent = this;

Na coś mniej zatruwającego; chociażby poprzedzić właściwość podkreśleniem. Szkoda, że nie zastosowałeś camelCase, bo w JS właściwie tylko on jest „słuszny” ;]

Psst: tu nie ma podglądu komcia?

13.04.2009, 20:01

Wasacz – a można i tak, ale już jestem zmęczony pieprzeniem się z jedną linijką kodu tylko po to, żeby puryści językowi mogli się podniecać zyskiem kilku milisekund lub większą dokładnością sprawdzenia – zostawcie to.
Co do nazwy metody to faktycznie masz rację, ale to głównie dlatego, że obsługę tablic dodałem w ostatniej chwili i nie chciało mi się już zmieniać. Zmienię.
A co do podglądu komentarza – nie, nie ma. Myli się z dodawaniem, przeszkadza i wpieprza się do tabulacji gdy nie jest potrzebny. Podglądowi komentarza mówię stanowcze nie.

13.04.2009, 20:44

D4rky – żeby Cie jeszcze powkurzać :P, kilka milisekund zysku powinno dać odwrócenie pętli :D
Albo w ogóle w add zamiast pętli dac concate, tzn co do ostatniego nie jestem pewien czy przyspieszy, ale na pewno za to mniej znaków ;)

A tak troche poważniej, to te moje porady jeśli przyniosą jakąś poprawe wydajności to co najwyżej pewnie o 1ms, no może 2ms, hehe, bo chyba nikt nie bedzie pakował 1000 czy 10000 obrazków.

Przy okazji testowania Twojego skryptu ciekawą rzecz zauważyłem jaką Google stosuje z logiem i innymi grafikami którymi urzywa na swoich stronach, pakują kilka grafik do jednego pliku :) http://www.google.pl/images/nav_logo4.png

13.04.2009, 21:02

@jkostrz: pst. Stare. CSS Sprite się to nazywa ;]

13.04.2009, 21:03

Ze względu na częsty trolling oraz osoby pokroju dzieci neostrady, komentowanie na blogu działa jedynie dla użytkowników serwisu blogowego Jogger.pl.

Jeśli chcesz przekazać mi swój komentarz, możesz zrobić to prywatnie poprzez sieć Jabber lub na maila. Przepraszam za utrudnienia.