Grafika SVG ÔÇö obszerny przewodnik

> Dodaj do ulubionych

Streszczenie

Jest to obszerny przewodnik po metodach programowania grafiki SVG na przyk┼éadzie tworzenia pary animowanych oczu. Dowiesz si─Ö jak utworzy─ç zbi├│r nadaj─ůcych si─Ö do wielokrotnego wykorzystania komponent├│w graficznych, a tak┼╝e zapoznasz si─Ö z niezb─Ödnymi informacjami na temat przekszta┼éce┼ä SVG oraz przestrzeni wsp├│┼érz─Ödnych.

SVG to, tak jak HTML czy XML, standardowy format znacznikowy, kt├│ry generuje skalowaln─ů grafik─Ö wektorow─ů (SVG) wewn─ůtrz przegl─ůdarek internetowych. Grafika wektorowa implementowana jest w postaci prostych kszta┼ét├│w, kt├│rych ostro┼Ť─ç zostaje zachowana przy dowolnym powi─Ökszeniu. Dla por├│wnania obrazy rastrowe, np. rysunki z programu Paint, sk┼éadaj─ů si─Ö z serii pikseli, kt├│re mog─ů sta─ç si─Ö niewyra┼║ne po powi─Ökszeniu w wysokiej rozdzielczo┼Ťci. Je┼Ťli wi─Öc chcesz m├│c swobodnie operowa─ç na wybranych elementach grafiki, u┼╝yj formatu SVG. Grafika SVG generowana jest wewn─ůtrz struktury DOM przegl─ůdarki, zatem ka┼╝dy komponent graficzny mo┼╝na formatowa─ç za pomoc─ů CSS, manipulowa─ç nim w JavaScripcie poprzez podstawowe API i sprawi─ç, by by┼é odpowiednio wy┼Ťwietlany na stronach HTML.

W tej cz─Ö┼Ťci przewodnika dowiesz si─Ö jak korzysta─ç z SVG w obr─Öbie innych, zapewne znanych ci ju┼╝, podstawowych standardowych technologii sieciowych. Zapoznasz si─Ö z istotnymi r├│┼╝nicami oraz wst─Öpnie zaznajomisz si─Ö z wieloma funkcjami SVG, kt├│re zosta┼éy szczeg├│┼éowo opisanych w dalszych cz─Ö┼Ťciach. W tej cz─Ö┼Ťci dowiesz si─Ö przede wszystkim jak stworzy─ç z┼éo┼╝on─ů interaktywn─ů grafik─Ö, sk┼éadaj─ůc─ů si─Ö z nadaj─ůcych si─Ö do wielokrotnego wykorzystania komponent├│w. Nauczysz si─Ö zmienia─ç ich rozmiar oraz umieszcza─ç je w obr─Öbie r├│┼╝nych powierzchni rysunku.

SVG oczy

W ramach tego kursu prze┼Ťledzisz przyk┼éad ilustruj─ůcy budow─Ö rysunku pary ruszaj─ůcych si─Ö i mrugaj─ůcych oczu.

Definiowanie pola rysunku

Kod SVG mo┼╝na zastosowa─ç na kilka sposob├│w. W przyk┼éadach prezentowanych w tym przewodniku jest on osadzony bezpo┼Ťrednio w kodzie HTML, co pozwala na stosowanie CSS i JavaScriptu zar├│wno do grafiki SVG jak i do tre┼Ťci HTML. Oto podstawowy kod z elementem svg zawieraj─ůcym dane grafiki:

<!DOCTYPE html>
<html>
<head>
<title>Przewodnik po grafice SVG ÔÇö demo</title>
<link href="css/eyeballs.css" rel="stylesheet" />
</head>
<body>
<svg width="600" height="200">
  <title>Oczy</title>
  <desc>Interaktywne demo przedstawiaj─ůce kilka funkcji grafiki SVG</desc>
</svg>
<script src="js/eyeballs.js"></script>
</body>
</html>

Kiedy tylko zajdzie taka potrzeba, mo┼╝esz doda─ç elementy title i desc, aby skomentowa─ç kod.

Gałka oczna

Poprzez dodanie poni┼╝szej linijki w obszarze svg utworzymy ko┼éo. Atrybuty cx i cy pozycjonuj─ů jego ┼Ťrodek, za┼Ť atrybut r odpowiedzialny jest za rozmiar okr─Ögu wzgl─Ödem ┼Ťrodka:

<circle id="eyeball" cx="100" cy="100" r="50"/>
SVG koło

Domy┼Ťlne wype┼énienie jest koloru czarnego. Dzi─Öki atrybutowi fill mo┼╝na zmieni─ç kolor t┼éa na bia┼éy, a atrybut stroke pozwala doprecyzowa─ç kolor kraw─Ödzi:

<circle id="eyeball" cx="100" cy="100" r="50" fill="white" stroke="black"/>

iałe koło SVG

By utworzy─ç t─Öcz├│wk─Ö i ┼║renic─Ö, mo┼╝esz doda─ç wi─Öcej k├│┼é i okre┼Ťli─ç ich kolory w atrybucie fill. Na pocz─ůtku zadeklaruj wi─Öksze okr─Ögi, aby nie przes┼éoni┼éy mniejszych:


<circle id="eyeball" cx="100" cy="100" r="50" fill="white" stroke="black"/>
<circle id="iris"    cx="100" cy="100" r="25" fill="lightblue"/>
<circle id="pupil"   cx="100" cy="100" r="12" fill="black"/>

Ko┼éo z ko┼éami w ┼Ťrodku SVG

Stylizacja grafik za pomoc─ů CSS

W powy┼╝szym przyk┼éadzie id, cx, cy, oraz r s─ů zwyk┼éymi atrybutami, natomiast fill i stroke nazywane s─ů atrybutami prezentacyjnymi. S─ů to po prostu w┼éasno┼Ťci CSS wyra┼╝ane jako atrybuty. Mo┼╝na je rozpozna─ç po nazwach, bo od standardowych w┼éasno┼Ťci CSS r├│┼╝ni─ů si─Ö tylko zastosowaniem notacjiWielb┼é─ůdziej zamiast typowej w CSSnotacji z ┼é─ůcznikami-w-d┼éu┼╝szych-nazwach.

