chevron-left chevron-right

[JS] Wykrywanie wydajności urządzeń mobilnych za pomocą HTML5 i JS

Projektując nowoczesne interfejsy użytkownika strony internetowej, aż się prosi aby dodawać małe subtelne animacje elementów znajdujących się na stronie, aby zwiększyć przyjemność korzystania z interfejsu. Lecz takie podejście może mieć ogromny wpływ na płynność działania strony.

Jednym ze sposobów na ograniczenie liczby efektów może być wykrywanie wielkości ekranu, tym samym zakładając że urządzenie mobilne (w przypadku ekranu o małej rozdzielczości) nie będzie wystarczająco mocne aby uciągnąć wszystkie efekty. Takie podejście już teraz może być błędne, ze wzgędu na to, że urządzenia już teraz mogą mieć duże ekrany, nie posiadając wymaganej mocy obliczeniowej. W przyszłości może również dojść do sytuacji, że urządzenia mobilne będą dorównywały mocą laptopom i większość z nich będzie posiadało ekrany o dużej rozdzielczości, tj. powyżej 1024x768px.

Na szczęście istnieje inny sposób na wykrycie wydajności danego urządzenia. W tym celu wykorzystałem element <canvas/> oraz requestAnimationFrame API a do zebrania danych wykorzystałem Google Analytics API.

Mechanizm sprawdzenia wydajności

Przykładowy kod HTML, który stanowi bazę dla naszego mechanizmu sprawdzenia wydajności urządzenia mobilnego wygląda następująco:

1
<canvas id="board" width="300" height="300"></canvas>

Jest to element o wymiarach 300 pikseli na 300 pikseli. Następnie trzeba sprawdzić, czy requestAnimationFrame API jest dostępne w przeglądarce:

1
2
3
4
5
6
7
8
9
10
11
12
window.requestAnimFrame = (function () {
  return  window.requestAnimationFrame        ||
          window.webkitRequestAnimationFrame  ||
          window.mozRequestAnimationFrame     ||
          window.oRequestAnimationFrame       ||
          window.msRequestAnimationFrame      ||
          function (callback, element) {
            window.setTimeout(function () {
                callback(+new Date);
            }, 1000 / 60);
          };
})();

Z racji tego, że API w każdej przeglądarce może mieć inną nazwę (z dodanym prefiksem), to trzeba kod ujednolicić dla ułatwienia.

Mając już tak przygotowaną bazę, możemy przystąpić do pisania kodu odpowiedzialnego za wykonanie pomiaru i przesłanie wyniku za pomocą Google Analytics.

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(function (window, document, undefined) {
  var isAnimationRunning  = true;
  var isResultDisplayed   = false;
  var oldtime       = +new Date;
  var fps           = 0;
  var checkCount    = 0;
  var maxCheckCount = 100;
  var results  = [];
 
  var showScore = function (score) {
    var boardCanvas   = document.getElementById('board');
    var boardContext  = boardCanvas.getContext('2d');
    var boardWidth    = boardCanvas.width;
    var boardHeight   = boardCanvas.height;
 
    boardContext.clearRect(0, 0, boardWidth, boardHeight);
    boardContext.fillStyle = '#000000';
    boardContext.font      = 'normal 1rem Arial';
    boardContext.fillText(score + ' fps', 10, 26);
  };
 
  var checkLoop = function (time) {
    time = parseInt(time, 10);
 
    if (!isNaN(time)) {
      fps     = parseInt(1000 / (time - oldtime), 10);
      oldtime = time;
    }
 
    if (isAnimationRunning &amp;&amp; checkCount &lt; maxCheckCount) {
      checkCount++;
      results.push(fps);
      requestAnimFrame(checkLoop);
    } else {
      if (!isResultDisplayed) {
        var sum = results.reduce(function (a, b) {
          return a + b;
        }, 0);
        var score = sum / maxCheckCount;
 
        isResultDisplayed = true;
        ga('send', 'event', 'performance', 'result', navigator.userAgent, score);
        showScore(score);
      }
      requestAnimFrame(checkLoop);
    }
  }
 
  checkLoop();
 
})(this, this.document);

Zdefiniowałem dwie funkcje checkLoop() oraz showScore(). Pierwsza z nich uruchamia pętlę, która zbiera pomiary czasu do tablicy results. Po wykonaniu wymaganej liczby powtórzeń pomiaru - maxCheckCount, następuje sprawdzenie czy wynik został wyświetlony. Jeśli nie, to następuje wyliczenie średniej z zebranych wyników i wyświetlenie jej na stronie oraz wysłanie zdarzenia do Google Analytics z danymi dotyczącymi wyniku oraz rodzaju przeglądarki.

