tanulas_html5_canvas_grafika_programozasa - Fehér Krisztián honlapja

Fehér Krisztián weboldala
Fehér Krisztián weboldala
Tartalomhoz ugrás

tanulas_html5_canvas_grafika_programozasa

Fehér Krisztián: HTML5 Canvas grafika programozása

Tartalomjegyzék
1  Előszó
1.1  A leírás célja és szerkezete
1.2  Követelmények
1.3 Letölthető kódok
1.4  Hibakeresés
2  Ismerkedés a Canvas objektummal
3  A rajzolás alapjai
3.1  Alapvető képernyőadatok lekérdezése
3.2  Transzformációk
3.2.1  Nagyítás
3.2.2  Forgatás
3.2.3  Origó mozgatása
3.2.4  Transzformációs mátrixok módosítása
3.3  Rajzállapotok kezelése
3.4  Canvas tartalmának kimentése
3.5  Rajzolási színek és stílusok megadása
3.5.1  Átlátszóság
3.5.2  Képtartalmak kombinálása
3.5.3  Vonalszín
3.5.4  Vonalvastagság
3.5.5  Vonalvégek rajzolata
3.5.6  Vonaltalálkozások rajzolata
3.5.7  Mitervastagság
3.5.8  Egyszerű kitöltőszín
3.5.9  Átmenetes kitöltőszínek
3.5.10  Bitképes kitöltés, csempézés
3.5.11  Árnyékbeállítások
3.6  Geometriai alakzatok rajzolása
3.6.1  Négyzetek rajzolása
3.6.2  Szakaszok rajzolása (paths)
3.7  Területek levágása
3.8  Szövegek megjelenítése
3.8.1  Betűtípus és betűméret
3.8.2  Szöveg igazítása
3.8.3  Alapvonalhoz igazítás
3.8.4  Kitöltött megjelenítés
3.8.5  Körvonalas megjelenítés
3.8.6  Szöveghossz lekérdezése
3.9  Pixelszintű képkezelés
3.9.1  Bitkép kirajzolása
3.9.2  Az Imagedata objektum
4  Komplex példa

1 Előszó

Ez a leírás  a HTML5 grafikus interfészének, a Canvasnak a programozását mutatja be, melynek segítségével látványos grafikával ruházhatunk fel weboldalakat.

1.1  A leírás  célja és szerkezete

A HTML5 óriási előnye az elérhetősége, mivel a legelterjedtebb böngészőprogramok alapértelmezetten támogatják, programozási szempontból pedig semmilyen kiegészítőre nem lesz szükségünk.
A leírás  a rajzoláshoz használatos függvényeket példákon keresztül mutatja be, egymás után, egy komplex példával a végén.
A leírás  tekinthető egy receptgyűjteménynek is.

1.2  Követelmények

A leírás  ismeretanyagának feldolgozásához és a kipróbáláshoz a JavaScript nyelv alapvető ismerete szükséges.

1.3  Letölthető programkódok

A leírás  végén megtalálható kódokat használó példaalkalmazás teljes forráskódja letölthető a következő weboldalról:


1.4  Hibakeresés

A hibakeresés nagyon fontos része minden programozási feladatnak, ezért mindenképpen fel kell hívnunk arra a figyelmet, hogy a böngészők manapság rendelkeznek beépített, fejlesztést támogató funkciókkal, eszközökkel.
A Firefox esetében ez az Eszközök menü Webfejlesztő almenüjében található eszközlistát jelenti, melyek közül nagy hasznát vehetjük a Webkonzolnak, mely az esetleges futási idejű hibaüzeneteket meg tudja jeleníteni. Rengeteg időt és fejfájást spórolhatunk így meg magunknak. Más böngészők, mint az Internet Explorer, vagy a Chrome szintén rendelkeznek hasonló segédeszközökkel.





2  Ismerkedés a Canvas objektummal

A Canvas a HTM 5 egyik beépített objektuma, segítségével dinamikus 2D grafikát alkalmazhatunk weboldalainkon.
A következő böngészők esetében beszélhetünk teljeskörű támogatottságról: Internet Explorer, Firefox, Chrome, Safari, Opera.
A Canvas alapesetben JavaScript nyelven programozható. A leírás  példái az alábbi kódot (PELDA01.HTML) fogják használni keretként.


<!DOCTYPE html>
<html>
<body>

<canvas id="rajzvaszon" width="800" height="600"
style="border:1px solid #000000;">
Nem tamogatott a Canvas elem!
</canvas>

<script>
//kodok ide
</script>

</body>
</html>


Mint a fenti kódból is látható, a Canvas maga egy HTML taggel hozható létre.

Két fontos dolgot kell megjegyeznünk ezen a ponton:
1.  A Canvas objektumnak mindenképpen kell egy ID tulajdonsággal rendelkeznie, mert ezen keresztül hivatkoznak rá a rajzoló metódusok, elsősorban a getContext metódus, mely a közvetlen referenciát szolgáltatja.
2.  Egy HTML dokumentumban egyszerre több Canvas obektumunk is lehet.

A leírás  példaprogramjai a fenti kódkeretet fogják használni, ezért azt külön nem fogjuk megadni, hanem csak a <script> tag-ek közti kódblokkokat.

Ahhoz, hogy elkezdhessünk Javascript kóddal rajzolni, először az alábbi két sort kell megadni a kódblokkban:

var canvas = document.getElementById("rajzvaszon");
var rajzolas = canvas.getContext("2d");


A fenti kódrészlet (PELDA02.HTML) hozza létre a kapcsolatot (az ún. “kontextust”) a HTML tag-gel megadott Canvas objektum és a Javascript kód között.
Ezután minden rajzutasítást a

rajzolas. metódusnév

formában adhatunk meg.

A Canvas koordinátarendszere alapértelmezetten jobbsodrású, origója a bal felső sarokban van és 0,0 kezdetű. A vízszintes és a függőleges koordináták jobbra, ill. lefelé növekednek.



3  A rajzolás alapjai