Wiele w┼éasno┼Ťci CSS, np. fill czy stroke, ma zastosowanie tylko dla tre┼Ťci SVG. Inne za┼Ť, tak jak font-size, mo┼╝na zastosowa─ç zar├│wno do kodu SVG jak i HTML. Niekt├│re z nich mog─ů mie─ç inne nazwy, lecz zachowuj─ů si─Ö tak jak w┼éasno┼Ťci CSS stosowane dla kodu HTML. W┼éasno┼Ť─ç fill funkcjonuje w SVG w du┼╝ym stopniu analogicznie do w┼éasno┼Ťci background-color i background-image dla HTML. Poniewa┼╝ fill jest w┼éasno┼Ťci─ů CSS, mo┼╝na j─ů zmodyfikowa─ç poprzez atrybut style. Dzi─Öki temu zyska on pierwsze┼ästwo wobec warto┼Ťci atrybut├│w prezentacyjnych. Wida─ç to na poni┼╝szym przyk┼éadzie, w kt├│rym kolor oczu zostanie zmieniony na br─ůzowy:

<circle id="iris" cx="100" cy="100" r="25" fill="lightblue" style="fill:brown"/>

Aby oddzieli─ç kod CSS od kodu znacznikowego, arkusze styl├│w mo┼╝na przenie┼Ť─ç do osobnego pliku, podobnie jak robi si─Ö w przypadku zwyk┼éego HTML-a. Umieszczenie kodu CSS w pliku eyeballs.css, do kt├│rego zosta┼éo utworzone odniesienie w kodzie HTML pozwoli uzyska─ç nieco kr├│tszy kod SVG:

#eyeball { fill: white; stroke: black }
#iris { fill: lightblue }
#pupil { fill: black }
<circle id="eyeball" cx="100" cy="100" r="50"/>
<circle id="iris"    cx="100" cy="100" r="25"/>
<circle id="pupil"   cx="100" cy="100" r="12"/>

W niekt├│rych przypadkach stosowanie arkuszy styl├│w jest jednak niewskazane, o czym przekonasz si─Ö w dalszej cz─Ö┼Ťci poradnika.

Tworzenie gradientu

Wsp├│┼é┼Ťrodkowe okr─Ögi s─ů dobrym sposobem na utworzenie kraw─Ödzi t─Öcz├│wki oraz ┼║renicy, ale nie sprawdz─ů si─Ö przy tworzeniu przekrwienia w k─ůcie oka. Alternatywnym rozwi─ůzaniem mo┼╝e by─ç implementacja ca┼éej ga┼éki ocznej w postaci du┼╝ego okr─Ögu, wewn─ůtrz kt├│rego gradient promienisty utworzy seri─Ö wsp├│┼é┼Ťrodkowych pier┼Ťcieni:

<circle id="eyeball" cx="100" cy="100" r="150" fill="url(#eyeballFill)" />
<img src="/wp-content/uploads/05502px-svgGrandTour_eyeball_fill.png" alt="Oko - kolejny etap" width="230" height="275" class="alignleft size-full wp-image-13294" />
<radialGradient id="eyeballFill">
  <stop id="inner"           offset="12%"  stop-color="black" />
  <stop id="pupil"           offset="15%"  stop-color="lightblue" />
  <stop id="iris"            offset="27%"  stop-color="lightblue" />
  <stop id="white"           offset="30%"  stop-color="white" />
  <stop id="bloodshotExtent" offset="40%"  stop-color="white" />
  <stop id="outer"           offset="100%" stop-color="pink" />
</radialGradient>
Oko - kolejny etap

Atrybut fill elementu circle wykorzystuje dost─Öpn─ů w CSSfunkcj─Ö url(), aby utworzy─ç odniesienie do identyfikatora elementu radialGradient. Zagnie┼╝d┼╝one w gradiencie elementy stop definiuj─ů do┼Ť─ç nag┼é─ů gradacj─Ö kolor├│w od ┼Ťrodka w kierunku kraw─Ödzi oka — zaczynaj─ůc od czerni poprzez kolor niebieski, a┼╝ do bieli, kt├│ra ┼éagodniej przechodzi w r├│┼╝ okalaj─ůcy kraw─Öd┼║ ko┼éa. Gradienty SVG funkcjonuj─ů w podobny spos├│b do gradient├│w CSS stosowanych do tre┼Ťci HTML. Te ostatnie dost─Öpne s─ů jednak tylko poprzez w┼éasno┼Ť─ç background-image, kt├│rej nie mo┼╝na zastosowa─ç do SVG.

Tworzenie referencji do grafiki

Nowoutworzone ko┼éo jest o wiele wi─Öksze ni┼╝ w┼éa┼Ťciwa ga┼éka oczna, poniewa┼╝ chcemy nim porusza─ç pod mniejszymi powiekami. (P├│ki co nie przejmuj si─Ö, ┼╝e kszta┼ét jest szerszy od pola rysunku). Nim zbudujemy powieki powinni┼Ťmy umie┼Ťci─ç w jednym miejscu zdefiniowane ju┼╝ komponenty graficzne. Do elementu svg dodaj zatem obszar defs :

<!DOCTYPE html>
<html>
<head>
<title>Przewodnik po grafice SVG ÔÇö demo</title>
<link href="css/eyeballs.css" rel="stylesheet" />
</head>
<body>
<svg width="600" height="200">
   <title>gałki oczne</title>
   <desc>Interaktywne demo przedstawiaj─ůce kilka funkcji grafiki SVG</desc>
   <defs>      <!-- tutaj umie┼Ť─ç komponenty -->   </defs></svg>
<script src="js/eyeballs.js"></script>
</body>
</html>

Je┼Ťli przeniesiesz radialGradient do obszaru defs, element circle wci─ů┼╝ b─Ödzie si─Ö do niego odnosi┼é; je┼Ťli jednak w to samo miejsce przeniesiesz circle, ko┼éo nie zostanie wygenerowane.

Dost─Öpny w SVG element use pozwala na wielokrotne wykorzystanie tych samych komponent├│w graficznych i na umieszczanie ich w obszarach o r├│┼╝nej wielko┼Ťci. Je┼Ťli umie┼Ťcimy element circle w obszarze defs i odniesiemy si─Ö do niego za pomoc─ů elementu use znajduj─ůcego poza elementem defs, grafika zostanie wygenerowana prawid┼éowo:

