Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Bardzo duza ilosc obiektow DOM
Forum PHP.pl > Forum > Po stronie przeglądarki
lukaskolista
Witam. Do DOM laduje 60 000 obiektow (<div>). Na chromie nie robi to wrazenia i dziala jakby nigdy nic (czyli dobrze), natomiast FF i IE strasznie spowalniaja. Jest jakis sposob, aby jakos przyspieszyc FF i IE? Divy laduje w petli w JS.
Crozin
Możesz konkretniej opisać swój przypadek? Bo na takie spowolnienie poza sporą ilością obiektów w drzewie będą miały takie czynniki jak:
1. Ich zagnieżdżenie.
2. Dodane style CSS.
3. Rozłożenie na stronie.
4. Pewnie cała masa innych.

Powinieneś mimo wszystko postarać się zmniejszyć tą ilość. Może masz możliwość usuwania niepotrzebnych elementów bądź wczytywania wyłącznie potrzebnych?
lukaskolista
Problem w tym, ze kazdy element jest potrzebny. Wszystkie sa w tym samym kontenerze. Style nie maja znaczenia, sprawdzilem.
kamil4u
Opisz dokładniej jak to robisz i jakie są to dane. Możliwe, że będzie się dało chociaż trochę przyspieszyć. Podaj kod lub jego fragmenty.
lukaskolista
  1. <script type="text/javascript">
  2. $('#objects').html('');
  3. for (var i in this.objects) {
  4. var map_object = this.objects[i];
  5.  
  6. var left = ((map_object.y * 0.5) + (map_object.x * 0.5) - 1) * 30;
  7. var bottom = 325 - ((map_object.x * 0.5) * 18) + ((map_object.y * 0.5) * 18) - (map_object.type.width * 0.5 * 18);
  8.  
  9. map_object.x = parseInt(map_object.x);
  10. map_object.y = parseInt(map_object.y);
  11.  
  12. //var z_index = (250 * 250) - ((map_object.y * 250) - (map_object.x * 2));
  13. var z_index = (map_object.x) + ((250 - map_object.y) * 250);
  14. var html = '<div id="object_'+map_object.id+'" class="object" style="..."></div>
  15. $('#objects').append(html);
  16. }
yevaud
taki problem zwykle zalatwia sie clusterami markerow. Pomysl polega na tym ze 60k elementow i tak nie wyglada spechalnie atrakcyjnie, wiec dokladniejsze dane user dostaje gdy zblizy/ustawi konkretny kawalek mapy
http://www.svennerberg.com/2009/01/handlin...in-google-maps/
lukaskolista
to jest mapa gry, wszystkie elementy musza byc widoczne. Mapa ma wymiary 250x250, gdzie szerokosc kratki to 17px. Dziwi mnie to, ze w chromie te +60 000 obiektow nie robi roznicy w wydajnosci, a w innych przegladarkach praktycznie uniemozliwia poruszanie sie po mapie.
kamil4u
Jedyne co mi przychodzi na myśl, aby przyspieszyć działanie to:
- przypisz div-a o id #objects(referencję do tego obiektu) do zmiennej, żebyś nie musiał tego wyszukiwać w dokumencie za każdym razem
- pozbądź się jQuery na rzecz "czystego JS" - tylko w tym fragmencie. Zawsze przy jQuery wykonuje się wiele zbędnych operacji.
- masz dość dużo operacji matematycznych - spróbuj je jakoś uprościć

Generalnie to wszystko co przychodzi mi do głowy, a te pomysły i tak pewnie gwałtownie prędkości nie podniosą smile.gif

Nie wiem jak to zadziała, ale może warto spróbować ukryć(diplay: none;) na czas tworzenia elementów div-a o id #objects i po jakimś czasie cyklicznie pokazywać część z nowo tworzono div-ów. Bardzo możliwe, że takie coś zadziała.

Może pomyśl o canvas. Nie napisałeś do czego ma Ci to służyć, ale taka ilość elementów może służyć pewnie Ci do gry lub czegoś takiego. Pomyśl nad tym.
Crozin
@kamil4u:
1. Chyba wszystkie silniki przeglądarek traktują pobieranie po ID wyjątkowo, tzn. referencje do obiektów posiadających ID są od razu składowane na specjalnym stosie, właśnie w celu błyskawicznego dostępu.
2. jQuery również umożliwia błyskawiczny dostęp gdy korzystamy bezpośrednio z ID. Nie mniej jednak rzeczywiście każdorazowe odwoływanie się do $("#objects") może delikatnie spowalniać. jQuery.html() to już nie problem.
3. Te operacje matematyczne to akurat najmniejszy problem.