A rajzolás során szükség lehet a rajzvászon globális tulajdonságainak módosítására, illetve lekérdezésére, melyek segítségével a rajzolás/renderelés alaptulajdonságait állíthatjuk be.
Ennek a fejezetnek az elején röviden ezeket fogjuk áttekinteni, majd rátérünk a konkrét rajzmetódusokra.
Az egyes rajztulajdonságok tárgyalásánál külön nem térünk ki rá, de ezen a ponton megemlítjük, hogy ezek tartalmát nevükkel hivatkozva nem csak írni, hanem kiolvasni is tudjuk Javascript programkódból.

3.1  Alapvető képernyőadatok lekérdezése

JavaScriptben a teljes képernyő méretét a screen.width és a screen.height változók segítségével kérdezhetjük le.
A böngésző ún. kliens méretét (az ablak azon része, melyre effektíve rajzolhatunk) a window objektum tartalmazza, a window.innerWidth és window.innerHeight változókban.
Ha szükségünk lenne a teljes képernyő felbontásértékeire, akkor az alábbi kódot használjuk:


var kepernyo_szelesseg = window.innerWidth
|| document.documentElement.clientWidth
|| document.body.clientWidth;

var kepernyo_magassag = window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight;


3.2  Transzformációk

A Canvas koordinátarendszerén transzformációkat hajthatunk végre, melyek megkönnyíthetik a rajzolás szervezését és a végső rajzkimenet megjelenését.

3.2.1  Nagyítás

context.scale( scalewidth, scaleheight);

A scale metódus a koordinátarendszer vízszintes és függőleges felosztását módosítja. Az 1-es érték az alapértelmezett, 100 százalékos nagyítást jelenti.
A scale(2,2) hívással a kétdimenziós rajzkoordinátarendszeren mindkét tengelyen kétszeres mértékű nagyítást eszközölhetünk.

Példa:

rajzolas.scale(2,2);


3.2.2  Forgatás

context.rotate( angle );

A rotate metódussal a forgatás során radiánsban megadott szögértékekkel beállíthatjuk a kirajzolt alakzatok „elforgatottságát". A forgatás iránya az óramutató járásával megegyező.

Példa:

rajzolas.rotate(20*Math.PI/180);


3.2.3  Origó mozgatása

context.translate( x, y);

A koordinátarendszer kezdőpontjának az elmozgatása a translate függvénnyel lehetséges.

Példa:

rajzolas.translate(70,70);


3.2.4  Transzformációs mátrixok módosítása

A Canvas minden objektuma rendelkezik egy ún. transzformációs mátrix-szal, aminek a segítségével a rajzolás nagyítása és orientációja változtatható meg. Erre a transform és a setTransform metódusok szolgálnak.

context.transform(a,b,c,d,e,f);
context.setTransform(a,b,c,d,e,f);

Az egyes paraméterek jelentése a következő:
•  a: vízszintes nagyítás
•  b: vízszintes döntés
•  c: függőleges döntés
•  d: függőleges nagyítás
•  e: vízszintes elmozdítás
•  f: függőleges elmozdítás.

A különbség a két metódus között az, hogy a transform metódushívások mindig relatívak az előzőekben már beállított ún. transzformációs mátrixhoz képest, míg a setTransform metódus mindig abszolút módosításokat jelent, tehát teljesen önmagában és egyedüliként hat ki a rajzolás kimenetére.

Példa:

//Alapértelmezett transzformációs mátrix
rajzolas.fillStyle = "yellow";
rajzolas.fillRect(0, 0, 250, 100)

//Új transzformációs mátrix
rajzolas.transform(1, 0.5, -0.5, 1, 30, 10);
rajzolas.fillStyle = "red";
rajzolas.fillRect(0, 0, 250, 100);


3.3  Rajzállapotok kezelése

A rajzállapotok kezelése a transzformációs mátrixok használata esetén nyújthat segítséget. Lehetőség van ugyanis a teljes rajzkontextus (azaz rajzbeállítások) elmentésére és visszaállítására, a save() és restore() metódusokkal.

Ezekre gyakorlati példát a 4.6.2.5 alfejezetben láthatunk.

3.4  Canvas tartalmának kimentése

Ide kívánkozik, hogy a Canvas toDataUrl metódusának segítségével elviekben lehetőségünk van arra, hogy a Canvas képtartalmát interaktív módon kimentsük egy fizikai fájlba. A gyakorlatban ez azonban böngészőnként eltérő módon, meglehetősen gyalázatosan érhető csak el, ezért a jövőbeli, kiforrottabb megoldások reményében ezen a ponton mindössze csak utalni szeretnénk erre a lehetőségre.

3.5  Rajzolási színek és stílusok megadása

Sok alakzatrajzoló függvény feltételezi, hogy az általa várt színt már megadtuk, ezért a színek megfelelő beállítását soha ne felejtsük el!
A renderelt képek esetében vonal-, kitöltő- és árnyékszínekről beszélhetünk, melyeket külön-külön meghatározhatunk.

3.5.1  Átlátszóság

A renderelt összes(!) alakzat átlátszóságát a globalAlpha tulajdonásggal tudjuk beállítani, ami, mint azt a neve is mutatja, egy kvázi globális változó.
Az alpha tulajdonság értéke egy 0.0 és 1.0 közötti lebegőpontos szám.

context.globalAlpha=number;

Példa:

rajzolas.globalAlpha = 0.2;


3.5.2  Képtartalmak kombinálása

Az egymásra rárajzolt képtartalmak pixelszintű viszonyát állíthatjuk be explicit módon a globalCompositeOperation tulajdonsággal. Ennek függvényében az átfedésben levő alakzatok más-más módon lesznek kirajzolva.

context.globalCompositeOperation="source-in|source-over|source-atop|source-in|source-out|destination-over|destination-atop|destination-in|destination-out|lighter|copy";

Ezzel összefüggésben megkülönböztetjük a már kirajzolt és a rárajzolandó képtartalmat. Előbbire destination image, utóbbira source image nevekkel szokás hivatkozni.
A klasszikus grafikus programozásban ezeket a módszereket bitmaszkolásnak is nevezik.