<use xlink:href="#eyeball"/>

Funkcjonowanie elementu use mo┼╝e si─Ö z pocz─ůtku wydawa─ç nieco zaskakuj─ůce. Nie stanowi on bowiem zwyk┼éej kopii obiektu, do kt├│rego si─Ö odnosi, lecz g┼é─Öbok─ů referencj─Ö. Wszystkie zmiany wprowadzone do prototypu elementu circle i komponent├│w radialGradient b─Öd─ů nast─Öpowa─ç dynamicznie zawsze, gdy element use b─Ödzie si─Ö do nich odnosi─ç. Mimo tej elastyczno┼Ťci nie mo┼╝na jednak zmodyfikowa─ç ┼╝adnych atrybut├│w obiekt├│w, do kt├│rych si─Ö odnosimy. Istnieje natomiast mo┼╝liwo┼Ť─ç dodania opcjonalnych atrybut├│w prezentacyjnych, kt├│re nie zosta┼éy jeszcze zdefiniowane.

Jako ┼╝e element use i jego docelowe elementy znajduj─ů si─Ö w r├│┼╝nych cz─Ö┼Ťciach drzewa dokumentu, arkusze styl├│w nie b─Öd─ů, tak jak to zwykle bywa, automatycznie kaskadowa─ç do elementu use. Kod CSS mo┼╝na natomiast zastosowa─ç do niego osobno. W poni┼╝szym przyk┼éadzie zastosujemy style przypisane identyfikatorowi docelowego elementu i klasie odwo┼éuj─ůcych si─Ö do niego element├│w use:

<style>
#eyeball, .eyeball {
    fill: url(#eyeballFill);
}
</style>
<svg width="200" height="200">
<defs>
<circle id="eyeball" cx="100" cy="100" r="150" />
<defs>
<use xlink:href="#eyeball" class="eyeball" id="eyeball_1"/>
</svg>

Zwr├│─ç uwag─Ö, ┼╝e je┼Ťli wybrany element circle mia┼éby r├│wnie┼╝ przypisan─ů klas─Ö eyeball, element use nie m├│g┼éby tego ko┼éa zmodyfikowa─ç.

Ponadto w elemencie use atrybut href zosta┼é przyporz─ůdkowany do przestrzeni nazw xlink, aby odr├│┼╝ni─ç go od zwyk┼éego atrybutu HTML o tej samej nazwie u┼╝ywanego bez kwalifikatora. Atrybut xlink:href stanowi inny spos├│b odnoszenia si─Ö do obiekt├│w ni┼╝ u┼╝ywana w CSS sk┼éadnia url(), kt├│r─ů zastosowano w przyk┼éadzie powy┼╝ej.

Powieki

Aby umie┼Ťci─ç oko mi─Ödzy powiekami, musimy skorzysta─ç ze ┼Ťcie┼╝ki odci─Öcia, dzi─Öki kt├│rej dany obiekt wy┼Ťwietlany jest tylko w├│wczas, gdy wyst─Öpuje w obr─Öbie innego kszta┼étu. W tym przypadku powieki s─ů ┼Ťcie┼╝k─ů o dowolnie wybranym kszta┼écie, kt├│rej atrybut d (definition) pozwala narysowa─ç dwie naprzeciwleg┼ée krzywe:

<path id="eyelids" d="M 200,100 Q 100,200 0,100 Q 100,0 200,100" />
Powieki SVG

Definicja ka┼╝dej krzywej zawiera punkty kontrolne, kt├│re nie s─ů wy┼Ťwietlane, lecz maj─ů wp┼éyw na kszta┼ét krzywej. Zaczynaj─ůc od prawego rogu (M, ang. move to ÔÇö przesuwa─ç si─Ö do), punkt kontrolny pierwszej kwadratowej (Q) krzywej umieszczony jest u g├│ry, a krzywa biegnie do lewego rogu. Druga za┼Ť ko┼äczy si─Ö w prawym, a jej punkt kontrolny umieszczony jest w dolnym rogu.

Oko nast─Öpny etap

Aby nasz kszta┼ét zachowywa┼é si─Ö jak ┼Ťcie┼╝ka odci─Öcia, nale┼╝y umie┼Ťci─ç ┼Ťcie┼╝k─Ö wewn─ůtrz elementu clipPath. W tym celu niezb─Ödne b─Ödzie ponowne wykorzystanie kszta┼étu, zatem warto odnie┼Ť─ç si─Ö do niego za pomoc─ů referencji use:

<clipPath id="clipEyelid">
    <use xlink:href="#eyelids" class="eyelids" />
</clipPath>

Teraz dodaj w┼éasno┼Ť─ç clip-path, aby zastosowa─ç ┼Ťcie┼╝k─Ö odci─Öcia do ga┼éki ocznej. W poni┼╝szym przyk┼éadzie w┼éasno┼Ť─ç ta zosta┼éa przypisana w kaskadowym arkuszu styl├│w:

