Jakiś czas temu chciałem korzystając z funkcji each() w jQuery wyświetlić serię załadowanych AJAXem elementów jeden po drugim, z opóźnieniem po każdym kolejnym.
Trafiłem na kilka różnych sposobów, ale żaden mnie nie zadowalał. O wiele za bardzo komplikowały prostą sprawę.
Dzisiaj przyszło mi do głowy rozwiązanie tak proste, że trudno uwierzyć, że nie wpadłem na nie od razu.
Na przykładzie: pokazywanie elementów listy jeden po drugim, z przerwą 300ms po każdym. Wydaje się, że wystarczy setTimeout():
$('li').each(function(){
var $li = $(this);
setTimeout(function() {
$li.fadeIn(500);
}, 300);
});
Ale to nie aż tak proste, bo w ten sposób wszystkie elementy pokażą się razem, z opóźnieniem 300ms od momentu wywołania akcji (np. klik na przycisk), a my chcemy uzyskać takie opóźnienie po animacji każdego kolejnego elementu.
Z pomocą przychodzi parametr index w funkcji each() i odrobina logiki. :)
Skoro nie chcę opóźnienia przed pokazaniem pierwszego elementu, a 300ms przerwy po każdym kolejnym tzn., że potrzebuję przerwy 0, 300, 600, 900ms, itd. licząc od momentu wywołania akcji. Najprościej zrobić to mnożąc index, czyli numer elementu (0-based) przez wartość opóźnienia.
$('li').each(function(index){
var $li = $(this);
setTimeout(function() {
$li.fadeIn(500);
}, index * delay);
});
Takie proste. ;) Zobacz demo.
UPDATE: A jednak nie do końca.
Jak Riddle słusznie zauważył w komentarzach, odpalanie wielu timerów jednocześnie może spowolnić przeglądarkę. W moim przykładzie nie robi to różnicy, bo elementów jest tylko kilka. Przetestowałem swoje demo dla 100 elementów listy - skok zużycia procesora to maksymalnie 15% w momencie startu, a przeglądarki w ogóle nie odczuwają różnicy (Firefox 3.5, Safari 4, Opera 10 /Mac). Być może przy jeszcze większej liczbie, bądź na słabszym sprzęcie dałoby się odczuć jakieś problemy.
Tak czy inaczej, z pewnością lepiej i bezpieczniej unikać odpalania wielu setTimeout() jednocześnie.
Komentarze
OMG… po co, skoro jQuery kolejkuje od ręki? ;/
OMG, a przeczytałeś? jQuery kolejkuje, ale tu chodzi o timeout.
Przeczytałem.
Sam timeout może mieć sens w tylko jednym przypadku, gdy masz pewność, że animacja każdego elementu będzie trwać dokładnie tyle samo. Więc twój kod zadziała, ale dla identycznych elementów z prostym fade in. Sytuacja: przychodzi klient i mówi, że chce slide down zamiast fade in, w jednym bloczku jest linia tekstu więcej i animacja się psuje. W kolejkowaniu – nie. No i JS to nie harmonogram zadań, żeby mu planować po kilka(naście) timeoutów, które czekają na wystrzał.
Wasacz - bądz realistą. Klient chce - nie ma rady ;) Chyba, że znasz lepszy sposób na kolejkowanie animacji, ja nie znam.
Wasacz: niezupełnie. Problemy mogłyby się pojawić przy dużych różnicach między czasem trwania animacji, a przerwą pomiędzy animacją kolejnych elementów. I to wcale nie poważne, po prostu elementy nakładałyby się na siebie.
A tutaj mam nad tym wszystkim pełną kontrolę.
Slide down, nawet z elementami o różnych wysokościach, nie jest problemem. Jeśli mam slideDown(500), animacja trwa pół sekundy niezależnie od wysokości elementu.
I chodziło o to, że nie znalazłem lepszego sposobu na takie animacje. Widywałem i takie na 20 linijek kodu, a też napisane w jQ i z wykorzystaniem setTimeout().
Nie za dobry sposób, ponieważ przy większej liczbie elementów możesz nieźle spowolnić przeglądarkę – odpalanie na raz n timerów nie zalicza się do dobrych praktyk.
Lepszy wybór:
Pardon, za szybko wysłałem kod. Poprawka, uwzględniająca pierwszy element:
Edit: Teraz powinno być okej. :)
Riddle: racja, zdaję sobie z tego sprawę i nie byłem do końca przekonany do odpalania kilku timerów jednocześnie, ale przy niewielkiej liczbie elementów w ogóle się tego nie zauważa.
Jednak moje rozwiązanie było zbyt proste. :)
Dzięki za pomoc.
Riddle: Twój kod jest dobry, ale nie działa w przeglądarce IE. Co jest nie tak?
Przepraszam, działa pod IE, tylko w moim konkretnym przypadku nie działa.
Potrzebuję zastosować jeden z kodów z tego artykułu na wszystkich elementach li w pudełku o id #galeria. Oto jak zmodyfikowałem kod:
function animateNext(handle) {...} function delayAnimation(handle) {...} $('#galeria li') .hide() .eq(0).each(function() { $(this).fadeIn('500', function() { delayAnimation($(this)); }); });Jednak gryzie się to z jakimiś ustawieniami na stronie bo nie działa.
Podaję podstronę na której ma działać efekt, jeśli ktoś chciałby mi pomóc byłbym wdzięczny.
***
Okazuje się, że efekt działa, tylko elementy <li> mają z zasady display:block. Ja natomiast potrzebuję, żeby każdy element listy był obok siebie. Float:left też nie pomaga. Jak zmienię na display:inline, to znów nie działa .hide().
Być może powinienem zamiast listy wstawić same obrazki i zastosować taki kod:
$('#galeria img')
Tak, teraz działa.
Kolejną modyfikacją efektu jest aby po wyświetleniu pierwszych 4 zdjęć zaczęły one znikać z użyciem fadeOut, zaczynając od pierwszego. W ten sposób można by zobaczyć wszystkie 18 zdjęć. Coś w rodzaju slideshow.
Podaję podstronę na której ma działać efekt, jeśli ktoś chciałby mi pomóc byłbym wdzięczny.
http:www.b3.net.pl/fisiakstudio/tkanina.htm
Myślałem też, by może wyświetlić od razu wszystkie obrazki i potem co sekundę usuwać z użyciem
fadeOut()pierwszy obrazek. Zmodyfikowałem kod, ale nie działa. Jestem nowicjuszem w jquery i robię to trochę po omacku.Gdybym miał taki kod, wystarczyłoby dodać warunek, że po usunięciu wszystkich obrazków, czyli gdy
('#galeria img:eq(18)')użyćshow()i zacząć usuwać od nowa.do funkcji
delayanimation();wprowadziłem zmienną time, która ustala czas opóźnienia, dla wygody.Dodaj komentarz