Nowy sposób prezentacji postępu i użycia – Progress Ring

Podczas luźnych dyskusji o interfejsie użytkownika w firmowym panelu administracyjnym zapytano mnie.

Czy możliwe jest wycięcie danego fragmentu koła przy pomocy CSS?

Oczywiście jako członek zespołu programistycznego od razu rzuciłem w odpowiedzi żelazną zasadę naszego zespołu, czyli Nie da się!. Szybko jednak okazało się, że to możliwe, a narysowanie wykresu kołowego w CSS, wcale nie jest tak trudne jak myślałem w pierwszej chwili. Wszystko to kwestia odpowiedniego podejścia i użycia dostępnych środków, czyli właściwości clip z CSS 2.1, a także border-radius oraz transform z CSS3.

Omawiany wycinek koła był potrzebny do oznaczenia aktualnego użycia lub postępu, co ostatecznie sprowadziło się do prezentacji jednej wartości względem całości koła lub pierścienia jako 100% naszych zasobów. Podczas działań sportowych na rzecz jak najmniejszej ilości kodu, a także bardzo uniwersalnego rozwiązania planowałem wykorzystanie pseudo-elementów ::before i ::after, choć ostatecznie pozostałem przy warstwach. Pomijam wsparcie tej części CSS w niektórych przeglądarkach, ale prezentowany przykład jest bardziej eksperymentem niż docelowym rozwiązaniem do gotowego i szerokiego stosowania.

Download · Live Demo

Struktura kodu dla naszych przykładów jest bardzo prosta.

<div class="progress-usage"> </div> <div class="progress-indicator less"> </div> <strong>20</strong><span>%</span> </div>

Zaczynamy od stworzenia elementudiv o klasie .progress-ring, który będzie bazą dla każdego pierścienia. Wewnątrz tworzone jeszcze dwa elementy div do oznaczenia aktualnego stanu poprzez wybrany kolor, a mianowicie .progress-usage oraz .progress-indicator.

.progress-ring,
.progress-usage,
.progress-indicator {
    width: 150px;
    height: 150px;
    display: block;
    border: 25px solid #eee;
    border-radius: 100px;
}

Wszystkie trzy są takimi samymi elementami, ale ze względu na użycie obramowania, konieczne jest ich wzajemne pozycjonowanie, a pokrywały się.

.progress-ring {
    float: left;
    margin: 0 25px;
    text-align: center;
    font: normal 100% sans-serif;
    
    position: relative;
    border-color: #eee;
}
.progress-usage,
.progress-indicator {
    position: absolute;
    top: -25px;
    left: -25px;
}
.progress-usage {
    border-color: #0a0;
    clip: rect(0px, 200px, 200px, 100px);
}

Jeśli zechcesz w ramach eksperymentów lub dalszej optymalizacji możliwe jest zastąpienie ostatnich dwóch przez pseudo-elementy :before i :after. Prezentowanie wartości liczbowej zostało przekazane do dodatkowych elementów strong oraz span.

.progress-ring strong {
    font-size: 4em;
    color: #444;
    line-height: 150px;
}
.progress-ring > span {
    display: block;
    width: 150px;
    margin: 0 auto;
    
    font-size: 2em;
    font-weight: bold;
    color: #aaa;
    
    position: absolute;
    top: 100px;
}

Elementy te akurat mają najmniejsze znaczenie do zrozumienia zasady działania całego mechanizmu.

Zasada działania

Główny element .progress-ring stanowi pierścień o szarym tle, który w zależności od wartości wypełnia się danym kolorem. Wypełnienie pewnej części kolorem jest czynnością dwuetapową. Mianowicie podstawą wszystkiego jest odpowiednie wycięcie z określonego obszaru połowy jego zawartości, i przykrycie go kolejnym elementem, dodatkowo obróconym.

Zostały stworzone dwie dodatkowe klasy .less i .more, które przeznaczone są do generowania połowy pierścienia w odpowiednim kształcie i kolorze.

.less {
    clip: rect(0, 200px, 200px, 100px);
    border-color: #eee;
}
.more {
    clip: rect(0, 100px, 200px, 0);
    border-color: #0a0;
}

Zakres 0-50%

Ponieważ z warstwy .progress-usage wycinamy tylko prawy obszar to od razu otrzymujemy zaznaczenie wielkości 50%. Jest to doskonale widoczne poprzez minimalny obrys pierścienia dla wartości mniejszych niż połowa. Element .progress-indicator, z dodatkową klasą .less jest odpowiednikiem .progress-usage, ale w kolorze szarym. Uzyskanie każdej wartości z zakresu 0-50% wiąże się z przykrywaniem pierścienia przez ten element i dadatkowym obrotem zgodnie z wskazówkami zegara. Wartość obrotu łatwo obliczymy przy założeniu, że jeden procent to 3.6 stopnia.

Przykładowo dla 20% wartość obrotu wynosi transform: rotate(72deg);.

Zakres 50-100%

Zaznaczenia powyżej 50% generowane są podobnie, ale element .progress-indicator ma wówczas klasę .more, która wycina sąsiednią połowę obszaru, a także zmienia kolor obramowania na aktywny, w naszym przekładnie zielony. Dalsze wypełnianie pierścienia bazowego jest analogiczne i wiąże się z obrotem elementu .progress-indicator od 180 do 360 stopni.

Z kolei dla 60% zaznaczenia wartość obrotu wynosi odpowiednia transform: rotate(216deg);.

Zaskakujące jest, że Google Chrome (przynajmniej w wersji do 25) nie obsługuje prawidłowo obrotu elementu, choć przy użyciu jQuery w wersji animowanej wszystko działa jak powinno. Prosty sposób naprawy tego stanu jest użycie określenie właściwości z prefiksem przeglądarki, czyli -webkit-transform.

Podsumowanie

Po raz kolejny przekonałem się, że nie ma rzeczy niemożliwych, a jedynie potrzebujesz odrobiny czasy i zaangażowania do znalezienia odpowiedniego rozwiązania. Idąc dalej, tym tropem dochodzę do wniosku, że HTML5 oraz CSS3 będą w najbliższych latach stanowiły prawdziwe narzędzia, umożliwiające uzyskanie efektów zarezerwowanych wcześniej choćby dla Flash czy SVG.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *