Let’s CSS #3 – napełnij szklankę procentami

Można go rozciągnąć poziomo, albo postawić pionowo. Można również połączyć jego końce i zwinąć w okrąg lub wyostrzyć krawędzie i złamać boki w wielokąt. Wreszcie, można także animować jego treść na rozmaite sposoby. Twój pasek postępu nie musi być nudny! W przykładzie, który opiszę stworzymy prosty obrazek szklanki używając CSSa oraz przy drobnej pomocy SVG i JavaScriptu sprawimy, że nasza szklanka wypełni się po brzegi. Do dzieła!

Struktura i główne elementy

Zacznijmy od stworzenia struktury w index.html.

Najważniejsza część naszego kodu będzie wewnątrz diva, który za chwilę zamieni się w szklankę. W nim umieszczamy kolejne elementy, stanowiące o wyglądzie naszego paska postępu. Nie zapomnijmy jeszcze dodać progress barowi tego, co progress barowe – czyli atrybutów określających jego rolę – role=progressbar a także aria-valuenow, aria-valuemin i aria-valuemax, które wyznaczają punkty graniczne oraz początkowe położenie paska postępu.

Dalej umieszczamy jeszcze dwa divy, w których będziemy przetrzymywali procentowy postęp naszego progress baru, oraz samą treść paska postępu. Już niedługo naszą szklankę będzie wypełniała woda. 🙂

<div class="progressbar" role="progressbar" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100">
          <div class="percent">
              <div class="percentNum" id="count">0</div>
              <div class="percentB">%</div>
          </div>
          <div id="water" class="water"> </div>
</div>

 

Podstawowe style

Przejdźmy teraz do napisania podstawowych styli. Progress barowi przypisujemy właściwości odpowiedzialne za wymiary i pozycjonowanie. Musimy też pamiętać żeby dodać mu takie samo tło jakie posiada element, na którym wyświetlana jest nasza szklanka. W tym przykładzie rodzicem dla progress baru jest element body.

Wszystkie elementy będą miały position:absolute, więc istotne będzie wykorzystanie właściwości z-index, która zapewni nam widoczność kolejnych elementów – im większa liczba dodatnia przypisana do z-index, tym bardziej „na wierzchu” będzie dany element.

Dodajmy także elementy ::after i ::before dla klasy progressbar, które sprawią, że nasza szklanka nabierze obłych kształtów.


body {
  background-color: #020438;
  font-size: 14px;
  font-family: 'Open Sans', Helvetica, sans-serif;
}
.progressbar {
  height: 260px;
  width: 120px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 2px solid #fff;
  border-top: none;
  background: #020438;
  border-radius: 5px;
}
.progressbar::after {
  content: '';
  position: absolute;
  width: 120px;
  height: 5px;
  border-radius: 50%;
  border: 2px solid #fff;
  bottom: -1px;
  left: 0;
  z-index: 3;
}

.progressbar::before {
  content: '';
  position: absolute;
  width: 120px;
  height: 5px;
  border-radius: 50%;
  border: 2px solid #fff;
  top: 0;
  left: 0;
  z-index: 3;
}
.percent {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 30px;
  color: #fff;
  font-size: 30px;
}

.water {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  transform: translate(0, 100%);
  background: #4D6DE3;
}

Jak na razie wynik naszego kodu jest następujący:

See the Pen glass1 by Greg (@GregSzczsnk) on CodePen.

Ustawiliśmy wodę poza szklanką przy pomocy właściwości transform, ponieważ chcemy by nie było jej tam od razu, tylko żeby pojawiała się stopniowo. Efekt ten uzyskamy kilkoma linijkami kodu w JavaScript.

Najpierw jednak sprawmy by nasza szklanka zaczęła rzeczywiście ją przypominać oraz ustawmy klasie progressbar właściwość overflow:hidden, która sprawi, że elementy poza szklanką nie będą wyświetlane. W ten sposób woda będzie widoczna tylko wtedy, gdy będzie w obrębie szklanki.

Szklanka nabiera kształtu

W index.html, wewnątrz diva o klasie progressbar dodajmy dwa elementy span, które wykroją nam z prostokątnego kształtu, coś bardziej pomysłowego. Tutaj także musimy pamiętać o ustawieniu odpowiedniej wartości z-index, która przykryje linie obramowania szklanki.


.glass-border-left {
  position: absolute;
  width: 30px;
  height: 265px;
  background-color: #020438;
  top: 0;
  left: 113px;
  border-left: 2px solid #fff;
  transform: rotate(4deg);
  z-index: 5;
}

.glass-border-right {
  position: absolute;
  width: 30px;
  height: 265px;
  background-color: #020438;
  top: 0;
  right: 113px;
  border-right: 2px solid #fff;
  transform: rotate(-4deg);
  z-index: 5;
}

Efekt? Obramowanie dla progress bara jest wciąż widoczne! Właściwość overflow nie pozwala na przysłonięcie obramowania przez inny element. Usuwamy zatem właściwość border klasie progressbar i przypisujemy ją elementowi o klasie percent, który przyjmuje dokładnie te same wymiary, co progressbar, ale nie posiada właściwości overflow.


