Wypełnienie, obrys i dwie rzeczy, które zaskakują wszystkich
Blok o path za nami. Umiesz narysować dowolny kształt. Teraz zajmiemy się tym, jak on wygląda — a zaczniemy od dwóch właściwości, które wpisywaliśmy przez pięć lekcji bez zastanowienia.
fill to wypełnienie wnętrza. stroke to obrys konturu. Brzmi banalnie i w 90% przypadków takie jest. Ale zostałe 10% zawiera dwie pułapki, na których wykłada się każdy.
Wartości domyślne
Warto je znać na pamięć, bo są nieoczywiste:
filldomyślnie jest czarne. Nie przezroczyste — czarne.strokedomyślnie nie istnieje. Wartość tonone.
Stąd dwa objawy z lekcji drugiej: linia bez stroke jest niewidoczna, a łamana bez fill="none" zamienia się w plamę. Kształt bez żadnych atrybutów będzie czarną sylwetką.
Obie właściwości przyjmują też none, dowolny kolor CSS (#e05c1a, tomato, rgb(), hsl()) oraz odwołanie do gradientu przez url(#id), o czym będzie osobna lekcja.
Do przezroczystości służą fill-opacity i stroke-opacity — osobno dla środka i konturu. Jest jeszcze zbiorcze opacity, ale ono działa inaczej: przyciemnia cały element naraz, po złożeniu, więc nachodzące na siebie kontur i wypełnienie nie prześwitują przez siebie. Przy półprzezroczystych kształtach ta różnica bywa widoczna.
Pułapka pierwsza: obrys leży na krawędzi
To jest rzecz, która psuje układy i której nie widać, dopóki ktoś nie pokaże.
Obrys nie jest rysowany wewnątrz kształtu ani na zewnątrz. Jest rozłożony na krawędzi po połowie. Przy stroke-width="20" dziesięć jednostek wchodzi do środka, a dziesięć wystaje na zewnątrz.
Cienka czarna linia pokazuje prawdziwą geometrię prostokąta — dokładnie to, co podałeś w
x,y,widthiheight. Pomarańczowy obrys rozkłada się na niej symetrycznie. Podkręć suwak do końca, a zobaczysz, jak figura wychodzi pozaviewBoxi zostaje przycięta, mimo że sam prostokąt cały czas siedzi w środku.
Konsekwencje są praktyczne. Prostokąt 100×100 z obrysem grubości 20 zajmuje w rzeczywistości 120×120. Ikona zaprojektowana w viewBox="0 0 24 24" z obrysem grubości 2 będzie miała ścięte krawędzie, jeśli kontur dotyka brzegu kadru. Dlatego zestawy ikon konturowych projektuje się z marginesem — zwykle jedną jednostkę z każdej strony.
W CSS istnieje box-sizing, który pozwala wybrać, jak liczyć obramowanie. W SVG nie ma odpowiednika. Jeśli potrzebujesz obrysu wyłącznie wewnętrznego, trzeba go przyciąć maską — i o tym będzie lekcja o clipPath.
Zakończenia i połączenia
Gdy obrys robi się gruby, zaczyna mieć znaczenie, co dzieje się na jego końcach i narożnikach.
stroke-linecap decyduje o końcach otwartej linii. butt ucina równo w punkcie końcowym, round dokłada półkole, squaredokłada kwadrat. Uwaga: round i square wydłużają linię o połowę jej grubości z każdej strony, więc odcinek od 20 do 180 z grubością 16 zajmie w rzeczywistości od 12 do 188.
stroke-linejoin decyduje o narożnikach. miter przedłuża oba boki, aż się spotkają, round zaokrągla, bevel ścina.
Zjedź wierzchołkiem do góry przy ustawieniu
miter. Im ostrzejszy kąt, tym dłuższy kolec — a przy bardzo ostrym kącie kolec miałby długość dążącą do nieskończoności. Dlatego SVG ma bezpiecznik.
Tym bezpiecznikiem jest stroke-miterlimit. Określa, ile razy dłuższy od grubości linii może być kolec, zanim narożnik zostanie automatycznie ścięty. Domyślna wartość to 4. Zmniejsz ją w widgecie do 1 i zobacz, że narożnik przeskakuje na bevel już przy łagodnym kącie.
To wyjaśnia zjawisko, które wygląda jak błąd przeglądarki: narożnik nagle zmienia kształt, choć nic w nim nie zmieniałeś. Nie zmieniałeś — przekroczyłeś limit.
Pułapka druga: co właściwie jest środkiem
Zostaje pytanie, które wygląda na filozoficzne, a jest jak najbardziej praktyczne. Gdy ścieżka przecina samą siebie, co jest jej wnętrzem?
Popatrz na gwiazdę narysowaną jednym ciągiem, jak pentagram na kartce. Środkowy pięciokąt jest w środku czy na zewnątrz? Odpowiedź zależy od przyjętej reguły, a SVG oferuje dwie.
Ta sama ścieżka, dwa ustawienia
fill-rule. Różnica dotyczy wyłącznie środkowego pięciokąta.
nonzero — domyślna. Prowadzi promień od badanego punktu na zewnątrz i liczy przecięcia z konturem, ale ze znakiem: przecięcie w jedną stronę dodaje, w drugą odejmuje. Wynik różny od zera oznacza wnętrze. W gwieździe wszystkie odcinki biegną w tę samą stronę, więc środek wychodzi na wypełniony.
evenodd — liczy przecięcia bez znaku. Nieparzysta liczba to wnętrze, parzysta to zewnętrze. Środek gwiazdy przecina kontur dwa razy, więc zostaje pusty.
W praktyce potrzebujesz tego w dwóch sytuacjach: kształty z dziurami (litera „o", pierścień, ramka) oraz ikony z wyciętym wnętrzem. Jeśli w wyeksportowanym pliku dziura nagle się zalała, fill-rule jest pierwszym miejscem do sprawdzenia — zaraz po kierunku, w którym narysowany jest wewnętrzny kontur.
Kolejność malowania
Domyślnie SVG rysuje najpierw wypełnienie, potem obrys na nim. Przy grubym obrysie oznacza to, że kontur zjada połowę wypełnienia od środka.
Zmienia to właściwość paint-order. Zapis paint-order="stroke fill" odwraca kolejność i sprawia, że wypełnienie ląduje na obrysie — dzięki czemu obrys wygląda, jakby był wyłącznie zewnętrzny. To najprostszy sposób na obwódkę wokół tekstu i na ikony z grubym konturem, który nie ma zjadać środka.
Podsumowanie
filldomyślnie czarne,strokedomyślnie żaden.fill-opacityistroke-opacitydziałają osobno; zbiorczeopacityprzyciemnia element po złożeniu.- Obrys rozkłada się na krawędzi po połowie i powiększa realny rozmiar figury o swoją grubość.
linecaptypuroundisquarewydłużają linię o połowę grubości z każdej strony.mitertworzy kolec przy ostrych kątach;stroke-miterlimit(domyślnie 4) automatycznie ścina go nabevel.fill-rulerozstrzyga, co jest wnętrzem ścieżki przecinającej samą siebie.evenodddaje dziury.paint-orderpozwala położyć wypełnienie na obrysie zamiast pod nim.
W następnej lekcji zostajemy przy obrysie, ale zaczniemy go przerywać — stroke-dasharray i mechanizm, na którym stoi efekt rysującej się linii.
Do wypróbowania: w presecie stroke-width dopisz do pomarańczowego prostokąta paint-order="stroke fill" i ustaw grubość na 30. Obrys przestanie zjadać wypełnienie od środka i zacznie wyglądać, jakby był rysowany wyłącznie na zewnątrz — choć geometrycznie nic się nie zmieniło.