SVG animace pomocí CSS a Snap.svg

Dnes jsem se s vámi chtěl podělit o snadnost animace v SVG pomocí nástrojů, které máme dnes k dispozici. SVG je stále jedním z těch předmětů, které mohou vyděsit mnoho vývojářů, ale jsem tu, abych vám ukázal, jak snadné to může být.

Při běžném hledání inspirace na internetu jsem narazil na tento skvělý příspěvek na From Up North. Jak můžete vidět, jsou tam krásné ilustrace od Miguela Sousy aka Heymikela, Snack Studio, Martína Feijoó &Sary Enríquez. Jakmile jsem uviděl animace, první věc, která mě napadla, bylo, jak by se z nich dalo udělat SVG animace, které by se daly volně používat na internetu.

Zobrazit ukázky

Pojďme na to

Existuje několik různých implementací, některé se více zaměřují na stránku věcí Snap.svg a pak také kombinují animace Snap.svg a CSS.

Příprava

První část jakéhokoli projektu SVG by měla být věnována optimalizaci vašich SVG, doufejme, že ve vašem případě jste je buď vytvořili sami, nebo si od svého návrhářského týmu necháte dodat pěkné SVG. Nyní spíše než vkládat rozsáhlé téma optimalizace do tohoto konkrétního článku. Doporučujeme vám přečíst si tento článek od Sara Soueidan, který má několik skvělých nástrojů, které vám pomohou.

Největší věcí, díky které bude práce s SVG hračkou, je být velmi organizovaný s vrstvením a seskupováním. Odstraňte všechny nepoužívané vrstvy a seskupte prvky, o kterých si myslíte, že budou v animaci propojeny.

Nastavení Snap.svg

Krása Snap.svg spočívá v tom, že se snadno nastavuje a poskytuje nám obrovské množství funkcí pro manipulaci s našimi SVG. Prvním krokem je zahrnout jej do našeho projektu; existuje mnoho způsobů, jak toho dosáhnout, které můžete najít zde

Inicializovat prvek Snap SVG

// HTML
<svg class="hill-valley article"></svg>

// JS
var s = new Snap('.hill-valley');

Načíst naše externí SVG

Snap.load('img/hill-valley.svg', function (response) {
    var hillValley = response;
    s.append(hillValley);
});

Animace města Gotham

Nejlepší věc, kterou musíte udělat se všemi svými animacemi, je udělat krok zpět a přemýšlet o tom, jak přesně animaci implementujete a co se musí stát.

Takže u této konkrétní animace probíhají dvě hlavní animace, jedna je animace 'netopýřího světla', která, když se pozorně podíváte, také aplikuje ořezovou masku na text. Druhým je animace rozsvícení scény ve vztahu k rozsvícení a blikání „netopýřího světla“.

Animace osvětlení scény

Chtěli jsme vám ukázat, jak snadné je stále používat CSS animace k manipulaci s prvky SVG, proto jsme se rozhodli pro osvětlení této animace, které by bylo perfektní ukázat.

Přidáváme třídy pouze k cestám, se kterými chceme manipulovat, a pak už jen vytváříme animace klíčových snímků. V níže uvedeném příkladu kódu to udělám pouze pro WebKit, ale měli byste se ujistit, že máte všechny správné předpony dodavatele.

.gotham__background {
  -webkit-animation: background-anim 7s infinite linear;  
}