.eyeball {
    fill       : url(#eyeballFill);
    clip-path  : url(#clipEyelid);
}
<use xlink:href="#eyeball" class="eyeball" />
Oko z obrysem

Do rozwi─ůzania pozostaje teraz jeszcze tylko jeden problem ÔÇöpowieka znikn─Ö┼éa. ┼Ücie┼╝ki odci─Öcia nie s─ů wy┼Ťwietlane, zatem musimy odnie┼Ť─ç si─Ö do nich za pomoc─ů jeszcze jednej referencji. Pierwszy element use widoczny w poni┼╝szym kodzie umieszcza ga┼ék─Ö oczn─ů wewn─ůtrz ┼Ťcie┼╝ki odci─Öcia, za┼Ť drugi generuje sam─ů ┼Ťcie┼╝k─Ö. Trzeci element use, znajduj─ůcy si─Ö poza elementem defs , generuje ca┼éy obiekt:

<defs>
<g id="eye">
  <use xlink:href="#eyeball" class="eyeball" />
  <use xlink:href="#eyelids" class="eyelids" />
</g>
</defs>
<use xlink:href="#eye" />
Oko z rz─Ösami

Na tym etapie pracy warto zgrupowa─ç nasze dwa elementy graficzne poprzez umieszczenie ich wewn─ůtrz elementu g, dzi─Öki czemu uzyskamy wi─Ökszy obiekt semantyczny o nazwie eye. Do zgrupowanego obiektu mo┼╝na odnie┼Ť─ç si─Ö za pomoc─ů referencji oraz, o czym przekonasz si─Ö w dalszej cz─Ö┼Ťci, przenie┼Ť─ç go lub przekszta┼éci─ç jako jednostk─Ö.

Rz─Ösy

Narysowanie rz─Ös wzd┼éu┼╝ powieki b─Ödzie wymaga┼éo od nas odrobiny kreatywno┼Ťci. Do powieki musimy doda─ç trzeci─ů referencj─Ö.

<g id="eye">
  <use xlink:href="#eyelids" class="eyelashes" />
  <use xlink:href="#eyelids" class="eyelids"   />
  <use xlink:href="#eyeball" class="eyeball"   />
</g>

Klasa eyelashes definiuje wygl─ůd podstawowego kszta┼étu eyelids zupe┼énie inaczej:

.eyelashes {
    fill              : none;
    stroke            : #ddd;
    stroke-width      : 40;
    stroke-dasharray  : 1,10;
    stroke-linejoin   : bevel;
}

W┼éasno┼Ť─ç stroke-width pogrubia kraw─Öd┼║ w taki spos├│b, ┼╝e wychodzi ona poza kontur zar├│wno do wewn─ůtrz jak i na zewn─ůtrz kszta┼étu. Domy┼Ťlnie w┼éasno┼Ť─ç stroke ma posta─ç linii ci─ůg┼éej, jednak mo┼╝na j─ů zmieni─ç w lini─Ö przerywan─ů. Ustawienie warto┼Ťci stroke-dasharray na 1,10 sprawi, ┼╝e na ka┼╝dy piksel wygenerowany wok├│┼é kraw─Ödzi kszta┼étu dziesi─Ö─ç zostanie pomini─Ötych. W ten spos├│b narysujemy pojedyncze rz─Ösy:

Oko z rz─Ösami

W┼éasno┼Ť─ç stroke-linejoin o warto┼Ťci bevel zapobiega wygenerowaniu rz─Ös zbyt daleko od rogu oka, gdzie te┼╝ spotykaj─ů si─Ö powieki.

Kilka poci─ůgni─Ö─ç tuszem

Nawet po rozja┼Ťnieniu linii, rz─Ösy wci─ů┼╝ wydaj─ů si─Ö zbyt ostre i za cienkie w por├│wnaniu do ga┼éki ocznej. Warto by┼éoby je nieco zmi─Ökczy─ç i pogrubi─ç. Filtry SVG dostarczaj─ů wielu narz─Ödzi do obr├│bki grafiki, kt├│re mo┼╝na ze sob─ů ┼é─ůczy─ç i dopasowywa─ç tak, aby osi─ůgn─ů─ç po┼╝─ůdany efekt.

Dodaj element filter do obszaru defs. B─Ödzie on pe┼éni┼é funkcj─Ö kontenera dla r├│┼╝nych komponent├│w efekt├│w filtra, kt├│re domy┼Ťlnie s─ů zastosowywane jeden po drugim. W poni┼╝szym przypadku efekt feGaussianBlur nieregularnie rozproszy piksele (nieco bardziej w pionie ani┼╝eli w poziomie), a feComponentTransfer je przyciemni:

<filter
    id           = "soften"
    x            = "-20"
    y            = "-20"
    width        = "250"
    height       = "250"
    filterUnits  = "userSpaceOnUse"
>
  <desc>Zmi─Ökcza powiek─Ö i rz─Ösy</desc>
  <feGaussianBlur stdDeviation="1 3" />
  <feComponentTransfer>
      <feFuncR type="linear" slope="0.3"/>
      <feFuncG type="linear" slope="0.3"/>
      <feFuncB type="linear" slope="0.3"/>
  </feComponentTransfer>
</filter>

Poniewa┼╝ powieka generowana jest mi─Ödzy 0 a 200 pikselem, rz─Ösy wychodz─ů nieco poza lew─ů kraw─Öd┼║ pierwotnego pola rysunku. Atrybuty x, y, width i height elementu filter nadaj─ů dany efekt na wi─Ökszym obszarze. Nadanie w┼éasno┼Ťci filterUnits warto┼Ťci userSpaceOnUse sprawi, ┼╝e wykorzystany zostanie uk┼éad wsp├│┼érz─Ödnych kszta┼étu, wobec kt├│rego jest stosowany filtr. W innym wypadku nadane warto┼Ťci by┼éyby interpretowane jako procentowe warto┼Ťci otaczaj─ůcego obiekt pola.

Aby nada─ç okre┼Ťlony efekt kszta┼étowi, kt├│ry wykorzystali┼Ťmy do wygenerowania powiek i rz─Ös, skorzystamy z w┼éasno┼Ťci filter:

#eyelids {
    filter : url(#soften);
}

Tak rz─Ösy b─Öd─ů prezentowa─ç si─Ö najpierw po rozmyciu, a nast─Öpnie po przyciemnieniu:

z filtremz filtrem z filtrami

Przekształcenia i przestrzenie współrzędnych

Na zako┼äczenie zgrupujmy dwa wyst─ůpienia obiektu eye w obiekcie eyes:

<g id="eyes">
  <use xlink:href="#eye"/>
  <use xlink:href="#eye"/>
</g>

Powy┼╝szy kod wygeneruje dwa kszta┼éty w tym samym miejscu. Aby je rozdzieli─ç, skorzystaj z atrybutu transform. Przesunie on prawe oko nieco w prawo, za┼Ť lewe oko zostanie umieszczone dalej o 300 jednostek:

<g id="eyes">
  <use xlink:href="#eye" id="eyeRight" transform="translate(50,0)"/>
  <use xlink:href="#eye" id="eyeLeft"  transform="translate(350,0)"/>
</g>

Funkcja translate() atrybutu transform pozwala zmieni─ç po┼éo┼╝enie obiekt├│w, do kt├│rych utworzono referencj─Ö za pomoc─ů elementu use. Przekszta┼écenia mo┼╝na jednak zastosowa─ç do niemal ka┼╝dego elementu graficznego. Przekszta┼écenia SVG cechuj─ů si─Ö tak─ů sam─ů funkcjonalno┼Ťci─ů co dwuwymiarowe przekszta┼écenia CSS. Funkcja scale() nadaje obiektowi rozmiar okre┼Ťlony przez warto┼Ť─ç dziesi─Ötn─ů — 1 to obecny rozmiar, 0 zmniejsza obiekt do punktu, a warto┼Ťci wi─Öksze od 1 powi─Ökszaj─ů grafik─Ö. Funkcja rotate() przyjmuje miar─Ö k─ůta deg lub rad, o kt├│r─ů obiekt zostanie obr├│cony. Funkcje skewX() i skewY() przyjmuj─ů k─ůt, o kt├│ry obiekt zostanie przekrzywiony w poziomie lub w pionie, a funkcja translate() zmienia pozycj─Ö obiektu.

Semantycznie zgrupowane oczy zostan─ů wygenerowane poprzez pojedynczy element use umieszczony poza obszarem elementu defs:

<use xlink:href="#eyes"/>
Dwoje oczu

Je┼Ťli b─Ödziemy chcieli wy┼Ťwietla─ç oczy wraz z innymi elementami interfejsu, konieczna mo┼╝e okaza─ç si─Ö zmiana rozmiaru naszej grafiki. Pierwotny element svg okre┼Ťla┼é, ┼╝e ma by─ç ona wy┼Ťwietlana w obr─Öbie prostok─ůta o wymiarach 600├Ś200 pikseli:

<svg width="600" height="200">
Dwoje oczu w ramce

Co jednak je┼Ťli taki rozmiar grafiki oka┼╝e si─Ö za du┼╝y na nasze potrzeby? Pomniejszenie elementu przy u┼╝yciu atrybut├│w CSS width i height sprawi, ┼╝e nowe wymiary nie b─Öd─ů odpowiada─ç warto┼Ťciom okre┼Ťlonym wewn─ůtrz grafiki:

<style>
svg {
    width  : 300px;
    height : 100px;
}
</style>
    <!-- ...or... -->
<svg width="300" height="100">
Pół oka

Rozwi─ůzanie stanowi zdefiniowanie w┼éasnego pola za pomoc─ů atrybutu viewBox. W ten spos├│b zadeklarujemy zbi├│r abstrakcyjnych jednostek, kt├│re b─Öd─ů wykorzystywane jedynie wewn─ůtrz danej grafiki. Nie b─Öd─ů one w ┼╝aden spos├│b powi─ůzane z zewn─Ötrznym uk┼éadem wsp├│┼érz─Ödnych grafiki, do kt├│rego odnosz─ů si─Ö atrybuty SVG width i height:

<svg width="300" height="100" viewBox="0 0 600 200">
viewbox

Je┼Ťli kszta┼ét elementu nie b─Ödzie pasowa┼é do obszaru widoku, w kt├│rym wy┼Ťwietlana jest grafika, przyjdzie nam z pomoc─ů atrybut preserveAspectRatio. W poni┼╝szym przyk┼éadzie warto┼Ť─ç xMidYMid atrybutu wy┼Ťrodkuje element i pomniejszy go do odpowiedniego rozmiaru — tak, aby ca┼éa grafika mog┼éa zosta─ç wy┼Ťwietlona:

<svg width="100" height="100" viewBox="0 0 600 200" preserveAspectRatio="xMidYMid">

Wprawianie oczu w ruch

Do rozwi─ůzania pozosta┼é tylko jeden problem — nasza grafika tak naprawd─Ö nic nie robi. Chcieliby┼Ťmy natomiast, aby nasze oczy si─Ö porusza┼éy.

W wielu przypadkach do animacji w┼éasno┼Ťci numerycznych i kolor├│w w SVG mo┼╝emy skorzysta─ç z technik CSS. Oto przyk┼éad, jak stopniowo zmieni─ç kolor oka, prze┼é─ůczaj─ůc si─Ö pomi─Ödzy reprezentuj─ůcymi punkty gradientu klasami blue i brown:

 .blue  { stop-color   : lightblue; }
 .brown { stop-color   : brown; }
 stop {
    transition         : all 5s;
    -webkit-transition : all 5s;
    -moz-transition    : all 5s;
 }
Czerwone oczy SVG

Ten znany nam spos├│b nie zadzia┼éa jednak w przypadku atrybut├│w SVG. SVG ma bowiem sw├│j w┼éasny mechanizm (oparty na standardzie SMIL) wykorzystywany do animacji warto┼Ťci atrybut├│w. Z jego pomoc─ů sprawimy, ┼╝e oko spojrzy w bok.

Na pocz─ůtek spr├│bujmy poruszy─ç oko statycznie za pomoc─ů opisanego powy┼╝ej przekszta┼écenia translate():

<circle id="eyeball" cx="100" cy="100" r="50" transform="translate(50,0)"/>
Zez w prawo

Zdecydowanie nie o to nam chodzi┼éo. Powy┼╝szy kod przesun─ů┼é ca┼é─ů ga┼ék─Ö oczn─ů, ┼é─ůcznie ze ┼Ťcie┼╝k─ů odci─Öcia, zza kt├│rej grafika ma by─ç generowana. Spr├│bujmy zamiast tego zmodyfikowa─ç atrybut cx, kt├│ry okre┼Ťla pozycj─Ö ┼Ťrodka ko┼éa:

<circle id="eyeball" cx="150" cy="100" r="50"/>
Poprawiony zez w prawo

Tak jest o wiele lepiej. Teraz przypiszmy atrybutowi cx jego pierwotn─ů warto┼Ť─ç. Aby oko porusza┼éo si─Ö dynamicznie, musimy umie┼Ťci─ç element animate wewn─ůtrz obiektu eyeball, kt├│rego atrybut chcemy zmodyfikowa─ç:

<circle id="eyeball" cx="100" cy="100" r="150" >
    <animate
        id             = "glanceStart"
        attributeType  = "XML"
        attributeName  = "cx"
        begin          = "1s"
        dur            = "0.5s"
        from           = "100"
        to             = "150"
    />
</circle>

Przyjrzyjmy się działaniom poszczególnych atrybutów:

  • attributeType zaznacza, ┼╝e warto┼Ť─ç, kt├│r─ů manipulujemy jest atrybutem XML, a nie w┼éasno┼Ťci─ů CSS.
  • attributeName zawiera nazw─Ö atrybutu, kt├│ry ma zosta─ç zmodyfikowany — w tym przypadku jest to cx.
  • begin okre┼Ťla czas, po jakim zostanie wykonana animacja — w tym przypadku jest to 1 sekunda. Przypisanie warto┼Ťci 0s wywo┼éa animacj─Ö natychmiast. (Je┼Ťli pominiesz ten atrybut, animacja nie zostanie wykonana domy┼Ťlnie, jednak mo┼╝esz ni─ů sterowa─ç przy pomocy JavaScriptu w opisany poni┼╝ej spos├│b).
  • dur okre┼Ťla czas trwania animacji — w tym przypadku jest to p├│┼é sekundy.
  • from i to okre┼Ťlaj─ů warto┼Ťci, w obr─Öbie kt├│rych ma nast─ůpi─ç przej┼Ťcie animacji. W tym przypadku ga┼éka oczna zostanie przesuni─Öta o 50 jednostek w prawo.

Jak wida─ç, w wyniku animacji oko porusza si─Ö w prawo, po czym natychmiastowo wraca do swojej pierwotnej pozycji. Mo┼╝emy w tym miejscu skorzysta─ç z atrybutu fill, nosz─ůcego niestety tak─ů sam─ů nazw─Ö co w┼éasno┼Ť─ç fill, nadaj─ůca kszta┼étom kolory i gradienty. Dodanie atrybutu fill i przypisanie mu warto┼Ťci freeze sprawi┼éoby, ┼╝e oczy by┼éyby skierowane w bok tak┼╝e po zako┼äczeniu animacji. Skorzystamy jednak z innej opcji —ÔÇö dodamy jeszcze inn─ů animacj─Ö, dzi─Öki kt├│rej oczy wr├│c─ů do swojej pierwotnej pozycji:

<circle id="eyeball" cx="100" cy="100" r="150" >
    <animate
        id             = "glanceStart"        attributeType  = "XML"
        attributeName  = "cx"
        begin          = "1s"
        dur            = "0.5s"
        from           = "100"
        to             = "150"
    />
    <animate
        id             = "glanceEnd"        attributeType  = "XML"
        attributeName  = "cx"
        begin          = "glanceStart.end"        dur            = "0.5s"
        from           = "150"        to             = "100"    />
</circle>

Opr├│cz odwr├│cenia warto┼Ťci from i to czas rozpocz─Öcia animacji glanceEnd uzale┼╝niony jest od momentu, w kt├│rym ko┼äczy si─Ö poprzednia animacja glanceStart. Zauwa┼╝, ┼╝e poza sk┼éadni─ů url() i xlink:href, za pomoc─ů kt├│rych tworzyli┼Ťmy odniesienia do obiekt├│w, skorzystamy teraz z trzeciego rodzaju sk┼éadni — sk┼éadni kropkowej, kt├│ra umo┼╝liwi nam odniesienie si─Ö do identyfikatora glanceStart i warto┼Ťci jego atrybutu end.

Mruganie

Tak jak w przypadku przej┼Ť─ç i animacji CSS, w SVG mo┼╝esz animowa─ç praktycznie ka┼╝d─ů warto┼Ť─ç numeryczn─ů czy kolor. Ponadto masz mo┼╝liwo┼Ť─ç animacji z┼éo┼╝onych serii wsp├│┼érz─Ödnych wykorzystanych w definicjach ┼Ťcie┼╝ek. Jedyny warunek to zgodno┼Ť─ç polece┼ä dotycz─ůcych ┼Ťcie┼╝ek — w├│wczas mamy pewno┼Ť─ç, ┼╝e istniej─ů koresponduj─ůce ze sob─ů punkty, bez kt├│rych oczy nie b─Öd─ů mog┼éy mruga─ç. Dodaj poni┼╝szy kod do obiektu eyelids:

<path id="eyelids" d="M 200,100 Q 100,200 0,100 Q 100,0 200,100">
    <animate        id            = "blink"        attributeType = "XML"        attributeName = "d"        from          = "M 200,100 Q 100,200 0,100 Q 100,0 200,100"        to            = "M 200,100 Q 100,100 0,100 Q 100,100 200,100"        begin         = "4s; 6s; 8s; 9s; 11.5s; 13s"        dur           = "0.1s"    /></path>

Atrybut begin okre┼Ťla w powy┼╝szym kodzie kilka r├│┼╝nych warto┼Ťci, zatem animacja zostanie wykonana kilkakrotnie. Pomi─Ödzy warto┼Ťciami to i from modyfikowane s─ů jedynie pozycje dw├│ch punkt├│w kontrolnych, kt├│re wp┼éywaj─ů na kszta┼ét krzywej. Animacja zachowuje si─Ö wi─Öc w poni┼╝szy spos├│b:

Wi─Öksz─ů kontrol─Ö animacji umo┼╝liwi ci JavaScript. Aby z niego skorzysta─ç, wywo┼éaj metod─Ö beginElement() na obiekcie animacji. W tym przyk┼éadzie przesuni─Öte zostaj─ů wsp├│┼érz─Ödne x/y punktu, w stron─Ö kt├│rego pada spojrzenie. Dodajemy tak┼╝e opcjonalny parametr dur, kt├│ry reguluje czas animacji:

function glanceTo(x,y,dur) {
    dur = (dur || 0.5) + 's'; // Przypisanie trzeciego parametru albo zastosowanie warto┼Ťci domy┼Ťlnehj
    var toThere = document.querySelector('#glanceStart');
    var andBack = document.querySelector('#glanceEnd');
    toThere.setAttribute("to", x + " " + y);
    andBack.setAttribute("from", x + " " + y);
    toThere.setAttribute("dur", dur);
    andBack.setAttribute("dur", dur);
    toThere.beginElement();
}

Po wywo┼éaniu poni┼╝szej funkcji oczy b─Öd─ů mruga─ç nieregularnie, w dwu- do pi─Öciosekundowych odst─Öpach:

function blink() {
    var minDelay = 2000;
    var maxExtraDelay = 3000;
    document.querySelector('#blink').beginElement();
    setTimeout(blink, (Math.floor(Math.random() * maxExtraDelay) + minDelay));
}

Interaktywne powi─Ökszenie

Za┼é├│┼╝my, ┼╝e chcia┼éby┼Ť m├│c powi─Ökszy─ç grafik─Ö poprzez klikni─Öcie. Skalowanie ga┼éki ocznej b─Ödzie problematyczne, poniewa┼╝ teraz mamy dwoje oczu, kt├│re mog┼éyby ze sob─ů kolidowa─ç. Mo┼╝emy natomiast zmieni─ç wymiary obecnego elementu viewBox, aby powi─Ökszy─ç wybrany obszar.

Odno┼Ťniki, podobnie jak w HTML-u, tworzy si─Ö przy u┼╝yciu elementu a, jednak atrybut xlink:href musi by─ç przyporz─ůdkowany do odpowiedniej przestrzeni nazw. Ponadto element a nie mo┼╝e opakowa─ç zwyk┼éego tekstu, a jedynie inne elementy SVG. Ten link zawiera kontener dla obydwu oczu, zatem klikni─Öcie kt├│regokolwiek z nich aktywuje odno┼Ťnik:

<a xlink:href="#zoomIn">
  <use xlink:href="#eyes"/>
</a> [en]
<view id="zoomIn" viewBox="100 50 100 100" />

Przy odnoszeniu si─Ö do wewn─Ötrznego elementu view, warto┼Ť─ç elementu svg zostaje nadpisana przez warto┼Ť─ç jego potomka, element viewBox. W rezultacie obszar zostaje powi─Ökszony:

Domy┼Ťlnie ca┼éa ga┼éka oczna jest aktywna, co mo┼╝na jednak odpowiednio skonfigurowa─ç. W dokumencie HTML mo┼╝na decydowa─ç czy maj─ů by─ç wykonywane procedury obs┼éugi zdarze┼ä myszy i dotyku dzi─Öki w┼éasno┼Ťci pointer-events — jest to przydatne w przypadku wielowarstwowych interfejs├│w. W SVG jako obszar aktywny dla obs┼éugi tych zdarze┼ä mo┼╝na wyznaczy─ç zar├│wno wn─Ötrze (visibleFill) jak i obrys (visibleStroke) grafiki (albo obie te cz─Ö┼Ťci na raz, dzi─Öki warto┼Ťci visiblePainted). Stan─ů si─Ö one aktywne je┼Ťli widoczno┼Ť─ç grafiki b─Ödzie w┼é─ůczona (visible) oraz gdy zdefiniowane zostan─ů warto┼Ťci fill i stroke. (W odr├│┼╝nieniu od visibility:hidden ustawienie display:none ca┼ékowicie blokuje generowanie elementu).

Tak jak w HTML-u, w┼éasno┼Ť─ç cursor pozwala na wyr├│┼╝nienie aktywnych obszar├│w:

#eyelid:hover, .eyelids:hover { cursor: pointer; }
#eyelid, .eyelids { pointer-events: visiblePainted; }

Powi─Ökszenie animowane

Istniej─ů dwa problemy z nawigacj─ů opisanego powy┼╝ej powi─Ökszenia uzyskanego za pomoc─ů elementu a. Po pierwsze dzia┼éa ono tylko w plikach SVG, za┼Ť nie w przypadku SVG osadzonego w kodzie HTML. Po drugie powi─Ökszenie wykonywane jest gwa┼étownie — tak samo jak dzia┼éa przewijanie strony HTML za pomoc─ů elementu a.

Obydwa problemy mo┼╝emy rozwi─ůza─ç poprzez przedefiniowanie domy┼Ťlnej nawigacji. Na pocz─ůtek zdefiniujmy alternatywne elementy docelowe view oraz element animate, co pozwoli nam na prze┼é─ůczanie si─Ö pomi─Ödzy atrybutem viewBox ka┼╝dego elementu docelowego. Przypisanie atrybutowi fill warto┼Ťci freeze umo┼╝liwi zachowanie danego stopnia powi─Ökszenia po zako┼äczeniu animacji:

<view id="zoomOut" viewBox="0 0 600 200" />
<view id="zoomIn" viewBox="100 50 100 100" />

<animate
   id            = "zoomNav" 
   attributeType = "XML"
   attributeName = "viewBox" 
   fill          = "freeze" 
   />
 
<a class="zoom" xlink:href="#zoomIn">
  <use xlink:href="#eyes"/>
</a>

W poni┼╝szym kodzie JavaScript wywo┼éujemy funkcj─Ö preventDefault(), kt├│ra dezaktywuje domy┼Ťln─ů nawigacj─Ö odno┼Ťnik├│w o klasie zoom. Zamiast tego wykonana zostanie animacja zmodyfikowana w oparciu o atrybut viewBox docelowego elementu:

var animate, svg; // odwo┼éuj─ů si─Ö do element├│w SVG
var duration = '3s';
<img src="/wp-content/uploads/24svgGrandTour_eyeball_textPath.png" alt="24svgGrandTour_eyeball_textPath" width="250" height="246" class="aligncenter size-full wp-image-13323" />
window.onload = function() {
    svg = document.querySelector('svg');
    animate = document.querySelector('#zoomNav');
    animate.setAttribute('dur', duration);
    animate.setAttribute('from', svg.getAttribute('viewBox'));
    animate.setAttribute('to', svg.getAttribute('viewBox'));
    var links = document.querySelectorAll('a.zoom');
    for (var i = 0, l = links.length; i < l; i++) {
        // zast─Öpuje domy┼Ťln─ů nawigacj─Ö:
        links[i].setAttribute('onclick', 'event.preventDefault()');
        links[i].addEventListener('click', zoomNav);
    }
};
<img src="/wp-content/uploads/25tekst.png" alt="25tekst" width="278" height="280" class="aligncenter size-full wp-image-13324" />
function zoomNav(e) {
    var hash = e.currentTarget.getAttribute('xlink:href');
    var tgt = document.querySelector(hash);
    // wymienia warto┼Ťci to/from:
    animate.setAttribute('from', animate.getAttribute('to'));
    animate.setAttribute('to', tgt.getAttribute('viewBox'));
    // wykonuje animacj─Ö:
    animate.beginElement();
    // stosuje klas─Ö CSS, aby ┼éatwiej dostosowa─ç wy┼Ťwietlanie animacji na ka┼╝dym poziomie powi─Ökszenia:
    svg.className = hash.replace(/#/,'');
}

W ostatniej linijce kontenerowi svg zostaj─ů przypisane r├│┼╝ne klasy. Wykorzystamy je, aby dostosowa─ç wy┼Ťwietlanie opisanych poni┼╝ej element├│w tekstowych przy r├│┼╝nych poziomach powi─Ökszenia.

Dodawanie tekstu

W SVG mo┼╝esz ┼é─ůczy─ç grafiki z tekstem, jednak nie zostanie on opakowany wewn─ůtrz pola tak jak ma to miejsce w HTML-u. Zazwyczaj korzysta si─Ö z wsp├│┼érz─Ödnych x i y, aby umie┼Ťci─ç elementy tekstowe bezpo┼Ťrednio w obr─Öbie grafiki. Poni┼╝szy przyk┼éad obrazuje typowe dla SVG umiejscowienie tekstu wzd┼éu┼╝ krzywej:

Element circle jest pozornie opakowany przez element tekstowy. W przypadku k├│┼é trudno jednak wyznaczy─ç punkt pocz─ůtkowy i ko┼äcowy, zatem b─Ödziemy musieli skorzysta─ç z do┼Ť─ç skomplikowanego polecenia path. W ten spos├│b narysujemy dwie elipsowate, wygi─Öte w ┼éuk krzywe, kt├│re b─Öd─ů do siebie naprzeciwleg┼ée, a ich punkty pocz─ůtkowe i ko┼äcowe znajdowa─ç si─Ö b─Öd─ů przy lewej kraw─Ödzi:

<path id="irisPath" d="M 60,100 A 40,40 0 0 1 140,100 A 40,40 0 0 1 60,100 "/>
<path id="pupilPath" d="M 78,100 A 22,22 0 0 1 122,100 A 22,22 0 0 1 78,100"/>

Dodamy teraz referencj─Ö do obiektu labels oraz innych komponent├│w oka:

<g id="eye">
  <use xlink:href="#eyelids" class="eyelashes" />
  <use xlink:href="#eyelids" class="eyelids"   />
  <use xlink:href="#eyeball" class="eyeball"   />
  <use xlink:href="#labels"/>
</g>

Element textPath sprawi, ┼╝e cz─Ö┼Ť─ç tekstu b─Ödzie wy┼Ťwietlana wzd┼éu┼╝ ┼Ťcie┼╝ki:

<g id="labels">
  <text>
    <textPath xlink:href="#irisPath">T─Öcz├│wka</textPath>
  </text>
  <text>
    <textPath xlink:href="#pupilPath">┼╣renica</textPath>
  </text>
</g>
z tekstem

Zwykle mo┼╝esz przesun─ů─ç tekst wzd┼éu┼╝ ┼Ťcie┼╝ki poprzez zwi─Ökszanie atrybutu startOffset elementu textPath. Mo┼╝esz tak┼╝e, tak jak w tym przypadku, obr├│ci─ç okr─ůg┼éy obiekt korzystaj─ůc z przekszta┼éce┼ä CSS:

#irisPath, #pupilPath {
    fill             : none;
    transform-origin : center         ; -webkit-transform-origin : center  ; -moz-transform-origin : center;
}
#irisPath {
    transform        : rotate(120deg) ; -webkit-transform : rotate(120deg) ; -moz-transform : rotate(120deg);
}
#pupilPath {
    transform        : rotate(20deg)  ; -webkit-transform : rotate(20deg)  ; -moz-transform : rotate(20deg);
}