Tak jak już @yevaud napisał wyświetlaj wyłącznie te elementy, które znajdują się w polu widoczności i powiedzmy 2 - 3 pozycje poza nim. Nie ma potrzeby byś wyświetlał całą mapkę +60k elementów w momencie gdy widocznych jest pewnie około 2500.
kamil4u
Cytat
jQuery.html() to już nie problem

Jesteś pewien? Oni chyba muszą prasować ten ciąg, a to swoje zabiera smile.gif - co prawda nie zapoznawałem się z żadnymi testami wydajności jQuery, ale myślę, że przy 60 000 elementów to prasowanie może trwać już dość długo. Ale powtarzam - to jedynie domysły i jak znasz jakieś dokładniejsze informacje na ten temat to z chęcią się dowiem czegoś nowego(tylko proszę nie odsyłaj mnie do źródeł jQuery ;] ).
lukaskolista
Problem nie lezy w tworzeniu mapy, bo to zajmuje 3 sekundy i wiem jak to zoptymalizowac. Problem polega na tym, ze juz po zaladowaniu mapy przegladarka (konkretnie akrta w ktorej otwarta jest gra) dziala bardzo wolno. Samo ladowanie mapy jest ok
yevaud
nie do konca rozumiem jak miescisz 60k obiektow na mapie 250x250, gdy kratka ma 17px

250/17 = 14 obiektow na szerokosc i wysokosc, czyli jakies 200 obiektow na prostokacie 250x250
Crozin
@lukaskolista: Samo przechowywanie w pamięci informacji o 60 000 elementów nie jest problemem. Problemem jest wyświetlanie tych elementów. Zwróć uwagę, że ponad 90% elementów przeglądarka musi niepotrzebnie przetworzyć w procesie renderowania strony, a być może nawet i częściowo wyrenderować i to właśnie w tym jest problem. A po co zmuszać przeglądarkę do przetwarzania danych z których się nie korzysta?

Jak rozumiem masz mapkę 250 x 250 z czego zaledwie fragment 15 x 15 jest widoczny, tak? W takim razie zamiast wyświetlać wszystko (tylko po to by ponad 90% zaraz ukryć) wyświetl mapkę o rozmiarach 19 x 19 pozostawiając sobie po 2 elementy ukryte z każdej strony - dzięki temu uniknie się efektu "dorenderowywania" mapki w trakcie poruszania się. Następnie w momencie gdy poruszasz się o jeden krok w lewo modyfikujesz wyświetlane elementy, tzn. usuwasz całą prawą, skrają kolumnę 19 pól i dodajesz nową kolumnę z lewej strony.

Dzięki temu, że masz pozostawiony margines w postaci dwóch kolumn / rzędów pól poza wyświetlanym obszarem unikniesz jakiś migotań itp. spowodowanych koniecznością "dorenderowania" fragmentu mapy. A zmniejszając ilość przetwarzanych elementów z 60 000 do 360 powinieneś pozbyć się problemów obciążenia.
lukaskolista
mapa nie ma szerokosci 250 pixeli tylko 250 kratek, 250*250=62500

tutaj zdjecie mapy, czarne pola to pola zajete przez jakies obiekty, jest ich 1225 i FF i IE juz przy tej ilosci leza. Faktycznie nie ma sensu ladowac od razu 60k, ale i tak dla 1225 przegladarki (poza chromem) maja problem


EDIT:
problem rozwiazany, uzylem elementu <canvas> i smiga pieknie wszedzie poza IE
#luq
Tutaj chodzi o to, że najbardziej spowalnia dodawania do drzewka DOM kolejnych elementów. Tego się tak po prostu nie robi przy dużej ilości elementów. W takim wypadku używa się tempa w postaci documentFragment. Samo jQuery nie ma wsparcia dla tworzenia documentFragment i trzeba to pisać w czystym JSie.

Zobacz to http://wiki.forum.nokia.com/index.php/Java..._Best_Practices i zerknij na to co jest napisane pod
Cytat
Use createDocumentFragment()


Poza tym tak jak wyżej mówią. Odwołanie w pętli do
Kod
$('#objects')

także spowalnia. W tym wypadku zawsze mielone jest całe drzewko DOM i pobierany jest na nowo ten węzeł.

Ogólnie operacje na DOMie są jednymi z najbardziej obciążających.
zegarek84
pokazałeś fragment kodu tworzenia mapki - na samym początku rzuciło mi się w oczy "zerowanie" zawartości warstwy przez $('#objects').html(''); co jednoznacznie sugeruje, że zapewne także przy przesuwaniu mapki raczej tez ją tworzysz od nowa lub próbujesz przesuwać wszystkie elementy...

gdy tworzysz mapkę mógłbyś zrezygnować z innerHTML i utworzyć ją w elemencie jeszcze nie dodanym do drzewa DOM (to też zamiast documentFragment) - potem dołączyć tylko raz tą całą warstwę... przesuwać mapkę mógłbyś przesuwając nie wszystkie elementy osobno a przesuwając warstwę zawierającą te elementy - co w efekcie skutkuje przesunięciem wszystkich elementów... do kluczowych obiektów mógłbyś trzymać referencje czy to w zmiennych czy w tablicy coby nie szukać ich po drzewie DOM...

samo kliknięcie w dany obiekt lub najechanie to... podpięcie nasłuchu zdarzeń pod tak wiele elementów jest bardzo mało optymalne -> rozwiązanie - podepnij nasłuch zdarzeń pod warstwę nadrzędną i sprawdzaj w górę co zostało kliknięte - kliknięty obiekt masz w evencie pod .target - skoro korzystasz z jQuery jest to "znormalizowane" i pod tą zmienną bedziesz miał także na IE... mniej więcej to chodzi mi np. o coś podobnego co jest opisane na tej stronie:
Speed up javascript event handling with event delegation and bubbling
lukaskolista
moglbys podac funkcje pomocne przy Twoim rozwiazaniu i schemat, jakby to mialo wygladac? To by mi bardzo pomoglo w napisaniu tego
zegarek84
schemat tworzenia mapki jest bardzo podobny do Twojego z tym, że w pierwszej kolejności tworzysz warstwę i jej nie dodajesz po czym w petli dodajesz do niej kolejne elementy ustawiając odpowiednie atrybuty i pozycjonowanie zapewne absolutne względem narożnika wspomnianej warstwy (przy okazji warstwa nadrzędna także musi mieć jakiekolwiek pozycjonowanie być zawartość wewnątrz niej mógł pozycjonować względem narożnika)... czyli w stylu:
var nDiv = document.createElement('div');
// ustawiasz atrybuty i nie dodajesz jeszcze do drzewa DOM
var nTemp; // kolejne elementy drzewa dom
// jakaś pętla
for(var i=0;....;++i){
nTemp = document.createElement('div');
// ustawiasz mu co trzeba czy to przez właściwości obiektu szy przez .setAttribute()
nDiv.appendChild(nTemp);
}
// po wszystkich operacjach dodajesz całą mapę która jest w jednej warstwie - i w tej warstwie nie musiały być wszystkie obiekty - możesz je dodać później gdy będziesz miał przesuwać mapkę

warstwę z mapką masz zawartą w innej warstwie pozycjonowanej relatywnie... teraz całą mapkę zawierającą wszystkie elementy możesz przesuwać przesuwając tego nDiv w pozycjonowaniu absolutnym...

suma sumarum to i mapkę mógłbyś tworzyć podobnie jak tworzyłeś jednak by to zoptymalizować pasuje byś ten kod html'a wpierw zsumował i dopiero na sam koniec przez ten innerHTML dodał wszystkie elementy ale też do warstwy mapy którą cała byś przesuwał względem jakiegoś zewnętrznego kontenera jak już to opisałem...

coby zapomnieć o pewnych zmiennych i referencjach np. do współrzędnych jaki obiekt drzewa został kliknięty i nie tworząc aż tylu listenerów tylko jeden na wzór tego co jest na stronie do której podałem linka to jakiś atrybut lub id'ka po prostu ustaw z odpowiednią nazwą byś tą nazwę mógł z klikniętego elementu sparsować i znać współrzędne kliknięte na obiekcie oraz inne potrzebne informacje...

jeszcze co do tego listenera ogólnego coby wiedzieć kiedy się zatrzymać szukając przez parentNode w górę gdy masz zagnieżdżone obiekty to możesz ustawić jakąś klasę lub ustalić, że zatrzymujesz się gdy obiekt ma ustawiony jakiś id i wtedy z nim robisz co tam chcesz...
lukaskolista
nie chodzi mi o ladowanie mapy, ale o to, jak przegladarka dziala juz po zaladowaniu. Uzywajac sposobu opisanego powyzej "na oko" jest o 1/3 szybciej, ale to wciaz malo sad.gif