@-webkit-keyframes background-anim {
  0%, 10%, 
  21%, 23%,
  25%, 27%,
  37%, 55%,
  57%, 61%,
  63%,
  80%   { fill: #333738; }
  11%, 20%, 22%, 24%, 28%, 36%, 38%,
  54%, 56%, 58%, 62%, 64%,
  79% { fill: #6D6C6D; }
}

Animace netopýřího světla

Ústředním prvkem animace na pozadí je, že plně využíváme ořezové masky SVG. To znamená, že můžeme zobrazit text titulku, jakmile přes něj přejede naše cesta klipu. Samotná animace je docela jednoduchá; je to jen otáčení a posouvání současně. Využíváme výhod dostupných algoritmů náběhu vestavěných do snap.svg. Pro více informací o tom, co tyto algoritmy dělají, se podívejte na CodePen, které jsem vytvořil zde.

Abychom vytvořili ořezovou masku v SVG, musíme se ujistit, že naše cesta je definována v prvku SVG ořezové cesty a je k ní připojeno ID. Poté aplikujeme atribut 'clip-path' na prvek, který chceme maskovat, a tím se nastaví ořezová cesta. Zde je, jak tento kód vypadá:

<clipPath id="b">
    <use xlink:href="#a" overflow="visible"/>
</clipPath>

<g clip-path="url(#b)"></g>

Pojďme si tuto animaci seřadit:

// rotateElems is the element we wish to rotate
rotateElems = s.selectAll('.gotham__rotate')

rotateElems.animate({
    transform: 'r0,250,453 s1,1'
}, 1500, mina.elastic);
Zobrazit ukázku

King Landing

Animace Kings Landing má několik malých triků, aby některé animace působily realističtěji. Budeme se jim věnovat dále, nyní se podívejme, jak jsme vytvořili cloudovou animaci a pomocí snap.svg dynamicky přidávali další prvky.

Animace Clouds

Krása SVG je v tom, že nám umožňuje rychle znovu použít prvky. Ať už se jedná o skupinu, cestu nebo tvar, může vám dokonce umožnit odkazovat na externí zdroje (externí zdroje mají nižší podporu prohlížeče). Můžeme toho dosáhnout pomocí use element, toto jednoduše odkazuje na jiné objekty na stránce pomocí xlink:href atribut.

Jedna věc, kterou je třeba poznamenat, pokud máte atributy výplně nebo tahu na původní cestě, budou také na každém prvku bez ohledu na to, co jste definovali v prvku použití. Pokud tedy chcete znovu použít části a mít na nich jednotlivé atributy, pak je lepší na svůj hlavní prvek nepoužít žádné a aplikovat pouze na jednotlivé prvky.

Protože se chystáme animovat mraky v různých velikostech a pozicích, je lepší nechat tento proces spravovat snap.svg, než aby byl pevně zakódován do SVG. Vše, co děláme v SVG, je vytvořit naši cloudovou cestu, která bude zkopírována pomocí prvku 'use'.

Následující vytvoří definované množství mraků v náhodném rozložení s libovolným měřítkem:

var containerHeight = s.node.offsetHeight / 4,
    numberOfClouds = 10;

  // Gets the width of the container
  cloudWidth = s.select('#a').getBBox().w;

  // Creates a group element
  clouds = s.g();

  // Loop to create clouds
  for (var i = numberOfClouds; i >= 0; i—) {
    /** 
    x is a random number between 0 and the container width
    y is a random number between 0 and our container height
    newCloud creates a use element referencing our cloud path
    randomScale is a random number between 0.2 and 0.9
    **/
    var x = Math.floor(Math.random() * cloudWidth),
        y = -Math.floor(Math.random() * containerHeight),
        newCloud = cloud.use(),
        randomScale = Math.random() * (0.9 - 0.2) + 0.2;

    // Applies our new attributes to the use element
    newCloud.attr({
      x: x,
      y: y,
      transform: 's' + randomScale
    });

    // Adds the use element to our group
    clouds.add(newCloud);
  }

Animace podél cesty

Jedna věc, kterou snap.svg hned po vybalení nedělá, je poskytnout metodu, která vám umožní animovat přes konkrétní cestu. Není to velký problém, i když můžeme využít metodu Snap.animate, což nám umožňuje manipulovat s animací snímek po snímku.

Vše, co nyní musíme udělat, je najít cestu, kterou chceme animovat. Potom s trochou kódu získejte jeho body v každém snímku animace a aplikujte je na animovaný prvek. Zde je funkce:

/**
  path is the path we wish with to animate along
  element is the element we want to animate
  start is the frame we wish to start the animation on
  dur is the duration in milliseconds
  callback is a function we wish to call once the animation has finished
**/
animateAlongPath = function (path, element, start, dur, callback) {
  // Get the path length, so we know how many frames we will animate over
  var len = Snap.path.getTotalLength(path);

  Snap.animate(start, len, function (value) {
    // movePoint gets the path attributes at a certain frame
    var movePoint = Snap.path.getPointAtLength(path, value);

    // applies the attributes to our element
    element.attr({ cx: movePoint.x, cy: movePoint.y });
  }, dur, mina.easeinout, function () {
    callback(path);
  });
};
Zobrazit ukázku

Hill Valley

Animace pro Hill Valley SVG má čtyři hlavní komponenty, u této konkrétní animace použijeme náběhové algoritmy poskytované Snap.svg.

Animace auta

Tato animace je pouze jednoduchým překladem kombinovaným s rotací. Jediná věc, která to dělá složitějším, je zmírnění; může se zdát, že je obtížné toho dosáhnout.

/**
  car is our SVG car element
  carStartMatrix and carMidMatrix initialises our Snap Matrix
**/

var car = s.select('.car'),
  carStartMatrix = new Snap.Matrix(),
  carMidMatrix = new Snap.Matrix();

// Sets up the matrix transforms
carStartMatrix.rotate(10);
carStartMatrix.translate(0,-50);
carMidMatrix.rotate(-15);
carMidMatrix.translate(300,-20);

car.animate({
  transform: carStartMatrix
}, 1250, mina.easeinout, function () {
  car.animate({
    transform: carMidMatrix
  }, 250);
});

Animace stromu

Stromová animace je dvoudílná animace otáčení, aby se během animace dosáhlo více realistického ohybu. Pokud by listy měly stejnou barvu, mohli jsme pro animaci použít transformaci cesty, ale v našem případě byla dvoudílná animace lepší volbou.

Je to docela jednoduchý koncept, vše, co děláme, je oživit celý strom o malé množství a pak současně animovat listy stromu. Můžeme také plně využít vynikajících algoritmů uvolňování, které do něj snap.svg zabudoval. Zde je návod, jak toho dosáhnout:

/**
  leaves are the leaves element we want to rotate
  leavesDim is the bounding box dimension of leaves
  tree is the tree element we want to rotate
  treeDim is the bounding box dimension of the tree
**/
var leaves = s.select('leaves'),
  leavesDim = leaves.getBBox();

leaves.animate({
  transform: 'r25,' + (leavesDim.x + (leavesDim.width / 2)) + ',' + (leavesDim.y + leavesDim.height)
}, 20, mina.easeinout, function (){

  // This animation triggers once the other has finished
  leaves.animate({
    transform: 'r0,' + (leavesDim.x + (leavesDim.width / 2)) + ',' + (leavesDim.y + leavesDim.height)
  }, 1000, mina.elastic);
});

tree.animate({
  transform: 'r8,' + (treeDim.x + (treeDim.width / 2)) + ',' + (treeDim.y + treeDim.height)
}, 20, function () {

  // This animation triggers once the other has finished
  tree.animate({
    transform: 'r0,' + (treeDim.x + (treeDim.width / 2)) + ',' + (treeDim.y + treeDim.height)
  }, 1000, mina.elastic);
});

Animace hodin

Animace hodin je poměrně přímočará operace. Jediná věc, na kterou musíte být opatrní při otáčení, je to, že pokud se otočí o 360 stupňů nebo více, použije se další otočení; animace půjde špatným směrem.

Můžete to vidět v naší následující animační rutině, vezměme to tak, že tento řádek kódu je volán ve smyčce. Jak můžete vidět, resetovali jsme otočenou transformaci, takže animace se neustále resetuje.

var s.select('.minute');

// Resets to 0
clockMinute.transform('r0,195.5,105.5');

// Animates 360 degrees around the point 195.5,105.5 over 1250 ms
clockMinute.animate({
    transform: 'r90,195.5,105.5'
},1250)

Animace textu

Struktura animace textu je poměrně přímočará; pouze vytvoříme pět prvků „použití“, které odkazují na hlavní text. Poté ve frontě spustíme animaci, která převede všechny prvky lineárně do pravého horního rohu počátečního textového prvku.

/**
    textiles selects all of the .text elements, this is stored as an array
    amount is the max translation value divided by text elements on the page
**/
var textElems = s.selectAll('.text'),
        amount = 20/textElems.length;

// Loops through each element
for (var i = 1; i < textElems.length; i++) {

    // Initiates the animation to translate to the correct position
    textElems[i].animate({
        'transform': 't' + (amount * i) + ',-' + (amount * i)
    }, 200, mina.easeinout);
};
Zobrazit ukázku

Doufejme, že vám to dalo trochu nahlédnout do toho, jak snadné je animovat SVG a vytvářet pozoruhodné snímky. Máte-li jakékoli dotazy, neváhejte nás kontaktovat prostřednictvím všech níže uvedených odkazů. Úžasná věc na animaci SVG je, že bude šťastně fungovat ve všech moderních prohlížečích a IE9 výše. Jak uvidíte v mých animacích výše, kde používám animaci klíčových snímků CSS, můžete k tomu použít pouze snap.svg.