Aktuálně: Postihly zákazy tvou profesi? Poptávka po ajťácích prudce roste, využij podzimní akce 30% výuky zdarma!

Lekce 16 - 2D kontext plátna v JavaScriptu

V minulé lekci, Obrázky a kreslení na canvas v JavaScriptu, jsme se věnovali obrázkům a naučili se pracovat s canvasem.

V dnešním JavaScript tutoriálu podrobně podíváme na plátno. Projdeme si postupně všechny jeho možnosti.

Mazání obdélníků

V minulém díle jsme lehce nakousli kreslení obdélníků. Uvedli jsme si metody fillRect() a strokeRect(). K obdélníkům se ještě váže metoda clearRect(), která vymaže obsah plátna na dané ploše. Parametry má stejné jako dvě výše zmíněné metody. Jaký je však rozdíl mezi clearRect() a fillRect(), když nastavíme výplň na bílou? Plátno totiž ve výchozím stavu není bílé, ale je průhledné. Pokud tedy vymažeme plátno pomocí fillRect(), sice uvidíme bílou plochu, ale neuvidíme pozadí plátna a to co bylo pod plátnem.

Obdélník v cestě

Poslední možností jak vykreslit obdélník je vložit ho do cesty. Ačkoliv by se to na první pohled mohlo zdát zbytečné, když máme metody fillRect() a strokeRect(), tak je to velmi výhodné, když chceme vybarvit obdélník a dát mu zároveň rámeček. Jednoduše obdélník vykreslíme jako cestu, vykreslíme obsah a rámeček.

kontext.beginPath();
kontext.rect(10, 10, 60, 60);
kontext.closePath();
kontext.fillStyle = "red";
kontext.strokeStyle = "blue";
kontext.stroke();
kontext.fill();

Výsledek:

Plátno
localhost

Transformace kontextu

Velmi důležité jsou při zpracovávání obrázku transformace. Čas od času se bude hodit, když pozice nula nebude nulou. Asi ten nejjednodušší případ je, když kolem canvasu budeme mít nějaký hodně specifický 10px široký rámeček, který bychom jen v CSS nenastylovali. Samozřejmě všude můžeme vypisovat, aby se vykreslovalo na x + 10, y + 10, ale jednoduší pro nás bude využít posunu kontextu. U rotací to bude o to zajímavější a mnohdy jediná cesta jak docílit daného výsledku.

Uložení kontextu

Před jakoukoliv manipulací s kontextem je dobré si ho uložit. My ho sice potom můžeme vrátit do pozice x -10 a y -10, ale je to zbytečně pracné a navíc si musíme pamatovat tyto hodnoty. Proto má kontext metody save() a restore(), ani jedna nepožaduje žádný parametr. Metoda save() si uloží aktuální transformace a metoda restore() je obnoví.

Jako příklad si vykreslíme obrázek s červeným rámečkem, ačkoliv je toto nejjednodušší řešit s pomocí CSS, zkusíme to v JavaScriptu.

Nejprve si načteme plátno, kontext a obrázek, to již zvládnete sami. Kontextu nastavíme barvu výplně např. na červenou a překreslíme jí celý <canvas>. Poté kontext uložíme.

kontext.fillStyle = "#f00";
kontext.fillRect(0, 0, 510, 340);

kontext.save();

Posun kontextu

Posun kontextu provádíme metodou translate(), která přijímá parametry x a y pro souřadnice posunu. Nový nulový bod bude na těchto souřadnicích. My si plátno posuneme o kladných 10px jak po ose x, tak po ose y.

kontext.translate(10, 10);

Nyní na pozici [0; 0] vykreslíme obrázek a kontext plátna obnovíme.

kontext.drawImage(obrazek, 0, 0);
kontext.restore();

Když si nyní aplikaci otevřeme, všimneme si, že ačkoliv jsme zadali, ať se obrázek nakreslí na [0;0], tak se vykreslil na [10;10]. Může za to právě náš posun.

Plátno
localhost
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Nezapomeňte vrátit kontext, kdybyste s ním ještě někdy potřebovali pracovat.

Zvětšení/zmenšení

Kontext můžeme zmenšovat a zvětšovat. Slouží k tomu metoda scale(), která jako parametry přijímá násobky skutečné hodnoty pro souřadnice X a Y. Pro zmenšování se zadávají desetinná čísla menší než 1.

Plátno
localhost

Rotace

Poslední operací je rotace. Často potřebujeme něco otočit. Např. když potřebujeme udělat kosočtverec nebo kosodélník, není nic jednoduššího než otočit kontext. Samozřejmě bychom mohli body přepočítávat, ale bylo by to zbytečně komplikované. Kontext otáčíme metodou rotate(), která jako parametr přijímá úhel v radiánech.

