Designerskie wykresy wektorowe w d3.js

Niedawno miałem przyjemność wystąpić na FireTalku zorganizowanym przez Sharpeo. Ten wpis jest rozwinięciem mojej 15-sto minutowej prelekcji.

Dowiesz się, co sprawiło, że nasz zespół potrafi szybko tworzyć takie wykresy

Tło do opowieści

Jakiś czas temu miałem dylemat, jaką formę pracy najbardziej lubię, więc wpierw zacząłem od startupu — miłego i lubianego — GoldenLine. Następnie zmieniłem wszystko o 90% i trafiłem do jednego z największych banków inwestycyjnych — Goldman Sachs. Tam spędziłem długie 1,5 roku i wreszcie znalazłem coś dla siebie — XSolve — tu pracuję od lutego 2017.

Część z was zastanawia się pewnie po co ten wstęp — otóż są dwa powody:

  1. Nie każdy wie, jak działają firmy typu software house, a taką jest XSolve
  2. Nigdy nie pisałem na Medium, a tezy będą mocne, chciałem żebyście wiedzieli, kto w tym wpisie będzie się mądrzył

Trafiłem do XSolve z zamiarem pracy w React.js. Tak się akurat złożyło że wolnego projektu w danym momencie nie mieli, zatem na okres przejściowy dostałem projekt w JavaScript dla klienta zagranicznego.
Cel? Ładne wykresy danych działające pod wieloma przeglądarkami.


2 wyzwania na start

Sketch — mówi Wam to coś? To taka apka, dzięki której grafik dostarcza design, który łatwo przenosić do CSS i JS. Można wybierać poszczególne elementy i dowiedzieć się o ich atrybutach (szerokość, wysokość itp), a sama apka potrafi nawet generować prosty CSS. Niestety, ten CSS w SVG zwykle Wam dużo nie pomoże.

Edytowaliście kiedyś kod SVG? Prawdopodobnie nie znacie potrzebnych znaczników i sposobów, w jaki je stylować. Całe szczęście mamy do tego bibliotekę D3.js.

Kod SVG pisany ręcznie

<svg>
<rect id=”kwadrat” x=”10" y=”10"
style=”width: 180px; height: 120px; fill: #000; stroke: #ff8e36; stroke-width: 3px”>
</rect>
</svg>
Wizualny efekt powyższego kodu

Biblioteka to tylko początek— d3.js oraz d3plus.js

Trafiłem do projektu, w którym już wybrano bibliotekę D3.js oraz jego gotowe wykresy w D3plus.js

Przykładowe gotowe rozwiązania z biblioteki D3plus

Wybór D3plusa był konieczny, aby utrzymać przewidziane w 2 tygodniowych sprintach tempo pracy. Za darmo dostajemy wiele trudnych do implementacji (w SVG) funkcjonalności.

Teraz zerknijcie na kod źródłowy D3plusa
https://github.com/alexandersimoes/d3plus

Pierwsza rzecz, która powinna wzbudzić Wasze obawy to trudno rozszerzalne wykresy. Niestety, biblioteka ciężko radzi sobie ze wspieraniem:
- stylowania tooltipa / customowego tooltipa
- języka arabskiego
- stylów na IE11
- ustalonej ilości liczb na osiach;

Z uwagi na wymagającego klienta musieliśmy wszystkie te wady szybko poprawić, opakowując wykresy D3plusa w klasy. Poniżej przedstawiam dwa case study z procesu tworzenia wykresów.


Case study #1: Wykres obszarów (TreeMap chart)

Wygląd wykresu w implementacji D3plus
Efekt naszej pracy

Jeśli wykresy wydają się Wam łudząco podobne, to pewnie nie zdajecie sobie sprawy z tego, ile trzeba było wykonać zmian w stosunku do tego co oferuje biblioteka.

Zmiana kolorków jest prosta.
Umieszczenie tekstu nie.

Zatem po kolei:

  1. Zmiana koloru tła obszarów (proste)
  2. Tekst arabski w obszarach (trudne, tekst Right-To-Left jest wspierany, ale język arabski chyba nie był testowany).
  3. Wiersz z liczbą (trudne — ma się wyświetlać w zależności od rozmiaru — tj. dostępności przestrzeni).
  4. Tekst, który się nie mieści, ma znikać (trudne w połączeniu z 2. — należy liczyć znak po znaku zajętość obszaru zarówno na szerokość jak i wysokość).
  5. Ma działać pod IE11 (które tak dobrze z arabskim sobie nie radzi, kilka ifów będziecie musieli dodać).

Teraz spójrzmy jak można usprawnić funkcjonalność:

Wykres połączony z kontrolkami
  1. Trzy przyciski na górze — zmiana typu danych. Po zmianie wykres powinien przeładować się z animacją fade out + fade in. 
    (łatwe, znikamy wykres, przebudowujemy w JS i wrzucamy ponownie)
  2. Obszary powinny być klikalne, np. wyobraźmy sobie że to co widzicie to dane dla państw a po kliknięciu wykres się odświeży i pokaże dane dla poszczególnych województw w klikniętym państwie.
    (też łatwe, implementacja jak w 1.)

Przejdźmy do następnego i bardzo znanego typu wykresu — liniowego.


Case study #2: Wykres liniowy (Line chart)