.percent {
…
  border-right: 2px solid #fff;
  border-left: 2px solid #fff;
}

See the Pen glass2 by Greg (@GregSzczsnk) on CodePen.

See the Pen glass3 by Greg (@GregSzczsnk) on CodePen.

Napełnij szklankę!

Przejdźmy teraz na chwilę do kodu JavaScript. Zbudujemy w nim bardzo prostą funkcję, która pozwoli nam jednocześnie wysuwać element o klasie water w pole szklanki oraz aktualizować liczbę procentów w jakich szklanka jest wypełniona.


var cnt = document.getElementById("count");
var water = document.getElementById("water");
var percent = cnt.innerText;
var interval;

interval = setInterval(function(){
  percent++;
  cnt.innerHTML = percent;
  water.style.transform = 'translate(0'+','+(100-percent)+'%)';
  
  if (percent == 100){
    clearInterval(interval);
  }

}, 40);

 

Zapisujemy do zmiennych elementy które będziemy animowali oraz powołujemy do życia zmienną interval. Następnie przypisujemy jej funkcję, która będzie się wykonywała aż do momentu, kiedy zmienna percent osiągnie wartość 100. W tym czasie zmieniamy dynamicznie style dla elementu o klasie water, wsuwając go w obszar szklanki. Element o klasie water przesuwany o tyle procent do góry ile wynosi zmienna percent. Ponieważ aktualna wartość właściwości transform:translate względem osi Y wynosi 100%, to odejmujemy od 100 wartość zmiennej percent, aż do uzyskania wartości 0, czyli do momentu kiedy woda w całości przesunie się w pole szklanki. Czas tej operacji określamy w callbacku funkcji – im mniejsza wartość, tym szybciej funkcja się wykona.

Mamy gotową funkcję, która wypełnia nam szklankę wodą! Poniżej stan końcowy funkcji.

See the Pen glass4 by Greg (@GregSzczsnk) on CodePen.

Ostatni krok

Jak na razie nasz progress bar nie zachwyca, dorzućmy coś ekstra! Czemu by nie zrobić fal imitujących nalewanie wody?
Z pomocą przychodzi nam SVG. Dodajmy grafikę w index.html na początku body.

 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" style="display: none;">

<symbol id="wave">
<path d="M420,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C514,6.5,518,4.7,528.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H420z"></path>
<path d="M420,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C326,6.5,322,4.7,311.5,2.7C304.3,1.4,293.6-0.1,280,0c0,0,0,0,0,0v20H420z"></path>
<path d="M140,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C234,6.5,238,4.7,248.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H140z"></path>
<path d="M140,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C46,6.5,42,4.7,31.5,2.7C24.3,1.4,13.6-0.1,0,0c0,0,0,0,0,0l0,20H140z"></path>
</symbol>

</svg> 

Wykorzystamy ją w dwóch różnych wersjach, określając dalej obszar w którym ma się pojawić. Rozróżnimy je nieco kolorem oraz kierunkiem i prędkością animacji.
W divie o klasie water dodajemy grafiki SVG:

 
<div id="water" class="water">
  <svg viewBox="0 0 560 20" class="water_wave water_wave_back">
    <use xlink:href="#wave"></use>
  </svg>
  <svg viewBox="0 0 560 20" class="water_wave water_wave_front">
    <use xlink:href="#wave"></use>
  </svg>
</div>

W pliku ze stylami dodajemy trzy klasy: water_wave ze wspólnymi stylami dla obu fal, oraz water_wave_front i water_wave_back określające wypełnienie różnymi kolorami oraz pozycje początkową.

.water_wave {
width: 200%;
position: absolute;
bottom: 100%;
}

.water_wave_back {
  right: 0;
  fill: #C7EEFF;
  animation: wave-back 1.4s infinite linear;
}

.water_wave_front {
  left: 0;
  fill: #4D6DE3;
  margin-bottom: -1px;
  animation: wave-front .7s infinite linear;
}

Animacje dla każdej z fal będą różniły się tylko kierunkiem:
@keyframes wave-front {
  100% {
    transform: translate(-50%, 0);
  }
}
@keyframes wave-back {
  100% {
    transform: translate(50%, 0);
  }
}

Nasz animowany progress bar jest już prawie gotowy. Możemy jeszcze dodać linie podkreślające kształt szklanki oraz zmienić nieco wartość top i height w klasie water. Niech top wynosi 5% a height 95%. W ten sposób będziemy widzieli animacje naszych pięknych fal także po napełnieniu szklanki.

See the Pen glass5 by Greg (@GregSzczsnk) on CodePen.

Nic nie stoi na przeszkodzie, żeby przy pomocy CSS zmienić kształt szklanki, lub dodać jeszcze jakieś szczegóły, które wzbogacą wygląd waszego paska postępu. Niech ten przykład będzie jedynie inspiracją do stworzenia własnych pomysłów. 🙂
Powodzenia!

 

Site Footer

Sliding Sidebar