Edit:
div zmienilem na img i jest duzo szybciej
zegarek84
to jaką strukturę mapki bedziesz mial zalezy głównie od Ciebie - mi dokładniej chodziło o to, żeby juz zaaldowanych elementów do drzewa DOM niepotrzebnie nie ruszać - aby przesunąć całą mapkę wykonując jak najmniej zmian na elementach mapki wystarczy dosłownie przesunąć w odpowiednią stronę warstwę zawierającą te elementy czyli całą mapkę... jeśli chcesz pokazać elementy z prawej strony mapkę przesuwasz w lewo... - cała ta mapka jednak musi być w kolejnym elemencie blokowym z pozycjonowaniem relatywnym i overflow: hidden coby nie było widać wystającej części mapki... czyli coś jak masz tutaj jeśli niebieski kwadracik miałby overflow: hidden to w przypadku gdy zielony jest na zewnątrz nie byłby widoczny - czyli nadmiar mapki też nie byłby widoczny...

dodatkowo w sumie nie musisz ładować całej mapki a mógłbyś tak jak wspomnieli załadować tylko część widoczną z lekkim marginesem - jesli będzie potrzeba doczytać resztę elementów ale przez DOM coby nie skasować już istniejących tam elementów - oczywiście na czas doczytywania elementów do warstwy z mapką daj jej display:none - starsze przeglądarki wywołują renderowanie przy każdej zmianie drzewa DOM nawet tylko atrybutu - dając display:none wyrenderujesz ukrycie warstwy, zmieniasz zawartość warstwy i nic się nie dzieje - pokazujesz całą warstwę i wtedy jest renderowany widok drzewa DOM... przy tej wersji jeśli nie będziesz wczytywał odrazu wszystkich elementów ale po trochu to w końcu i tak wczytasz calą mapkę jeśli ktoś będzie ją przewijał...

EDIT
i jeszcze jedno - jeśli elementów jest wiele powtarzalnych to możesz utworzyć wzorce pierwotne po czym zamiast tworzyć od zera nowy element możesz go sklonować przez .cloneNode(true) - true jeśli element zaweira inne elementy a false jeśli jest to pojedyńczy element (lub w jQuery .clone) i dopiero klonowi ustawić atrybuty - jest to szybsze niż document.createElement('...');
lukaskolista
Zrobilem jak napisales i przyspieszylo, jednak dalej sie zacina. Testuje teraz dla 1000 obiektow czyli dla tylu, ile maksymalnie zmiesci sie na ekranie (dla takich, ktore maja 1 jednostke szerokosci i dlugosci).
Kartofelek
Hah. Do takich rzeczy bardzo fajny jest GameMaker. Niby program zabawka, a bardzo fajnie uczy jak podchodzić do takich spraw by działało sensownie smile.gif
Osobiście pewnie bym wykorzystał canvasa jeżeli render iluś tam divów jest ułomny.
Druga sprawa już była poruszona - zależy do od tego jak to potem jest obsługiwane. Wiadomo przecież że na raz wszystkiego się nie wyświetla więc i trzymanie tego w pamięci nie ma sensu. Ale to już było powiedziane.
Jeżeli się tnie, to trzeba iść na kompromis.
Zwłaszcza że ciężko przewidzieć jaki user będzie miał komputer i przeglądarkę smile.gif

1) ładowanie części - możliwe że wykorzystanie 2 tablicy (ala taki 2 buffor)
2) e.tartget zamiast listenera dla każdego elementu
3) zapoznanie się z lekturą ebooka: Oreilly.High.Performance.JavaScript.Mar.2010
4) może to się przyda, chociaż nie koniecznie: http://www.canvasdemos.com/2010/12/15/5-pe...ur-html5-games/
lukaskolista
Na obszarze mapy, ktory widzi uzytkownik miesci sie troche ponad 1000 elementow i tylko te elementy laduje, ale dla 1000 i tak jest wolno. Nie laduje wszystkich 60k

Edit:
Potestowalem i wyszlo na to, ze ilosc obiektow na mapie nie ma znaczenia, znaczenie ma natomiast ilosc elementow widocznych. Dla 1000 obiektow (100 widocznych, 900 gdzies tam dalej schowanych, ale POBRANYCH I WSTAWIONYCH DO DOM) jest duzo szybciej, niz dla 1000 tez pobranych i wstawionych do DOM, ale na obszarze widocznym dla gracza).

Podsumowujac:ilosc elementow nie ma jednak duzego znaczenia, znaczenie ma ilosc elementow widocznych dla gracza.

Dodam, ze kazdy element na mapie to osobny <img>
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.