kontext.save();
kontext.translate(100, 100);
kontext.rotate(45 * Math.PI / 180);
kontext.strokeRect(0, 0, 50, 50);
kontext.restore();

Výsledek:

Plátno
localhost

Stín

Všechny operace, které něco na canvas nakreslí, můžeme provádět se stínem. Vlastností shadowColor nastavíme barvu stínu. Vlastnostmi shadowOffsetX a shadowOffsetY nastavíme posun stínu a pomocí shadowBlur jak moc bude rozostřený.

kontext.shadowColor = "red";
kontext.shadowOffsetX = 6;
kontext.shadowOffsetY = 3;
kontext.shadowBlur = 10;

Výsledek:

Plátno
localhost

Barevné přechody

Občas se hodí obrázek "vylepšit" barevnými přechody. Existují 2 typy přechodů - lineární a kruhové (radiální). Oba se dají nastavit jak pro výplň, tak pro obrys.

Lineární přechod

Lineární přechod je jednoduchý, barvy se postupně mění jak řekneme a to za sebou. Vytváří se metodou createLinearGradient() na objektu kontextu a předáváme jí 4 parametry. Jsou to souřadnice x a y pro počátek a pro konec gradientu. Nově vzniklý objekt disponuje metodou addStopColor(), která přidá gradientu další barvu. Tato metoda přijímá jako první parametr pozici "zastavení" v rozmezí 01 a jako druhý danou barvu.

let gradient = kontext.createLinearGradient(0, 0, 100, 0);
gradient.addColorStop(0, "yellow");
gradient.addColorStop(0.2, "orange");
gradient.addColorStop(0.4, "pink");
gradient.addColorStop(0.6, "red");
gradient.addColorStop(0.8, "green");
gradient.addColorStop(1, "blue");

kontext.fillStyle = gradient;
kontext.fillRect(0, 0, 100, 100);

kontext.font = "19px Calibri"
kontext.fillText("ITnetwork.cz", 0, 115);

Výsledek:

Plátno
localhost

Radiální přechod

Radiální neboli kruhový přechod se používá úplně stejně, jen vytvoří jiný efekt. Metoda, která jej vytvoří, se nazývá createRadialGradient() a přijímá 6 parametrů. x a y pro počáteční bod, poloměr a to vše ještě jednou i pro koncový bod.

V hodnotách je poměrně zmatek. První pozice x a y je vlastně střed, první poloměr je poloměr, u kterého začne přechod. Pokud bude nula, přechod začne již od středu. Pokud cokoli jiného, bude od středu první barva a až na zadaném poloměru začne přecházet. Pokud chcete docílit pravidelného kruhu, tak 4. a 5. parametr bude kopírovat první a druhý. Šestý parametr poté udává na jakém poloměru se přechod zastaví, dále již bude jen poslední barva. Nastavování barev je stejné.

Plátno
localhost

Obrázková výplň

Jedna z dalších možností výplně (dostupná i pro obrys) je obrázková výplň. Docílíme jí tak, že vytvoříme pattern (vzor) a ten nastavíme výplni (nebo obrysu). Pattern vytvoříme metodou createPattern(), kde jako první parametr předáme obrázek a jako druhý způsob opakovaní. Pro opakování po ose X i Y můžeme ponechat prázdný String.

let vzor = kontext.createPattern(obrazek, "");
kontext.fillStyle = vzor;

Výsledek:

Plátno
localhost

Jako vzor můžeme použít i jiný element <canvas>.

Práce s pixely

Až doposud za nás nějaké pixely řešil JavaScript, ale občas se hodí, abychom i my mohli pracovat s jednotlivými pixely.

Obrázková data

2D kontext plátna poskytuje objekt ImageData. Získáme jej metodou getImageData(), které jako parametry předáme souřadnice levého horního rohu a velikost výřezu. Tento objekt bude mít pole data, ve kterém jsou všechny RGBA hodnoty pixelů v tomto výběru. Pixely jsou řazené postupně, řádek po řádku, zleva doprava.

Práce s pixely na canvasu v JavaScriptu

Vytvořme si aplikaci, která invertuje barvy obrázku. Načtěme si zas plátno, kontext a obrázek. Na plátno vykreslíme obrázek a z obrázku si získáme obrázková data.

kontext.drawImage(img, 0, 0);
let dataObrazku = kontext.getImageData(0, 0, platno.width, platno.height);