Implementacja D3plusa
Nasza rozszerzona implementacja wykresu liniowego

Ponownie, na pierwszy rzut oka wydaje się proste do wykonania.

Co będzie trzeba zrobić?

  1. Usunąć górną linię wykresu.
  2. Dodać oś Y2.
  3. Przesunąć “ticksy” — wartości na osiach Y i Y2.
  4. Usunąć “kratkę” w tle wykresu.
  5. Zadbać o ilość wartości na osiach (tu: 5 na osiach Y).
  6. Zadbać o łatwość czytania wykresu — wartości na osiach mają być możliwie najpełniejsze.
    (tj. 100,200,300 jest ok, a 122, 244, 366 już nie)
  7. Aproksymować wartości dla lat dla których nie mamy danych oraz nie wyświetlanie tej wartości po najechaniu na linię.

To dopiero początek, czas na kilka linii.

Nasz wykres liniowy dla wielu danych

Co tym razem?

  1. Dodatkowe elementy komunikujące się z wykresem. (opis pomijam bo to zwykły css+html — np. legenda czy selektor “danych”)
  2. Tooltip — niestandardowy i do tego z legendą (zajmuje dużą część wykresu) ma pojawiać się po najechaniu myszką na linie
  3. Dodajemy możliwość wyłączenia osi Y2
  4. Kropki na liniach wykresu — nie wspierane przez D3plusa, wymagające również wyłączenia domyślnego zachowania.

Przejdźmy do opisania problemu z perspektywy bardziej programistycznej.


Jak to widzi programista

  1. Mam klasę wykresu z D3plusa. Ta klasa wystawia swoje metody publiczne i prywatne.
  2. Muszę rozszerzyć klasę i nadać jej nowe funkcjonalności.
  3. Pewne nieprzemyślane rozwiązania w D3plus to godziny implementacji funkcjonalności od nowa, np. wsparcie języka arabskiego lub pokazywanie kropki na linii.
  4. Niektóre zmiany w stylach bazujące na danych nie są możliwe bez dostępu do danych “prywatnych” biblioteki.
  5. Muszę sprawdzić czy działa na Chrome, Safari i Internet Explorerze. Testerzy sprawdzą na wszystkich innych przeglądarkach wraz z mobilnymi. W skrócie na początku pracy w projekcie muszę to testować na kilku wirtualnych maszynach.

Lista mogłaby być dłuższa, ale tyle wystarczy, by było ciekawie.


Solucja jest na widelcu

Jak się domyślacie, żeby zaimplementować wszystkie powyższe wymagania z Case Study 1 & 2 trzeba było wykonać naprawdę dużo zmian w podstawowej funkcjonalności biblioteki.
Czy zatem nie lepiej było od początku napisać to samemu?

Moim zdaniem, nie. W jednym ze sprintów projektu napisałem jeden z wykresów kołowych bez D3plusa i wtedy zrozumiałem, jak wiele edge-caseów ta bibloteka już sprawdziła i przetestowała.

Czy niema prostszego rozwiązania? Może rozszerzenie biblioteki?
Długo długo opieraliśmy się przed tym krokiem z następujących powodów:

  1. Jeśli zmieniamy bibliotekę, to prawdopodobnie odetniemy się od możliwości szybkiego i prostego uaktualniania (bugfixing itp).
  2. Nie będziemy mogli korzystać z implementacji nowych wykresów które zostaną dodane
  3. Wszystko będziemy musieli testować sami — a D3plus nie bardzo przyłożył się do unit testów. ;)
  4. D3plus jest zaimplementowany w CoffeeScript — my go nie znamy.
    Wymaga to zmian w Webpacku i konfiguracji budowania.

Tylko przecież my i tak poświęcamy mnóstwo czasu na tę bibliotekę i poprawiamy jej funkcjonalność… testujemy bardzo skrupulatnie zarówno manualnie jak i automatycznie…


Zróbmy forka D3plusa

To co wydawało się nieco absurdalne przez pierwszy miesiąc okazało się najlepszym wyborem. Dlaczego?

  1. Łatwy dostęp do stałych które kontrolujemy (stałe D3plus).
  2. Łatwy sposób na tworzenie nietypowych zdarzeń na wykresie.
  3. Łatwe zmiany styli domyślnych, często bez css.
  4. Łatwiejsza implementacja dynamicznych zdarzeń na wykresie.
  5. Usprawnienie wsparcia arabskiego na wszystkich wykresach.
  6. Lepsza integracja z komponentami zewnętrznymi.

Teraz pokażę Wam jakie inne wykresy zbudowaliśmy.


Krótszy czas developmentu = więcej wykresów

Mapa D3plus
Jak my zmodyfikowaliśmy mapę
Np usuwając część mapy świata i łącząc dane z innymi wykresami
4 wykresy liniowe połączone ze sobą

Dzięki za przeczytanie!

To był mój pierwszy wpis na medium i jeden z niewielu który jest mało techniczny. Mam nadzieję, że pomoże Wam w estymowaniu pracy nad wykresami, przekona do wykonania forka oraz zwróci uwagę na możliwe problemy podczas tworzenia wykresów. :-)

PS: >> Link do prezki z prelekcji <<
PPS: Pozdrawiam cały zespół i dzięki za świetny czas razem w projekcie!
PPPS: Dzięki Michał