A lehetséges beállítások:
•  source-over: az alapértelmezett mód, ilyenkor az új képtartalom felülírja a régit.
•  source-atop: az új képtartalom felülírja a régit, de ahol nincsen átfedés, ott az új képtartalom sem fog kirajzolódni.
•  source-in: ha a meglévő képtartalom átlátszó pixelekből áll, akkor az új tartalom csak a régivel átfedésben levő részei fognak kirajzolódni.
•  source-out: ha a meglévő képtartalom átlátszó pixelekből áll, akkor az új tartalom csak a régivel átfedésben nem levő részei fognak kirajzolódni.
•  destination-over: a régi képtartalom az új tartalom felett lesz látható.
•  destination-atop: a régi képtartalom az új tartalom felett lesz látható, de a régi képtartalom azon része, mely az új képtartalmon kívül esik, nem lesz látható.
•  destination-in: a régi és az új tartalom közös része lesz látható, de ott is csak a régi képtartalom.
•  destination-out: a régi és az új tartalom különbsége lesz látható, de ott is csak a régi képtartalom része.
•  lighter: a két képtartalom pixelei a közös területeken összeadódnak.
•  copy: csak az új képtartalom lesz látható.
•  xor: a régi és az új képtartalom is látható lesz, de a közös terület (metszet) nem.

Példa:

rajzolas.globalCompositeOperation = "source-over";




3.5.3  Vonalszín

Vonalszínt a strokeStyle metódussal tudunk megadni, ami lehet egyszerű szín, színátmenet, vagy mintázat:

context.strokeStyle=color|gradient|pattern;

Példa:

rajzolas.strokeStyle="#FF0000";
rajzolas.strokeRect(20,20,150,100);


3.5.4  Vonalvastagság

context.lineWidth=number;

A vonalvastagságot a lineWidth tulajdonsággal állíthatjuk be.

Példa:

rajzolas.lineWidth=10;
rajzolas.strokeStyle="#FF0000";
rajzolas.strokeRect(20,20,80,100);




3.5.5  Vonalvégek rajzolata

context.lineCap="butt|round|square";

A vonalvégek rajzolatát állíthatjuk be a lineCap tulajdonsággal, mely a fent látható 3 értéket veheti fel.

Példa:

rajzolas.lineWidth = 10;
rajzolas.beginPath();
rajzolas.lineCap = "round";
rajzolas.moveTo(20, 40);
rajzolas.lineTo(200, 40);
rajzolas.stroke();




3.5.6  Vonaltalálkozások rajzolata

context.lineJoin="bevel|round|miter";

Amikor két vonal találkozik, a csatlakozási pontoknál a vonalak kirajzolását befolyásolhatjuk a lineJoin tulajdonság 3 fenti értékével.

Példa:

rajzolas.beginPath();
rajzolas.lineWidth = 10;
rajzolas.lineJoin = "round";
rajzolas.moveTo(20, 20);
rajzolas.lineTo(100, 50);
rajzolas.lineTo(20, 100);
rajzolas.stroke();




3.5.7  Mitervastagság

context.miterLimit=number;

A miter maximális vastagságát a miterLimit tulajdonsággal állíthatjuk be, ami két vonal találkozási területén a vonalak átfedésének szélességét jelenti. Fontos, hogy ezt megelőzően a lineJoin tulajdonság is ’miter’-re legyen beállítva!
Amennyiben a maximális értéknél többre lenne szükség, akkor a vonaltalálkozás a ’bevel’ tulajdonságnak megfelelően fog kirajzolódni.

Példa:

rajzolas.lineWidth = 10;
rajzolas.lineJoin = "miter";
rajzolas.miterLimit = 5;
rajzolas.moveTo(20, 20);
rajzolas.lineTo(50, 27);
rajzolas.lineTo(20, 34);
rajzolas.stroke();



   

3.5.8  Egyszerű kitöltőszín

context.fillStyle=color|gradient|pattern;

A kitöltőszín lehet szín, színátmenet, vagy mintázat és beállítására a fillStyle tulajdonság használható. Amikor egy kitöltött alakzatot rajzolunk programból, a kitöltési műveletet a fill() metódussal jelezzük.

Példa:

rajzolas.fillStyle="#FF0000";
rajzolas.fillRect(20,20,150,100);
rajzolas.fill();


3.5.9  Átmenetes kitöltőszínek

Látványos megjelenésű alakzatokat hozhatunk létre átmenetes kitöltőszínek alkalmazásával. Ezeknek két fajtája létezik a HTML5 Canvas esetében:
•  lineáris átmenet
•  radiális átmenet.

Az átmenetes kitöltéseket a strokeStyle és fillStyle rajztulajdonságokhoz rendelhetjük hozzá.
Mindkét átmenetes kitöltés létrehozásakor meg kell adnunk legalább egy színléptéket az addColorStop metódussal.

gradient.addColorStop(stop,color);

A stop paraméter használata némi magyarázatra szorul. Ez egy 0 és 1 közé eső lebegőpontos szám, ahol a 0.0 érték jelképezi a kezdő és az 1.0 a befejező színlépcsőt.
Tetszőleges számú színátmenetet megadhatunk az addColorStop metódus többszöri meghívásával és különböző stop paraméter megadásával.
A kitöltőszínek deklarálásánál meg kell adnunk az átmenet képzeletbeli kiterjedését és lineáris átmenet esetén annak irányát is a createLinearGradient metódussal.

context.createLinearGradient(x0,y0,x1,y1);

Lineáris átmenet esetén az átmenet kezdő és végpontját kell megadnunk a deklarációban. Amennyiben az Y koordináták nem egyeznek meg, az átmenet ferdén tölti ki a területet.

Az alábbi példa lináris átmenet használatát mutatja be:

var atmenet = rajzolas.createLinearGradient(0, 0, 170, 0);
atmenet.addColorStop(0, "black");
atmenet.addColorStop(1, "white");
rajzolas.fillStyle = atmenet;
rajzolas.fillRect(20, 20, 150, 100);




Radiális átmenet esetén a kitöltés egy kör alakú területen megy végbe. Ilyenkor a kezdő kör pozícióit, majd sugarát, majd pedig az átmenet végét jelképező kör ugyanazon paramétereit kell megadnunk az átmenet deklarációja során a createRadialGradient metódussal.