Nyní cyklem projdeme všechny barvy, každý pixel je v poli zastoupen 4×, respektive každá jeho složka (R, G, B, A) tam má hodnotu. Cyklus tedy nebude mít klasickou inkrementaci, ale iterační proměnnou zvýšíme o 4.

for (let i = 0; i < dataObrazku.data.length; i += 4) {
}

V cyklu poté hodnotám i (R), i + 1 (G), i + 2 (B) nastavíme novou barvu. V našem případě odečítáme hodnotu barvy od 255, což je maximální hodnota. Tím hodnoty otočíme a docílíme efektu negativu. Alfa kanál nebudeme měnit.

dataObrazku.data[i + 0] = 255 - dataObrazku.data[i + 0];
dataObrazku.data[i + 1] = 255 - dataObrazku.data[i + 1];
dataObrazku.data[i + 2] = 255 - dataObrazku.data[i + 2];

Nakonec vložíme upravená ImageData do obrázku metodou putImageData() na pozici [0;0].

kontext.putImageData(dataObrazku, 0, 0);

Výsledek:

Plátno
localhost

Nyní byste již měli být schopni ovládat <canvas> v JavaScriptu. Doporučuji si vyzkoušet cvičení, který má tento kurz k dispozici.

V následujícím cvičení, Řešené úlohy k 15. a 16. lekci JavaScriptu, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Stáhnout

Staženo 306x (232.19 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

Předchozí článek
Obrázky a kreslení na canvas v JavaScriptu
Všechny články v sekci
Základní konstrukce jazyka JavaScript
Článek pro vás napsal Michal Žůrek - misaz
Avatar
Jak se ti líbí článek?
15 hlasů
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.
Aktivity (15)

 

 

Komentáře

Avatar
Patrik Pastor:30.3.2019 21:21

chtel bych se zeptat, kdyz neudelam tag <img> ale vytvorim ho v javascriptu a potom mu dam src hodnotu, jak to ze to nejde? Cestu mam spravne. Vykresli se pouze cerna barva, bez obrazku jako patternu, nevite v cem to je?

<script>
let platno;
let kontext;
let obrazek;
window.onload = function(){
platno = document.getE­lementById("plat­no");
kontext = platno.getCon­text("2d");

obrazek = new Image();
obrazek.src = "fotky/lamp1.png";

let pattern = kontext.create­Pattern(obrazek, "");

kontext.fillStyle = pattern;
kontext.fillRec­t(50, 50, 100, 100);

kontext.begin­Path();
kontext.arc(100, 225, 50, 0, Math.PI * 2);
kontext.close­Path();
kontext.fill();

}
</script>

 
Odpovědět
30.3.2019 21:21
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:15. března 11:49

Tak tato sekce tutoriálu je pěkně odfláklá.
V podstatě je to úplně stejné, jako kdybyste vypsali možné metody třídy canvas a pod to připsali: použití si vygooglete.
U víc jak půlky příkladů jsem to musel udělat. Je to tu hůř popsané, než kdybych si o tom přečetl rovnou dokumentaci.
Snad poprvé tady na stránkách kritizuji bohužel celou sekci:(

Příklad:

Zvětšení/zmenšení
Kontext můžeme zmenšovat a zvětšovat. Slouží k tomu metoda scale(), která jako parametry přijímá násobky skutečné hodnoty pro souřadnice X a Y. Pro zmenšování se zadávají desetinná čísla menší než 1.

Ale to, že se musí metoda scale volat před vykreslováním obrázku, to jsem si musel vygooglit.

Odpovědět
15. března 11:49
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Vojtech Palec:15. března 17:54

To ano, ale to je dál za touhle sekcí. :-)

Editováno 15. března 17:57
 
Odpovědět
15. března 17:54
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na Vojtech Palec
Lubor Pešek:15. března 17:56

A nebo (objektově) můžeš vytvořit instanci čtverce a pak s ní pracuješ, voláš její metody a modifikuješ ji.

Odpovědět
15. března 17:56
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Marek
Člen
Avatar
Marek:13. září 21:49

Ahoj, nesetkal se někdo během používání metody getImageData() s takovouto chybou ?

Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.

Google zatím nepomohl. Obrázek mám lokálně ve složce..

Díky,
Marek

 
Odpovědět
13. září 21:49
Avatar
Marek
Člen
Avatar
Odpovídá na Marek
Marek:15. září 22:11

Vyřešeno lokálním serverem přes node.js + browsersync. Myslím, že toto během návodu nebylo zmíněno, že je třeba lokální server.

Z nějakého důvodu chrome blokuje při využití některých funkcí canvasu lokální cestu k souboru.

 
Odpovědět
15. září 22:11
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.