Niezależnie od tego czy masz czuprynę jako Giorgio Tsoukalos czy też mógłbyś brać udział w Super Bowl, przepraszam Super Ball, włos z głosy Ci nie spadnie po przeczytaniu dzisiejszego odcinka. I nie tylko z głowy. Dziś o właściwościach związanych z SVG, może niekoniecznie jeżących włosy na głowie, ale równie ciekawych.
Być jak drogowcy.
Był już wpis zbliżający nas do umiejętności malarskich Picasso, ale tym razem pójdziemy w bardziej przyziemnym kierunku. Przyjrzymy się sztuce zbliżonej do malowania pasów na jezdni. To co nam ułatwi pracę, to właściwość stroke-dasharray dla elementów SVG, a dokładnie dla ich obrysów (stroke), ale nie wypełnienia (fill). Głównie chodzi o kształty typu: linia, krzywa, okrąg, prostokąt, poligon czy też elementy tekstowe. Kształt bez tej właściwości ma po prostu ciągłą obwódkę (albo jej brak), bez początku i końca. Ciekawe efekty pojawiają się wraz z pojawieniem się wartość dla tej właściwości. Niech za przykład posłuży odcinek o długości 10.
<svg viewBox="0 0 10 1" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="0" x2="10" y2="0" stroke="#000" stroke-dasharray="1 2 3" />
</svg>
Możemy podać dowolną liczbę wartości. Pierwsza z nich określa długość fragmentu obrysu, druga długość pustej przestrzeni. Jeśli liczba podanych wartości jest nieparzysta, to po zakończeniu cyklu pierwsza z wartości staje się długością przerwy. Widać to na przykładzie 2, 3 oraz 5. Podane wartości mają sens jeśli ich suma nie przekracza długości obrysu. W naszym przypadku nie ma sensu wzór: 5, 6 lub 7, 4, 1, 3, bo część pełnych fragmentów i przerw będzie poza obrysem.
Ale tak nie do końca. Jest jeden szczególny przypadek, który z pozoru wydaje się sytuacją obrysu bez stroke-dasharray. Jest to wartość równa długości obrysu. Jeśli nasza linia ma długość 10 i ustawimy stroke-dasharray="10", to obrys wypełni linię. W przypadku kształtów zamkniętych oczywiście taka wartość spowoduje połączenie początu i końca obrysu. Natomiast przerwy o długości 10 nie zobaczysz - przynajmniej nie w tej chwili.
Kto ma ochotę na przerwę?
Żeby upragniona przerwa zbliżyła się do nas, użyteczna będzie współpracująca z wcześniejszą właściwością, stroke-dashoffset. Ta pozwala nam ustalić przesunięcie wzorca. Wartości dodatnie przesuwają szlaczek w lewo, ujemnie w prawo. Tu dla przykładu mamy znów linię o długości 10 i stroke-dasharray="10".
Jak zapewne zauważyłeś ustawiając wartość równą długości obrysu, wypełnienie schowa się, a pokaże się cała przerwa. Jak zapewnie też zauważyłeś wystarczy zmieniać wartość stroke-dashoffset od wartości maksymalnej do 0, aby uzyskać efekt paska postępu (0-100%). A to, że obrys nie musi być linią prostą pozwala nam stworzyć pasek postępu jakiego dusza zapragnie.
Let's twist!
Linia prosta powoduje, że efekt jest prosty. I nudny. Frajdę sprawia użycie elementów o nieregularnych kształtach. Jest jednak małe drobne utrudnienie. W przypadku kształtów regularnych zazwyczaj znamy długość obrysu. W przypadku elementów PATH tylko jasnowidz bez żadnej pomocy może znaleźć tą długość. Nie wiem jak Ty, ale ja nim nie jestem i wspomagam się JavaScript. Pomocna staje się metoda getTotalLength dla elementów SVG. Możesz to zrobić własnoręcznie (tymczasowo w kodzie) lub użyć prostego narzędzia do obliczania długości ścieżki, które przygotowałem (link jest w przydatnych linkach, wystarczy wkleić kod źródłowy SVG i kliknąć w wybraną ścieżkę).
Mając długość podstawiamy ją jako wartość dla stroke-dasharray oraz początkową dla stroke-dashoffset. Zmniejszając cyklicznie wartość drugiej właściwości nasz obrys będzie sprawiał wrażenie wydłużania się. Pierwszy przykład, to symulacja preloadera. Kliknij w pustą przestrzeń, aby wystartować postęp.
Kod odpowiedzialny za powyższy przykład:
/* nasz szlaczek */
<svg xmlns="http://www.w3.org/2000/svg" width="450.66" height="50.937" viewBox="0 0 130 18"><path style="--offset:156.227" d="m47.625 87.979c3.528-3.528 7.06-7.06 10.583-7.06 3.528 0 7.06 3.528 10.583 7.06 3.528 3.528 7.06 7.06 10.583 7.06 3.528 0 7.06-3.528 10.583-7.06 3.528-3.528 7.06-7.06 10.583-7.06 3.528 0 7.06 3.528 10.583 7.06 3.528 3.528 7.06 7.06 10.583 7.06 3.528 0 7.06-3.528 10.583-7.06 3.528-3.528 7.06-7.06 10.583-7.06 3.528 0 7.06 3.528 10.583 7.06 3.528 3.528 7.06 7.06 10.583 7.06 3.528 0 7.06-3.528 10.583-7.06" fill="none" stroke="#f00" stroke-width="2px" transform="translate(-47.531-78)"/></svg>
+
svg {
position: absolute;
width: 500px;
max-width: calc(100% - 64px);
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
svg path {
/* ustawiamy długość obrysu
na długość ścieżki
*/
stroke-dasharray: 156.227;
/* przesunięcie pobieramy ze zmiennej
CSS
*/
stroke-dashoffset: var(--offset);
/* właściwość jest animowalna,
więc wykorzystamy to, aby przesunięcie
było łagodniejsze
*/
transition: stroke-dashoffset .25s ease;
}
+
/* startujemy postęp na kliknięcie
*/
document.body.addEventListener('click', function() {
preload();
});
function preload() {
setTimeout(function() {
/* pobieramy aktualne przesunięcie
*/
let offset = getComputedStyle(document.querySelector('svg path')).getPropertyValue('--offset');
/* jeśli jest dodatnie
*/
if (offset > 0) {
/* to zmniejszamy o 10
*/
offset -= 10;
if (offset < 0) {
offset = 0;
}
/* i wstawiamy jako nowe przesunięcie
*/
document.querySelector('svg path').style.setProperty('--offset', offset);
preload();
}
}, Math.random() * 500 + 500);
}
Powyższy kod JS jest tylko przykładowym. Jeśli faktycznie chcesz go użyć jako preloadera, to zamiast wołać setTimeout, musisz wywoływać funkcję preload po każdym zdarzeniu load pojedynczego obrazka. Wartość dla właściwości stroke-dashoffset należy obliczyć jako: (1 - ilosc_wczytanych_obrazkow / ilosc_wszystkich_obrazkow) * dlugosc_obrysu.
Ostatnio pojawiło się kilka wpisów na temat wskaźnika przewinięcia strony, więc byłoby wstyd gdyby znów nie wspomnieć o nim. Tym razem zupełnie odmienny efekt.
<svg xmlns="http://www.w3.org/2000/svg" width="492.63" height="494.64" viewBox="0 0 141 141"><path d="m87.69 153.37c.014.015-.038.039-.042.04-.194.057-.286-.239-.28-.373.027-.685.912-.881 1.444-.754 1.606.384 1.949 2.414 1.454 3.747-1.147 3.085-5.102 3.616-7.771 2.371-5.251-2.45-6.01-9.336-3.498-14.01 4.428-8.236 15.473-9.259 22.948-4.828 12.172 7.215 13.491 23.874 6.352 35.08-10.948 17.191-34.897 18.833-50.904 8.06-23.424-15.763-25.415-48.902-9.95-70.9 21.794-31 66.25-33.363 95.56-12.01 40.06 29.18 42.807 87.29 14.231 125.38" fill="none" fill-rule="evenodd" stroke="#f00" stroke-width="2px" transform="translate(-36-83)"/></svg>
+
svg {
position: fixed;
width: 500px;
max-width: calc(100% - 64px);
left: 50%;
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
opacity: 0.25;
z-index: -1;
}
svg path {
/* ustawiamy długość obrysu
na długość ścieżki
*/
stroke-dasharray: 481.89;
/* przesunięcie pobieramy ze zmiennej
CSS
*/
stroke-dashoffset: var(--offset);
stroke-linecap: round;
}
+
window.addEventListener('scroll', function() {
document.querySelector('svg path').style.setProperty('stroke-dashoffset', (1 - (window.scrollY / (document.body.scrollHeight - document.documentElement.clientHeight))) * 481.89);
});
var e = document.createEvent('HTMLEvents');
e.initEvent('scroll', true);
window.dispatchEvent(e);
Podpinamy się tu pod zdarzenie scroll okna. Przy każdym przewinięciu obliczamy (wzór podałem przy okazji preloadera) ile procentowo przewinięliśmy strony i przeliczamy na długość fragmentu obrysu. Drugi fragment służy tylko i wyłącznie do ustawienia obrysu w odpowiednim miejscu, kiedy odświeżymy stronę przy przesuniętym scrollbarze (po prostu odpalamy zdarzenie scroll).
Nie ma co więcej dzielić włosa na czworo. Do przeczytania w kolejnym wpisie!
Przydatne linki:
Narzędzie do obliczania długości ścieżki w SVG.
Nieregularny preloader.
Spiralny wskaźnik przewinięcia strony.