context.createRadialGradient(x0,y0,r0,x1,y1,r1);

Az alábbi példakód segítségével radiális átmenetes kitöltőszínt használunk:

var atmenet = rajzolas.createRadialGradient(75, 50, 5, 90, 60, 90);
atmenet.addColorStop(0, "black");
atmenet.addColorStop(1, "white");
rajzolas.fillStyle = atmenet;
rajzolas.fillRect(10, 10, 150, 100);




3.5.10  Bitképes kitöltés, csempézés

A kitöltések speciális fajtája, amikor a kitöltés tartalma ismétlődik. Az ehhez hasonló effektust az ún. pattern-ekkel érhetünk el, ami lehet egy kép, videó, vagy akár egy másik Canvas elem is és a createPattern metódus első paramétereként kell megadnunk.

context.createPattern(image,"repeat|repeat-x|repeat-y|no-repeat");

A metódus második paramétere a kitöltési eljárást határozza meg. A felhasználás során a fillstyle, vagy strokeStyle tulajdonságoként kell megadnunk a létrehozott pattern-t.

Példaként egy bitképes kitöltést mutatunk be:

<img src="bitkepp.jpg" id="bitkep" width="32" height="32">

var bitkep = document.getElementById("bitkep")
var mintazat = rajzolas.createPattern(bitkep, „repeat”);
rajzolas.rect(0, 0, 150, 100);
rajzolas.fillStyle = mintazat;
rajzolas.fill();
   




3.5.11  Árnyékbeállítások

Nagyon hasznos, hogy az alakzatokhoz árnyékokat is rendelhetünk.
Az árnyékoknak van színe, elmosódottsága és vízszintes, ill. függőleges eltolódása.

Ezeket a rendre az alábbi tulajdonságokkal tudjuk megadni:
•  shadowBColor
•  shadowBlur
•  shadowOffsetX
•  shadowOffsetY.

Az alábbi példakóddal egy négyszöget rajzolunk ki, árnyékkal:

rajzolas.fillStyle = "#aaaaaa";
rajzolas.shadowBlur = 20;
rajzolas.shadowColor = "black";
rajzolas.shadowOffsetX = 20;
rajzolas.shadowOffsetY = 20;
rajzolas.fillRect(20, 20, 100, 80);




3.6  Geometriai alakzatok rajzolása

Az előzőekben megismertük, hogyan állíthatjuk be a rajzolás stílusát. Most ideje megismerkedni konkrét alakzatok kirajzolásának mikéntjével is.

3.6.1  Négyzetek rajzolása

A négyzetek rajzolásának legegyszerűbb módja a rect metódus használata.

context.rect(x,y,width,height);

A paraméterek a kirajzolandó négyzet bal felső koordinátáit, valamint a szélességét és a magasságát adják meg.



Ahhoz, hogy a négyzet meg is jelenjen, a metódushívást egy .beginPath() .. .stroke() metóduspáros közé ajánlott elhelyezni. Az ún. path-ok rajzolását a következő alfejezetben részletesen is tárgyaljuk.

Példa:

rajzolas.beginPath();
rajzolas.rect(20, 50, 150, 100);
rajzolas.stroke();


A stroke() metódus mellett állhat fill() metódus is, vagy csak az egyik. Az előbbi csak a négyzet körvonalát fogja kirajzolni, az utóbbi ki is tölti a négyzetet az aktuálisan érvényes kitöltőszínnel.

Kitöltött négyzetet egy lépésben, a fillrect metódussal is kirajzolhatunk.

context.fillRect(x,y,width,height);

Példa:

rajzolas.fillRect(20,20,150,100);


Körvonalas négyzetet egylépésben a strokeRect metódussal is rajzolhatunk.

context.strokeRect(x,y,width,height);

Példa:

rajzolas.strokeRect(20, 20, 150, 100);


Érdekes lehetőséget kínál a clearRect metódus, mely egy már kirajzolt négyzeten a megadott területen törli a rajzszínt.

context.clearRect(x,y,width,height);

Vegyük észre, hogy ez a metódus kvázi képernyőtörlésként is használható!

Példa:

rajzolas.fillStyle = "red";
rajzolas.fillRect(0, 0, 300, 150);
rajzolas.clearRect(20, 20, 100, 50);


3.6.2  Szakaszok rajzolása (paths)

Szakaszrajzolás alatt nem egyszerű vonalakat kell érteni, hanem bonyolult görbék, sőt poligonok kirajzolását is, melyeket akár ki is tölthetünk.
Minden esetben a beginPath() metódus meghívásával kell jeleznünk, hogy egy ilyen összetett alakzatot akarunk rajzolni. Ezután több rajzolási műveletet megadhatunk, melyek egy logikailag egységesnek veendő alakzatot írnak le. Egy-egy ilyen műveletsorozatot a stroke(), vagy fill() metódusokkal kell lezárnunk, attól függően, hogy csak körvonalat, vagy kitöltést is kívánunk rajzolni.

3.6.2.1  Egyenes szakaszok rajzolása

A vonalak, illetve poligonok kirajzolása a moveTo és lineTo metódusokkal történik.

context.moveTo(x,y);
context.lineTo(x,y);

A moveTo metódus egy képzeletbeli rajzceruza hegyét felemeli és leteszi a Canvas megadott pontjára. Vonal, él rajzolása ilyenkor nem történik. Minden ezután következő lineTo metódushívás a megadott koordinátákig egy vonalat húz, az aktuálisan érvényes vonalszínnel és vonalvastagsággal. Egy következő lineTo metódus pedig ettől az új koordinátától fog rajzolni.



Példa:

rajzolas.beginPath();
rajzolas.moveTo(0, 0);
rajzolas.lineTo(300, 150);
rajzolas.stroke();


Amennyiben zárt alakzatot szeretnénk rajzolni, a rajzolás megkezdését kiváltó parancs (pl. stroke) kiadása előtt a closePath() metódust is meg kell hívnunk. Elegendő az alakzat utolsó csúcspontjánál megtennünk ezt, ugyanis a closePath() automatikusan létrehoz egy élt a legeslegelső csúcsponthoz.