Domy┼Ťlnie przekszta┼écenia SVG maj─ů sw├│j pocz─ůtek w lewym g├│rnym rogu elementu. Prawid┼éowy obr├│t ko┼éa osi─ůgniesz zatem tylko poprzez przypisanie w┼éasno┼Ťci transform-origin jej domy┼Ťlnej warto┼Ťci w CSS50% 50%. B─ůd┼║ tak┼╝e ostro┼╝ny przy ┼é─ůczeniu przekszta┼éce┼ä, poniewa┼╝ SVG mo┼╝e wej┼Ť─ç w konflikt z CSS.

tekst

Na koniec dodamy kod CSS okre┼Ťlaj─ůcy wygl─ůd podpis├│w, kt├│rych po┼éo┼╝enie przy du┼╝ym powi─Ökszeniu b─Ödzie zale┼╝a┼éo od innych element├│w. W wyniku przekszta┼éce┼ä w┼éasno┼Ťci fill-opacity i stroke-opacity podczas wykonywania animacji tekst b─Ödzie si─Ö pojawia─ç i zanika─ç:

text {
    font-size          : 10px;
    text-transform     : uppercase;
    stroke-width       : 0.5;
    stroke             : black;
    stroke-opacity     : 0;
    fill               : red;
    fill-opacity       : 0;
    /* przej┼Ťcie na domy┼Ťlny poziom powi─Ökszenia zajmuje 2 sekundy; brak op├│┼║nienia */
    transition         : all 2s;    -webkit-transition : all 2s;    -moz-transition    : all 2s;
}
 
