„Az összevisszaságban találd meg az egyszerűséget, a hangzavarban a harmóniát...”

Zászló effekt CSS-sel

A cikkhez kapcsolódó élő demómat lásd itt: DEMO

Azt veszem észre, hogy egyre többször alkalmazok CSS animációkat javaScriptes megoldások helyett. Rengeteg előnye van. Egyrészről roppant egyszerű a transzformációkat megadni. Másrészről nem kell a requestAnimationFrame-mel bajlódni, a böngésző ezeket a transzformációkat alapból a megfelelő időzítéssel rajzolja, a GPU-t is segítségül hívja, így elérhető a teljesen folyékony 60 FPS is.

Ma egy olyan gondolat támadott rám, hogy hogyan lehetne zászló fodrozódás effektet csinálni kizárólag CSS adta segítségekkel. A <canvas> adná magát a feladathoz, de ezúttal pusztán a CSS3 erejében bízva zuhantam neki a feladatnak. A feladat definiálásakor az alap elvárásom mindössze annyi volt, hogy a zászló képe hullámozzon. Menet közben aztán - ahogyan az lenni szokott - megjött az étvágy, és már fénylenie is kellett a kilibbenő részeknél, valamint árnyéknak is szükségét láttam a jobb térbeli élményhez.

CSS zászló fodrozódásFodrozódás. Ez az alap animáció szükséges, sőt, már elégséges feltétel a megfelelő érzet keltéséhez. Az ötletem az volt, hogy sok kis egyenlő oszlopra bontom a zászló téglalapját, és az egyes részeket külön-külön mozgatom szinuszosan fel-le. Egy zászló képe esetemben egy 450x300 pixeles téglalap. Kezdetnek 10 részre osztottam, ez 10 darab 45 pixeles oszlopot, vagyis DIV-et jelentett egymás mellett. Mindegyik szelet megkapta háttérképként a zászló képét, viszont a háttér pozícióját 45 pixellel eltolva az előtte állóhoz képest. Lássuk az ide vonatkozó kódokat. A HTML-ben semmi nincs, csak 10 darab üres, attribútum nélküli DIV:

<section id="flag">
<div></div>
<div></div>
<div></div> ...
</section>

A CSS esetében definiáltam az összes oszlopra egységesen vonatkozó szabályokat, majd külön-külön az egyes szeletekét:

div {
float: left;
position: relative;
width: 45px;
height: 300px;
background-image: url(flags/hu.png);
background-repeat: no-repeat;
background-position: 0px 0px;
}
div:nth-child(2) { background-position: -45px 0px }
div:nth-child(3) { background-position: -90px 0px }
div:nth-child(4) { background-position: -135px 0px }
...

Így egyben látjuk a zászló képét, de tudjuk, hogy valójában 10 részből áll, és az egyes részek külön-külön kezelhetők. Nincs más hátra, mint fel-le mozgatni ezeket a részeket, úgy, hogy az egymást követő oszlopok animációját picit elcsúsztatjuk időben. A keyframes megadása:

@keyframes wave {
0% { top: 0px }
50% { top: 50px }
100% { top: 0px }
}

Megjegyzendő, hogy a -webkit prefixes változatokat is meg kell adni, nehogymár egységes legyen (lásd a demó forrását). Az animáció lényege, hogy az oszlopok a körülölelő téglalap tetején kezdik az életüket, az animáció felénél 50 pixellel lejjebb érnek, majd az animáció végére ismét elérik a tetőpontot. Egészítsük ki ezzel az animációval a zászlóelemek CSS leírását:

div {
... animation: wave 2s ease-in-out infinite;
}

Megadtuk, hogy 2 másodperc alatt fusson le az animáció, legyen végtelenített, illetve az ease-in-out karakterisztikát választottuk a jobb, szinuszosabb zászlóhatás kedvéért. Már mozog a zászlónk, de minden eleme egyszerre ugyanúgy, tehát a komplett zászló megy fel-le. Toljuk el időben az egyes szeletek animációját a hullám kialakításához!

div:nth-child(2) { animation-delay: -0.22s }
div:nth-child(3) { animation-delay: -0.44s }
div:nth-child(4) { animation-delay: -0.66s }
...

A negatív előjel azért szükséges, mert ellenkező esetben annyi késéssel indulna be az animáció és a zászló később beinduló részei állva maradnának induláskor. Így viszont visszamentünk a múltba, vagyis mintha az oldalbetöltés előtt indítottuk volna be az animációt, és már annyival előrébb járna. Ezzel meg is vagyunk, gyönyörű szépen fodrozódik a zászló. Hátradőlve nyugtázhatnánk sikerünket, de a valósabb zászlóhatás eléréséhez akad még a CSS3 tarsolyában pár eszköz.

CSS zászló fényesítésFényesítés. Ennek lényege, hogy a besüppedő, tehát a képernyőn lentebb lévő szeleteket sötétítjük attól függően hogy mennyire vannak lent. Roppant egyszerű lesz ezt megadni, hisz ez épp egybeesik az animációnk top értékének változásával:

@-webkit-keyframes wave {
0% { top: 0px; -webkit-filter: brightness(1) }
50% { top: 50px; -webkit-filter: brightness(.7) }
100% { top: 0px; -webkit-filter: brightness(1) }
}

Itt azért a -webkit prefixes változatot szerepeltetem, mert a filterek egyelőre csak ott működnek. Minél fentebb van egy oszlop, annál jobban közelít a fényessége az eredetihez. A leginkább lent (vagyis hátul) lévő rész az eredeti fényerő 70%-án él. Most már hullámzik és fénylik is a zászló.

CSS zászló árnyékÁrnyék. Remek térhatást érünk el, ha árnyékot rakunk a zászlónk alá. Na nem kell egy Gravity élményre gondolni, de azért megdobja az összképet. Az árnyék távolsága és peremélessége is a magasságtól függ, így szintén az animáció definícióját bővítjük vele:

@keyframes wave {
0% { top: 0px; box-shadow: 20px 20px 20px black }
50% { top: 50px; box-shadow: 1px 1px 1px black }
100% { top: 0px; box-shadow: 20px 20px 20px black }
}

Tesztjeim alatt azt tapasztaltam, hogy a hozzáadott box-shadow, de főleg a fényesítés rendkívüli módon megfogja a gépet, ezeket bekapcsolva már távolról sem nevezném folyékonynak az animációt.

A fenti példában a jobb elképzelhetőség kedvéért a 10 részre osztást említettem. De belátható, hogy minél több szeletet használunk, annál finomabb lesz a végeredmény. A legszebb hatás érdekében értelmezhetjük úgy a feladatot, hogy a 450 pixel széles kép esetén 450 részre bontjuk azt. Tehát minden egyes pixelsáv külön oszlopot képvisel és külön lesz vezérelve. A demóban állíthatóvá tettem a szeletek számát is, ahol gyönyörűen látszik, hogy mennyivel jobb zászlóélmény kerekedik ennek növelésével. Trinidad és Tobago zászlója az átlós csík miatt remek példa erre. Persze ez az élményjavulás egy ponton visszafordul, amikor beszaggat a dolog. Én a finomság/szaggatás megfelelő arányát 180 szelet esetében tapasztalom (effektek nélkül), ez persze erősen gépfüggő.

Azt javaslom neked, hogy ha bárminemű animációra van szükséged a weboldaladon, akkor mostantól először a CSS lehetőségei jussanak eszedbe. Csak akkor fordulj javaScript megoldáshoz, ha a feladat nagyon speciális.