Példa:

rajzolas.beginPath();
rajzolas.moveTo(20, 20);
rajzolas.lineTo(20, 100);
rajzolas.lineTo(70, 100);
rajzolas.closePath();
rajzolas.stroke();


3.6.2.2  Másodfokú bezier görbék rajzolása

context.quadraticCurveTo(cpx,cpy,x,y);

A másodfokú bezier görbék olyan szakaszok, melyek két pontból és az ezeket összekötő görbéből állnak.
A görbe kezdőpontját egy moveTo metódus adja meg, a végpontját a quadraticCurveTo metódus utolsó paraméterpárja.
Azt a  pontot, amelyhez képest a görbe meg lesz rajzolva, ún. kontrollpontnak nevezik. Ez a quadraticCurveTo metódus első paraméterpárja.


 

Példa:

rajzolas.beginPath();
rajzolas.moveTo(20, 1660);
rajzolas.quadraticCurveTo(30, 1740, 200, 1660);
rajzolas.stroke();


3.6.2.3  Bezier görbék rajzolása

context.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);

Bezier görbét egy moveTo és a bezierCurveTo metódusok kombinálásával tudunk kirajzolni.
A görbe kezdőpontját a moveTo metódus adja meg, a végpontját a bezierCurveTo metódus utolsó paraméterpárja.
A bezier görbék is kontrollpontokat használnak a görbék megadásához. A bezierCurveto metódus első két paraméterpárja ezt a két kontrollpontot adja meg.


 

Példa:

rajzolas.beginPath();
rajzolas.moveTo(20, 20);
rajzolas.bezierCurveTo(20, 100, 200, 100, 200, 20);
rajzolas.stroke();


3.6.2.4  Körív rajzolása

Körvonalat az arc metódussal rajzolhatunk.

context.arc(x,y,r,sAngle,eAngle,counterclockwise);

A kör középpontját adhatjuk meg az első paraméterpárral. Az r (mint radius) a kör sugarát adja meg. Az sAngle és az eAngle a kezdő- és a záró szöget jelentik és fontos, hogy radiánban adjuk meg (0-tól 2 * Math.PI-ig terjedő érték). Az utolsó paraméter értéke true, vagy false (az alapértelmezett érték) lehet, de megadása opcionális. Azt adja meg, hogy a szögek köztí ív az óramutató járásával ellentétesen, vagy megegyezően értendő.


 

Példa:

rajzolas.beginPath();
rajzolas.arc(100, 75, 50, 0, 2 * Math.PI);
rajzolas.stroke();


3.6.2.5  Ellipszis rajzolása

A HTML5 Canvas-ra ellipszist csak némi trükközéssel lehet kirajzolni, transzformációs mátrixok beállításával és kezelésével.
Egy saját függvénnyel tudjuk a legegyszerűbben megoldani, ami egy szabályos kört rajzol az arc metódussal, de úgy, hogy ideiglenesen megváltoztatja a rajzolás arányait.

Példa:

ellipse(100,2000,80,30);

function ellipse(cx, cy, rx, ry)
{
       rajzolas.save(); // canvas állapotának elmentése
       rajzolas.fillStyle = "#000000";
       rajzolas.strokeStyle="#000000";
       rajzolas.beginPath();

       rajzolas.translate(cx-rx, cy-ry);
       rajzolas.scale(rx, ry);
       rajzolas.arc(1, 1, 1, 0, 2 * Math.PI, false);

       rajzolas.restore(); // canvas állapotának visszaállítása
       rajzolas.fill();
       rajzolas.stroke();
}


3.6.2.6  Két pontot összekötő körív rajzolása

Egy speciális esete a körív rajzolásának, ha az két pontot köt össze oly módon, hogy azok pontosan a köríven helyezkedjenek el.
Ezt így lehet elképzelni:


 

Erre a speciális rajzműveletre az arcTo metódus szolgál.

context.arcTo(x1,y1,x2,y2,r);

Az első két paraméterpár a tangensek koordinátáit adják meg. Az r paraméter a képzeletbeli körív sugarát jelenti.

Példa:

rajzolas.beginPath();
rajzolas.moveTo(20, 20);
rajzolas.lineTo(100, 20);
rajzolas.arcTo(150, 20, 150, 70, 50);
rajzolas.lineTo(150, 120);
rajzolas.stroke();  


3.6.2.7  Befoglalás ellenőrzése

Egy érdekes lehetőség a szakszokból felépített alakzatok esetében annak vizsgálata, hogy egy pont az alakzaton belül helyezkedik-e el.
Erre szolgál az isPointInPath metódus, mely logikai true, vagy false értéket ad vissza.

context.isPointInPath(x,y);

Példa:

rajzolas.rect(20, 20, 150, 100);
if (rajzolas.isPointInPath(20, 50))
{
 rajzolas.stroke();
};


3.7  Területek levágása

A clip() metódushívást megelőző alakzatmegadással tetszőleges területet megadhatunk a Canvas objektumon. Ezután minden további rajzolás kizárólag ezen alakzaton belül fog megjelenni, a többi terület ignorálva lesz.

Példa:

// Terület megadása
rajzolas.rect(50,20,200,120);
rajzolas.stroke();
rajzolas.clip();

// kivágás tesztelése
rajzolas.fillStyle="blue";
rajzolas.fillRect(0,0,150,100);


Az eredeti Canvas kivágási beállításokat (azaz, amikor nincsen kivágás definiálva), az ún. kontextuskezelő metódusokkal (lásd save és restore metódusokat a 4.3 fejezetben) bármikor elmenthetjük és visszaállíthatjuk.

3.8  Szövegek megjelenítése

A szövegmegjelenítő metódusok és tulajdonságok segítségével statikus szöveges tartalmakat renderelhetünk, melyek nagyban megkönnyítik információk kiemelését, hangsúlyozását.

3.8.1  Betűtípus és betűméret

A használandó betűtípust és betűméretet egy a font tulajdonságnak megadott, CSS szintaxisú sztringen keresztül állíthatjuk be.

Példa:

rajzolas.font=" italic small-caps bold 12px arial";
rajzolas.fillText("Hello világ!",10,50);