Eksperyment można wzbogacić za pomocą dodania dodatkowego elementu <canvas/>, która będzie wykonywała zaawansowaną animację, np. animację zegarka.

Wyniki pomiarów

Na podstawie zebranych wyników (51 niezależnych pomiarów - różnych urządzeń mobilnych) mogłem wywnioskować, że im nowsze urządzenie mobilne tym lepiej sobie radzi z obsługą animacji. Najgorszy wynik miały Blackberry Bold 9900 - 14,61fps (frame per second), Samsung i5510 - 14,32fps oraz Nokia Lumia 710 - 31fps.

Zauważyłem jeszcze jedną rzecz, że przeglądarki internetowe (nie ważne czy na urządzeniach mobilnych czy też na standardowym komputerze) mają górne ograniczenie liczby FPS i średnie wyniki oscylują w okolicach 60fps. Większość urządzeń jednak ma wydajność na poziomie między 50fps a 60fps.

Podsumowanie

Tego typu funkcjonalność można wykorzystać, aby sprawdzić czy dane urządzenie w danym momencie jest w stanie uruchomić zaawansowane animacje po stronie interfejsu. Jeśli na przykład użytkownik ma kilka programów uruchomionych w tle, np. gry i akurat chce przejrzeć wiadomości na stronie internetowej, to wtedy warto sprawdzić czy przeglądarka będzie w stanie płynnie wyświetlić zaawansowane animacje na naszej stronie. Żaden użytkownik nie będzie lubił gdy przewinięcie w dół lub przełączenie się między widokami danych będzie trwało długo lub będzie jeszcze bardziej spowalniało urządzenie.

Co myślisz o tego typu rozwiązaniu? Zapraszam do komentowania.

  • >Tego typu funkcjonalność można wykorzystać, aby sprawdzić czy dane urządzenie w danym momencie jest w stanie uruchomić zaawansowane animacje po stronie interfejsu.
    I dlatego mam wygenerować canvasa i puścić w nim animację, żeby ostatecznie dobić mobilniaka? 😉 To raczej właśnie nadaje się bardziej do zbierania danych statystycznych niźli do testowania ad hoc. Taki test *w najlepszym* razie trwałby nieco ponad 1.5 s przy 100 powtórzeniach (przy założeniu 60fps na sekundę), więc taki „freeze” raczej nie jest fajny.
    rAF natomiast doskonale sobie radzi przy technikach typu delta timing, więc tutaj można by było coś ugrać (np jeśli widać, że zamiast wysokiej liczby FPS dostajemy mało, to np zmniejszyć liczbę particles). Tak przynajmniej to widzę.
    Duże nadzieje pokładam w Web Performance API – być może ono spowoduje, że nie będziemy musieli używać takich hacków.

  • Nie zgadzam się ze stwierdzeniem, że to jest hack. Jest to dobra metoda do zbierania danych o wydajności urządzeń. Jeśli martwisz się tym, że dobijesz urządzenie mobilne, to zawsze możesz zmniejszyć ilość przeprowadzonych testów/zapętleń. Taki freeze, jeśli będzie trwał 1,5 sekundy przy założeniu że strona się ładuje dłużej niż 1,5 sekundy nie sprawi większego problemu (różnego rodzaju aplikacje potrafią się długo ładować).
    Co do Web Performance API, to na tą chwilę nie jest zbytnio rozbudowane i zwraca tylko HighResTimestamp i nic poza tym. Być może to kiedyś bardziej rozbudują, dzięki czemu będzie bardziej użyteczne.

  • Nie przeczę, że jest to dobra metoda. Czasami hacki są bardzo pozytywne, jak ten. Niemniej dla mnie to jest hack, bo canvas raczej nie zostało stworzone w celu zbierania danych o wydajności – w gruncie rzeczy tylko o to mi chodziło. Takie coś powinno robić właśnie Web Performance API, tyle że ono jest jeszcze w powijakach

  • Dokładnie. Teraz się mogę zgodzić 🙂

  • THE GUGS

    fajnywpis.

  • THE GUGS

    Mam pytanie. Jak napisać w arkuszu html, że używamy JS?

  • Zaskoczyło mnie to pytanie. Możemy wstawić do HTML tekst informujący o tym, że korzystamy z JS. Tak jak każdy inny element HTML. Następnie, za pomocą JS możemy ukryć ten tekst. Jeśli JS nie działa, to tekst będzie widoczny.

  • THE GUGS

    dziękuję

  • Mariusz

    Można gdzieś zobaczyć demo ?