A HTML5 Canvas elem használata

Bevezetés

A HTML5-ös szabvány egy nagyon hasznos újítása a <CANVAS> elem. Ezzel egy raszteres rajzolási felületet hozunk létre a weboldalon, aminek tartalmát aztán JavaScript kódok segítségével alakíthatjuk.

Segédanyagok

Alapok

A Canvas használata két részből áll. Egyrészt definiálni kell a HTML kódban a rajzfelületet a <CANVAS> elem segítségével, másrészt meg kell írni az azt "telerajzoló" JavaScript kódot. Mivel a JavaScript kódban hivatkoznunk kell a <CANVAS> elemre, biztosítanunk kell, hogy végrehajtásakor már létezzen a megfelelő objektum a DOM-ban. Ezért vagy a <body onload="..."> konstrukciót használjuk, vagy a kódot a <CANVAS> elem definiálása után illesszük a html kódba, ahogy az a következő példában is van:

<!doctype html>
<html>
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
  <title>Canvas példák 1.</title>
 </head>
 <body>
  <h1>Canvas példák 1.</h1>
  <canvas id="rajz" width="400px" height="500px"></canvas>  
  <script>
   var rajz=document.getElementById("rajz");
   var rajzCt=rajz.getContext('2d');
   with(rajzCt)
   {
     beginPath();
     rect(100,200,200,200);
     moveTo(100,200);
     lineTo(200,60);
     lineTo(300,200);
     stroke();
   }
  </script>
 </body>
</html>
1. példa: egyszerű vonalas rajz.

Látható, hogy a tulajdonképpeni rajzolási műveleteket nem közvetlenül a Canvas objektumon, hanem egy úgynevezett "drawing context"-en (magyarul ???) hajtjuk végre. Ezt a context objektumot a Canvas getContext('2d') metódusával kapjuk meg. A rajzoló utasításokat a context objektum különféle metódusainek meghívásával adhatjuk ki.

Vonalas rajz esetén az azonos rajzi tulajdonságokal (szín, vastagság, kitöltés) rendelkező elemeket egy beginPath() és egy stroke() utasítás közé lell zárni. A tényleges kirajzolás mindig a stroke() végrehajtásával történik meg.

Természetesen kitöltést is adhatunk a rajzi elemeknek, a fill() metódus használatával. A kitöltés színét, esetleg mintáját a fillStyle tulajdonság értéke határozza meg, míg a vonalak színét a strokeStyle

...   
   with(rajzCt)
   {
     beginPath();
     rect(100,200,200,200);
     fillStyle="#ff8080";
     strokeStyle="red";
     fill();
     stroke();
     beginPath();
     moveTo(100,200);
     lineTo(200,60);
     lineTo(300,200);
     fillStyle="#80ff80";
     strokeStyle="green";
     fill();
     stroke();
   }
...
2. példa: egyszerű vonalas rajz, színekkel, kitöltéssel.

Lehetőségünk van "lyukas" poligonokat is létrehozni. Ehhez arra kell ügyelnünk, hogy a belső határokat a külsőhöz képest ellentétes körüljárási iránnyal definiáljuk. (A rect() által rajzolt téglalap körüljárási iránya óramutató szerinti.)

...   
     beginPath();
     rect(100,200,200,200);
     moveTo(110,210);lineTo(110,300);lineTo(195,300);lineTo(195,210);lineTo(110,210);
     moveTo(205,390);lineTo(290,390);lineTo(290,210);lineTo(205,210);lineTo(205,390);
     fillStyle="#ff8080";
     strokeStyle="red";
     fill();
     stroke();
...
2/a. példa: "lyukas" poligon kitöltéssel.

Kép betöltése külső fájlból

Ha fájlból szeretnénk egy raszteres képet a vászonra helyezni, először be kell azt tölrtnünk a memóriába, mint Image() objektumot. Az objektum onload eseménykezelőjében kell a kép tényleges kirajzolását elvégezni. Ez azért van, mert addig nem tudjuk megjeleníteni a képet, amíg be nem töltődött, viszont a képbetöltés aszinkron folyamat, tehát a programkód futása nem ál meg a töltés befejeztéig.

...
   // bitkép létrehozása
   var kep=new Image();
   // betöltés után jelenítsük majd meg
   kep.onload=function()
   {
     with(rajzCt)
     {
       drawImage(kep,105,205,190,190);
       // a kép elé rajzoljuk a házfalat az ablakkal-ajtóval
       beginPath();
       rect(100,200,200,200);
       moveTo(110,210);lineTo(110,300);lineTo(195,300);lineTo(195,210);lineTo(110,210);
       moveTo(205,390);lineTo(290,390);lineTo(290,210);lineTo(205,210);lineTo(205,390);
       fillStyle="#ff8080";
       strokeStyle="red";
       fill();
       stroke();
       // tető
       beginPath();
       moveTo(100,200);
       lineTo(200,60);
       lineTo(300,200);
       fillStyle="#80ff80";
       strokeStyle="green";
       fill();
       stroke();
     }
   }  
   kep.src="tj.jpg";
...
3. példa: kép betöltése fájlból.

Pixelenkénti manipuláció

A Canvas pixeleihez közvetlenül is hozzáférhetünk. A vászon egy téglalap alakú részének képadatai kinyerhetők a getImageData(x0,y0,w,h) metódussal. Ennek eredménye egy ImageData objektum, melynek data adattagja egy egydimenziós tömbben tartalmazza a pixelek adatait. Minden pixelt 4 egymást követő érték reprezentál (R,G,B,A), így az i. sor j. pixelének adatai a data[(i*w+j)*4] - data[(i*w+j)*4+3] helyeken vannak.

ImageData objektumot üresen is létrehozhatunk a createImageData(w,h) metódussal. A putImageData(imageData,x0,y0) segítségével pedig a Canvas megfelelő helyére rakhatunk egy ImageData objektumot. Így pl. átmásolhatjuk a kép egyes részeit máshova, vagy a módosított képrészletet visszarajzolhatjuk a helyére. A következő példában a képet gombnyomásra "negativizálhatjuk": minden pixel (R, G, B) értéke (255-R, 255-G, 255-B)-re változik.

...
   function negativ() // negatív kép létrehozása
   {
     var iData=rajzCt.getImageData(0,0,600,600);
     var l=iData.data.length;
     for(var i=0;i<l;i++)
       if (i%4!=3)
         iData.data[i]=255-iData.data[i];
     rajzCt.putImageData(iData,0,0);
   }

...
4. példa: pixelenkénti képmanipuláció.

További példák

x. példa: térkép rajzolás szövegfájl beolvasásával

y. példa: az előbbi térkép kiegészítése JSON formátumú adatok megjelenítésével

z. példa: Mandelbrot- és Julia-halmaz böngésző

...folyt.köv...