A sztring az alábbi tulajdonságokat tartalmazhatja:
•  font-style: a font stílusa. Lehetséges értékei:
o  normal
o  italic
o  oblique.
•  font-variant: a font változatai. Lehetséges értékei:
o  normal
o  small-caps.
•  font-weight: a betűk vastagsága. Példák: normal, bold, 100,200 stb.
•  font-size/line-height: a font mérete, pixelekben.
•  font-family: a fontcsalád megadása.
•  caption: a feliratos vezérlők, például nyomógonbok betűbeállításainak átvétele.
•  icon: az ikonok címkéihez használt betűbeállítások átvétele.
•  menu: a menük betűbeállításainak átvétele.
•  message-box: a dialógusablakok betűbeállításainak átvétele.
•  small-caption: kisméretű vezérlők betűbeállításainak átvétele.
•  status-bar: a státuszsor betűbeállításainak átvétele.

Az alapértelmezett érték: ”10px sans-serif”.

3.8.2  Szöveg igazítása

A szövegszerkesztőkben megszokott igazításokat alkalmazhatunk, pár speciálissal együtt. A beállítást sztringként kell megadni a textAlign tulajdonságban.

context.textAlign="center|end|left|right|start";

Mindig a fillText, ill strokeText metódushívások előtt el kell végezni ezt a beállítást, mert az utána következő hívásokra lesz hatással. Az ezen metódusokban megadott koordinátákhoz képest kell értelmezni a lehetséges igazítási értékeket:
•  start: a szöveg kirajzolása a megadott koordinátán kezdődik.
•  end: a szöveg kirajzolása a megadott koordinátával ér véget.
•  center: a szöveg közepe a megadott koordinátán lesz.
•  left: a szöveg renderelése a megadott koordinátán kezdődik.
•  right: a szöveg kirajzolása a megadott koordinátán ér véget.

Az egyes beállításokat az alábbi ábra is jól szemlélteti:



 
Példa:

rajzolas.font = "15px Arial";
rajzolas.textAlign = "start";
rajzolas.fillText("textAlign=start", 150, 60);


3.8.3  Alapvonalhoz igazítás

context.textBaseline="alphabetic|top|hanging|middle|          ideographic|bottom";

A szövegek egyik fontos tulajdonsága az ún. alapvonalukhoz (angolul: baseline) viszonyított elhelyezkedésük.

Ennek 6 különböző módját állíthatjuk be:
•  alphabetic: ez az alapértelemezett beállítás, a szöveg a normál alapvonalhoz igazítva jelenik meg.
•  top: az alapvonal a karakterek felső vonala.
•  hanging: az alapvonal a karakterek felső alaphatára, ún. függő kiindulópontja.
•  middle: az alapvonal a karakterek függőleges középvonala.
•  ideographic: az alapvonal a karakterek ún. képírásos vonala.
•  bottom: az alapvonal a karakterek legalsó határoló vonala.

A fentieket szemlélteti az alábbi ábra:



Példa:

rajzolas.textBaseline = "top";
rajzolas.fillText("Top", 5, 100);


3.8.4  Kitöltött megjelenítés

context.fillText(text,x,y,maxWidth);

A fillStyle tulajdonság és a fillText metódus kombinálásával kitöltőszínnel renderelhetünk szövegeket. Az alapértelmezett kitöltőszín a fekete.
A filltext metódus utolsó, opcionális paramétere a szöveg maximális szélességét tudja megszabni, pixelekben megadva. Ha a kirajzolt szöveg ennél több helyet igényelne, egyszerűen le lesz vágva.

Példa:

rajzolas.fillStyle="#FF0000";
rajzolas.font="20px Georgia";
rajzolas.fillText("Hello világ!",10,50);




 
3.8.5  Körvonalas megjelenítés

context.strokeText(text,x,y,maxWidth);

Lehetőség van szövegek kvázi csak körvonalas, kontúros megjelenítésére is, a strokeStyle tulajdonság és a strokeText metódus használatával. Az alapértelmezett rajzszín a fekete.
A strokeText metódus utolsó, opcionális paramétere a szöveg maximális szélességét tudja megszabni, pixelekben megadva.

Példa:

rajzolas.strokeStyle="#FF0000";
rajzolas.font="20px Georgia";
rajzolas.strokeText("Hello világ!",10,50);




3.8.6  Szöveghossz lekérdezése

context.measureText(text).width;

Ha valamilyen okból szükségünk van a kirajzolandó szöveg hosszára, akkor a measureText metódussal kérdezhetjük le, ami ezt az értéket adja vissza, pixelekben.

Példa:

rajzolas.font="30px Arial";
var txt="Hello World"
rajzolas.fillText("Szelesseg: " + rajzolas.measureText(txt).width,10,50)
rajzolas.fillText(txt,10,100);


3.9  Pixelszintű képkezelés

Ez a fejezet a raszteres képeadatok renderelési lehetőségeit mutatja be. A legegyszerűbb előre elkészített bitképeket megjeleníteni, ám képpontpuffer használatával jelentősen kitolhatjuk a HTML5 Canvas renderelési lehetőségeit is.

3.9.1  Bitkép kirajzolása

context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

Bitképeket a drawImage metódussal tudunk kirajzoltatni. Kész képet először is a HTML szintjén kell betölteni. Ezt egy <IMG> tag-gel lehet megtenni. Fontos, hogy ID tulajdonságot is megadjunk! Javascript kódoban ezután már hivatkozhatunk rá és explicit kezelhetjük.
Annak érdekében, hogy a képre csak annak tényleges  betöltődése után hivatkozzon a kódunk, a képkezelést a böngésző onload eseményéhez kell rendelnünk.
Érdemes észben tartanunk, hogy nem csak bitképeket, hanem akár videókat, vagy más Canvas objektumot is megjelenthetünk ezzel a technikával.

