Tvoříme slider obrázků pomocí HTML5 canvasu

Na internetu najdete milion a jedna verzí různých sliderů obrázků, které jsou implementovány napříč spektrem technologií a frameworků. Technologie dozrála do doby, kdy k vytvoření slideru nepotřebujete žádné knihovny, jen JavaScript a canvas, jen vanilla JavaScript.

Komu je seriál určen

Tímto článkem začínáme seriál, který má dvě cílové skupiny:

  • ti, kdo chtějí mít pěkný slider obrázků
  • ti, kdo si chtějí něco s canvasem vyzkoušet

Cíl seriálu

Tento mini seriál v pár článcích provede čtenáře tvorbou slideru za pomoci canvasu. Nejprve se podíváme na samotné animace, kde bude brán zřetel hlavně na jednoduchost. Následně z animací sestavíme jednoduchý slider, který bude použitelný na webové prezentaci. Dalším cílem bude přepsání kódu tak, aby mohly animace běžet souběžně.

Implementace

Do psaní jsem se pouštěl s následujícími požadavky:

  • Fade in na začátku
  • Animace pohybu
  • Cross fade mezi obrázky

Canvas

Prvně potřebujeme canvas, tak si jej přidáme do dokumentu.

<canvas id="myCanvas">No canvas MSG.</canvas>

Více informací o canvasu naleznete na MDN: Canvas, nebo zde na zdrojáku: Tag canvas.

Fade in

Fade animace je asi nejznámější. Tento přechod implementují snad všechny JS frameworky, které jsou zaměřeny mimo jiné i na UI. Z tohoto důvodu tímto přechodem začneme. Co to vlastně fade je? Není to nic jiného, než změna průhlednosti v čase.

Abychom mohli tuto animaci provést, potřebujeme znát tři věci: obrázek, canvas a délku animace. Vytvoříme si proto třídu, ve které si připravíme metodu pro nastavení konfigurace. Je to sice pro fade efekt značný overkill, ale s touto animací pouze začínáme.

var Anim = function() {
 this.timeLog = [];
}

Anim.prototype.configure = function(imgUri,animDuration,canvas) {
 this.imgUri = imgUri;
 this.animDuration = animDuration;
 this.canvas = document.getElementById(canvas);
 return;
}

Samotný fade můžeme řešit pomocí property globalAplha, který se nám nabízí v 2d kontextu canvasu. Jak už napovídá název, bude mít něco společného s průhledností. Hned ze začátku tu mám menší varování: property globalAlpha opravdu není globální! (Toho využiji při cross Fade animaci.)

Samotná fade animace je smyčka, s těmito kroky:

  1. Vyčistíme canvas
  2. Nastavíme globalAplha
  3. Vykreslíme obrázek

Vyčištění canvasu

K tomu nám slouží metoda clearRect (mimo jíné), ta očekává 4 parametry : startX,startY,dX,dY.

Nastavení globalAlpha

Zde není co řešit, property je float, prostě jí nastavíme hodnotu v rozmezí 0-1.

Vykreslení obrázku

K vykreslení obrázku do kontextu slouží metoda drawImage(), ta přijímá 9 parametrů:

  • Instance obrázku (html element)
  • 4 parametry pro obrázek (startX,startY,dx,dy)
  • 4 parametry pro kontext (startX,startY,dx,dy)

Řízení běhu

K řízení běhu použijeme funkci requestAnimationFrame, která se postará o volání dalšího kroku animace ve chvíli, kdy je to možné. Jejímu popisu se věnuje MDN.

Internet Explorer implementuje tuto funkci pouze ve verzi 10+, proto si musíme ještě napsat fallback pollyfill pro IE9 a starší. Existuje několik hotových polyfillů, já jsem si ale napsal svůj, z jednoho prostého důvodu: Nechci mít jako parametr dTime vysoké hodnoty unix timestamp, lépe se potom čte průběh animace při debugování. Samozřejmě nesmíme zapomenout na prefixy různých prohlížečů.