svg.zoomIn text {
    fill-opacity       : 0.25;
    stroke-opacity     : 0.25;
    /* przej┼Ťcie na du┼╝e powi─Ökszenie zajmuje 2 sekundy, 2 sekundy op├│┼║nienia */
    transition         : all 2s 2s;    -webkit-transition : all 2s 2s;    -moz-transition    : all 2s 2s;
}

Litery zachowuj─ů si─Ö identycznie jak ┼Ťcie┼╝ki, zatem w┼éasno┼Ťci fill i stroke b─Öd─ů funkcjonowa─ç tak samo jak w przypadku innych kszta┼ét├│w.

Masz ju┼╝ dosy─ç?

zm─Öczone oczy

Autor: Mike Sierra

Źródło: http://docs.webplatform.org/wiki/svg/tutorials/smarter_svg_overview

Tłumaczenie: Joanna Liana

Tre┼Ť─ç tej strony jest dost─Öpna na zasadach licencji CC BY 3.0

2 komentarze do “Grafika SVG ÔÇö obszerny przewodnik”

  1. Genialny poradnik, naprawd─Ö super, ┼╝e kto┼Ť to tak fajnie opisa┼é. ­čÖé

  2. przebrnelam przez kawalek… niestety przy kolejnych liniach kodu nie wiem co usuwa─ç, co gdzie wstawiac ­čÖü

Mo┼╝liwo┼Ť─ç komentowania zosta┼éa wy┼é─ůczona.