A drawImage metódus paraméterei:
•  img: a képadat ID-ja.
•  sx: opcionális, képkivágás kezdetének vízszintes koordinátája.
•  sy: opcionális, képkivágás kezdetének függőleges koordinátája.
•  swidth: opcionális, kivágott kép szélessége.
•  sheight: opcionális, kivágott kép magassága.
•  x: a kép X koordinátája.
•  y: a kép Y koordinátája.
•  width: opcionális, a kép szélessége (átméretezési lehetőség).
•  height: opcionális, a kép magassága (átméretezési lehetőség).

Példa:

<img id="kepecske" width="220" height="277" src="kep.jpg">
window.onload = function() {
 var c = document.getElementById("myCanvas");
 var ctx = c.getContext("2d");
 var img = document.getElementById("kepecske");
 ctx.drawImage(img, 10, 10);
}


3.9.2  Az Imagedata objektum

Az Imagedata objektum segítségével képpontpuffereket hozhatunk létre és jeleníthetünk meg. Ez tág teret biztosít a kreatív felhasználásnak, némi többletkódolás árán.
A képpontok négybájtos értékcsoportokban tárolódnak, RGBA, azaz vörös-zöld-kék-átlátatlanság szerkezetben, mindegyik 0-255 értéket vehet fel.
Egy ilyen puffert a createImageData metódussal lehet létrehozni, mely alapesetben fekete, látható képpontokat tartalmaz.
A képpontok az imageData objektum data elnevezésű tömbtulajdonságában vannak eltárolva, itt is lehet hozzáférni ezekhez az adatokhoz.
Az objektum szélessége és magassága is közvetlenül hozzáférhető a width és height tulajdonságain keresztül.
A createImageData metódus kétféleképpen is meghívható.
Az egyik, magától értetődőbb a puffer szélességének és magasságának megadásával használható:

var kepadat =context.createImageData(width,height);

A másik módszer egy már létező imageData objektum szélességét és magasságát fogja lekérni és felhasználni:

var kepadat =context.createImageData(imageData);

Példa:

var kepadat = rajzolas.createImageData(100, 100);
var i;
for (i = 0; i < kepadat.data.length; i += 4) {
 kepadat.data[i+0] = 255;
 kepadat.data[i+1] = 0;
 kepadat.data[i+2] = 0;
 kepadat.data[i+3] = 255;
}


A Canvas-ra a putImageData metódus segítségével rajzolhatunk ki egy ilyen puffert.

context.putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,        dirtyHeight);

Az egyes paraméterek jelentése a következő:
•  imgData: hivatkozás az imageData objektumra.
•  x: a kirajzolás bal felső X koordintátája.
•  y: a kirajzolás bal felső Y koordintátája.
•  dirtyX: opcionális, a kép helye a Canvas-on.
•  dirtyY: opcionális, a kép helye a Canvas-on.
•  dirtyWidth: opcionális, a kép szélessége.
•  dirtyheight: opcionális, a kép magassága.

Példa:

rajzolas.putImageData(kepadat, 10, 70,10,10,200,100);


A getImageData metódussal a Canvas tetszőleges területét kimenthetjük egy imageData szervezésű objektumba. Ehhez a terület bal felső koordinátáit, valamin szélességét és magasságát kell csupán megadnunk paraméterként, pixelben.

context.getImageData(x,y,width,height);

Példa:

var kepadat = rajzolas.getImageData(10, 10, 50, 50);
rajzolas.putImageData(kepadat, 10, 70);


4  Komplex példa

Az alábbiakban leközlünk egy komplex példakódot, mely az összes itt bemutatott technikát demonstrálja, egyetlen HTML oldalon megjelenítve.




<!DOCTYPE html>
<html>
<body>

<img src="bitkep.jpg" id="bitkep" width="32" height="32"><br>

<canvas id="rajzvaszon" width="800" height="2700"
style="border:1px solid #000000;">
Nem tamogatott a Canvas elem!
</canvas>

<script>

var canvas = document.getElementById("rajzvaszon");
var rajzolas = canvas.getContext("2d");
var kep;

window.onload = function() {
kep = document.getElementById("bitkep");
rajzolas.drawImage(kep, 10, 2460,70,70);
}

function ellipse(cx, cy, rx, ry)
{
       rajzolas.save(); // canvas állapotának elmentése
       rajzolas.fillStyle = "#000000";
       rajzolas.strokeStyle="#000000";
       rajzolas.beginPath();

       rajzolas.translate(cx-rx, cy-ry);
       rajzolas.scale(rx, ry);
       rajzolas.arc(1, 1, 1, 0, 2 * Math.PI, false);

       rajzolas.restore(); // canvas állapotának visszaállítása
       rajzolas.fill();
       rajzolas.stroke();
}

rajzolas.font="bold 28px arial";
rajzolas.fillText("Grafikus példakódok",10,30);
rajzolas.font="bold 16px arial";

rajzolas.fillText("Transzformáció - nagyítás",10,50);
rajzolas.beginPath();
rajzolas.rect(20, 60, 150, 50);
rajzolas.stroke();
rajzolas.save();
rajzolas.scale(0.5,0.5);
rajzolas.beginPath();
rajzolas.rect(20, 160, 150, 50);
rajzolas.stroke();
rajzolas.restore(); //visszaállítjuk a kezdőállapotot