function(w){
 "use strict";
 w.requestAnimationFrame = requestAnimationFrame || mozRequestAnimationFrame 
 || webkitRequestAnimationFrame || msRequestAnimationFrame ||
 (function(){
  var startTime = new Date().getTime();
  return function(callback) {
   var time = new Date().getTime();
   var dTime = time - startTime;
   window.setTimeout(callback.bind(callback,dTime),17)
   return;
  }
 }())
}(window))

Číslo 17 není magická kontanta, velká část zobrazovacích zařízení funguje s frekvencí 60Hz, z tohoto důvodu je ideální tick animace 16.6ms, ale setTimeout přijímá integer.

Sestavení kódu

Naše třída má zatím pouze metodu pro předání konfigurace a konstruktor. Nyní si připravíme canvas, nastavíme mu globalAlpha na 0 a načteme si obrázek.

Anim.prototype.run = function() {
 this.ctx = this.canvas.getContext('2d');
 this.ctx.globalAlpha = 0;

 this.img  = new Image();
 this.img.src = this.imgUri;
 this.img.onload = this._beforeFade.bind(this,null);
 return;
}

Předchozí metoda má nastavený callback pro onload event, ten směruje na metodu _beforeFade. Tato metoda zavolá requestAnimationFrame, abychom měli relativní čas začátku animace, a poté spustí samotnou animaci.

Anim.prototype._beforeFade = function(startTime) {
 if(!startTime)
  requestAnimationFrame(this._beforeFade.bind(this))
 this.animStart = startTime;
 requestAnimationFrame(this._fade.bind(this,this._afterFade.bind(this)))
 return;
}

Samotná animace je pak pouze rekurzivní volání metody, která vyčistí canvas, nastaví globalAlpha a vykreslí obrázek.

Anim.prototype._fade = function(callback,dTime) {
 this.timeLog.push(new Date().getTime());

 var alpha = 1/this.animDuration * (dTime - this.animStart);  
 if(alpha > 1) alpha = 1;

 this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
 this.ctx.globalAlpha = alpha;
 this.ctx.drawImage(this.img,0,0,this.img.width,this.img.height
  ,0,0,this.canvas.width,this.canvas.height)
 if(alpha == 1) {
  callback();
 } else {
  requestAnimationFrame(this._fade.bind(this,callback))  
 }
 return;
}

Nakonec se zavolá callback, který vypíše statistiku animace.

Anim.prototype._afterFade = function() {
 var log = document.getElementById("log");
 var tmpl = [];
 var timeLog = this.timeLog;
 tmpl.push("Počet kroků animace: ",timeLog.length,"<br/>");
 tmpl.push("Čas animace v s: ", (timeLog[timeLog.length-1]-timeLog[0])/1000,"<br/>");
 tmpl.push("Prum. fps: ",timeLog.length/((timeLog[timeLog.length-1]-timeLog[0])/1000));
 log.innerHTML = tmpl.join("");
 return;
}

Závěr a zdrojový kód

Tímto máme napsaný jednoduchou fade-in animaci za použití canvasu – podívejte se na výsledný kód. V dalším dílu se podíváme na animaci pohybu a zoomu a vytvoříme nelineární animaci.

Jsem vývojář na volné noze. Programuji v JavaScriptu a v node.js. Občas něco napíši v PHP.

Komentáře: 15

Přehled komentářů

zbyso diky
Ondřej Žára polyfill
Michal Taneček Re: polyfill
Ondřej Žára Re: polyfill
Martin Hassman Re: polyfill
Michal Taneček Re: polyfill
keff Re: polyfill
Miloš Proč canvas
Pavel Lang Re: Proč canvas
Michal Taneček Re: Proč canvas
Pavel A co tablety?
Martin Hassman Re: A co tablety?
Jan Růžička Jen pozor na ten překlep...
Michal Taneček Re: Jen pozor na ten překlep...
Michal Perutka Polyfill - oprava
Zdroj: https://www.zdrojak.cz/?p=10909