rajzolas.fillText("Transzformáció - forgatás",10,150);
rajzolas.save();
rajzolas.rotate(2*Math.PI/180);
rajzolas.beginPath();
rajzolas.rect(20, 160, 150, 50);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Transzformáció - origó",10,250);
rajzolas.save();
rajzolas.translate(70,0);
rajzolas.beginPath();
rajzolas.rect(20, 260, 150, 50);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Transzformáció - transzformációs mátrixok",10,350);
rajzolas.save();
rajzolas.transform(0, 0.1, 1, 1  , -330, 0);
rajzolas.beginPath();
rajzolas.rect(20, 360, 150, 50);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - átlátszóság",10,450);
rajzolas.save();
rajzolas.fillStyle="#FF0000";
rajzolas.fillRect(20,460,150,70);
rajzolas.fill();
rajzolas.globalAlpha = 0.5;
rajzolas.fillStyle="#FFFF00";
rajzolas.fillRect(120,465,150,60);
rajzolas.fill();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - bitmaszkolás",10,550);
rajzolas.save();
rajzolas.fillStyle="#FF0000";
rajzolas.fillRect(20,560,150,70);
rajzolas.fill();
rajzolas.globalCompositeOperation = "xor";
rajzolas.fillStyle="#FFFF00";
rajzolas.fillRect(120,565,150,60);
rajzolas.fill();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - vonalszín",10,650);
rajzolas.save();
rajzolas.strokeStyle="#FF0000";
rajzolas.strokeRect(20,660,150,70);
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - vonalvastagság",10,750);
rajzolas.save();
rajzolas.lineWidth=10;
rajzolas.strokeStyle="#FF0000";
rajzolas.strokeRect(20,760,150,70);
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - vonalvégek",10,850);
rajzolas.save();
rajzolas.strokeStyle="#000000";
rajzolas.lineWidth=10;
rajzolas.lineCap="round";
rajzolas.beginPath();
rajzolas.moveTo(20,900);
rajzolas.lineTo(220,900);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - vonaltalálkozások",10,950);
rajzolas.save();
rajzolas.strokeStyle="#000000";
rajzolas.lineWidth=10;
rajzolas.lineJoin = "round";
rajzolas.beginPath();
rajzolas.moveTo(20,1000);
rajzolas.lineTo(120,1000);
rajzolas.lineTo(20,970);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - miter",10,1050);
rajzolas.save();
rajzolas.fillStyle = 0;
rajzolas.strokeStyle="#000000";
rajzolas.lineWidth=20;
rajzolas.lineJoin = "miter";
rajzolas.miterLimit = 5;
rajzolas.beginPath();
rajzolas.moveTo(20,1100);
rajzolas.lineTo(120,1100);
rajzolas.lineTo(20,1070);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - egyszerű kitöltés",10,1150);
rajzolas.save();
rajzolas.fillStyle = "#444444";
rajzolas.fillRect(20, 1160, 150, 70);
rajzolas.fill();
rajzolas.restore();
rajzolas.lineWidth=2;
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - átmenetes kitöltés",10,1250);
rajzolas.save();
var atmenet = rajzolas.createLinearGradient(0, 1300, 200, 1400);
atmenet.addColorStop(0, "black");
atmenet.addColorStop(1, "white");
rajzolas.fillStyle = atmenet;
rajzolas.fillRect(20, 1260, 150, 70);
rajzolas.fill();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - bitképes kitöltés",10,1350);
rajzolas.save();
var bitkep = document.getElementById("bitkep")
var mintazat = rajzolas.createPattern(bitkep, "repeat");
rajzolas.fillStyle = mintazat;
rajzolas.rect(20, 1360, 150, 70);
rajzolas.fill();
rajzolas.restore();

rajzolas.fillText("Rajztulajdonságok - árnyékolás",10,1450);
rajzolas.save();
rajzolas.fillStyle = "#aaaaaa";
rajzolas.shadowBlur = 20;
rajzolas.shadowColor = "black";
rajzolas.shadowOffsetX = 20;
rajzolas.shadowOffsetY = 20;
rajzolas.fillRect(20, 1460, 100, 60);
rajzolas.restore();

rajzolas.fillText("Alakzatok - egyenes szakasz",10,1550);
rajzolas.save();
rajzolas.beginPath();
rajzolas.moveTo(20, 1560);
rajzolas.lineTo(300, 1590);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Alakzatok - másodfokú bezier görbe",10,1650);
rajzolas.save();
rajzolas.beginPath();
rajzolas.moveTo(20, 1660);
rajzolas.quadraticCurveTo(30, 1740, 200, 1660);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Alakzatok - bezier görbe",10,1750);
rajzolas.save();
rajzolas.beginPath();
rajzolas.moveTo(20, 1760);
rajzolas.bezierCurveTo(10, 1820, 230, 1840, 250, 1760);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Alakzatok - körív",10,1850);
rajzolas.save();
rajzolas.beginPath();
rajzolas.arc(100, 1900, 30, 0, 1.5 * Math.PI);
rajzolas.stroke();
rajzolas.restore();

rajzolas.fillText("Alakzatok - ellipszis",10,1950);
rajzolas.save();
ellipse(100,2000,80,30);
rajzolas.restore();

rajzolas.fillText("Alakzatok - két pont - körív",10,2050);
rajzolas.save();
rajzolas.beginPath();
rajzolas.arcTo(20, 2090, 50, 2110, 40);
rajzolas.stroke();  
rajzolas.restore();

rajzolas.fillText("Alakzatok - levágás",10,2150);
rajzolas.save();
// Terület megadása
rajzolas.rect(50,2160,200,70);
rajzolas.stroke();
rajzolas.clip();
// kivágás tesztelése
rajzolas.fillStyle="blue";
rajzolas.fillRect(0,2170,150,60);
rajzolas.restore();

rajzolas.fillText("Szöveg - körvonal",10,2250);
rajzolas.save();
rajzolas.strokeStyle="#FF0000";
rajzolas.font="50px Georgia";
rajzolas.strokeText("Körvonalas szöveg.",10,2310);
rajzolas.restore();

rajzolas.fillText("Szöveg - kitöltés",10,2350);
rajzolas.save();
rajzolas.fillStyle="#FF0000";
rajzolas.font="50px Georgia";
rajzolas.fillText("Kitöltött szöveg.",10,2410);
rajzolas.restore();

rajzolas.fillText("Bitkép - kirajzolás",10,2450);
//lásd windows.onload függvényt a script kód elején

rajzolas.fillText("Bitkép - dinamikus képpuffer",10,2550);
rajzolas.save();
var kepadat = rajzolas.createImageData(252, 100);
var i;
for (i = 0; i < kepadat.data.length; i += 4) {
 kepadat.data[i+0] = i/300;
 kepadat.data[i+1] = i/200;
 kepadat.data[i+2] = i/300;
 kepadat.data[i+3] = 255;
}
rajzolas.putImageData(kepadat, 20, 2560,10,10,200,100);
rajzolas.restore();

</script>

</body>
</html>

KAPCSOLAT

E-mail:
feher.konyvek@gmail.com
KAPCSOLAT

E-mail:
feher.konyvek@gmail.com
Vissza a tartalomhoz