Strona początkowa

Obrazki w canvas

Rysowanie prostych kształtów w poznany sposób nie jest problemem. Jednak przy bardziej skomplikowanych grafikach o wiele lepiej jest przygotować obrazek w jakimś programie graficznym i wczytać go na nasze płótno. Zewnętrzne obrazki mogą przyjmować standardowe rozszerzenia czyli GIF, JPEG i PNG. Osobiście polecam ten ostatni format - ze względu na obsługę 255 poziomowej poziomowej przezroczystość.

Aby narysować obrazek na płótnie musimy wykonać 2 kroki:

Podstawowy wariant użycia metody drawImage(image, x, y) przyjmuje następujące atrybuty:
image - określa obrazek, który będziemy rysować na płótnie,
x, y - określają położenie rysowanego obrazka na płótnie.

Aby mieć pewność, że rysowany obrazek jest już w pełni załadowany skorzystamy dla niego ze zdarzenia onload.

Koniec teorii - czas na praktykę

	var obrazek = new Image();		
		obrazek.onload = function(){
			// Rysujemy
		}
		obrazek.src = 'super_grafika.png';
	

Zauważ kolejność deklaracji zdarzenia onload i ustawiania parametru src.
Parametr src powinien być ustawiany PO ustawieniu zdarzenia onload. Czemu? Wskazanie src powoduje, że przeglądarka zaczyna wczytywać daną grafikę. Jeżeli obrazek znajduje się w cache przeglądarki, wtedy jest załadowany natychmiastowo. Tak więc obrazek zostaje załadowany, ale nasz skrypt nie ustawił jeszcze przypisanego zdarzenia onload.

W przeglądarkach pracujących na silniku Webkit (np Chrome) możemy pobrać wymiary obrazka dopiero PO całkowitym jego wczytaniu (czyli dopiero po wykonaniu zdarzenia onload) - dlatego warto się trzymać powyższej kolejności.

Skoro jesteśmy już specjalistami w wczytywaniu grafiki, możemy pokusić się o narysowanie prostego wykresu:

    var c = document.getElementById('canvas_slupek').getContext('2d');
	
    var tlo = new Image();		
		tlo.onload = function(){
				
			//rysujemy wczytaną grafikę z tłem
			c.drawImage(tlo,0,0);			

			for (i=0; i<8; i++) {			

				//słupki zaczynamy rysować od 30, każdy kolejny słupek rysujemy co 80px, 
				//a od wyliczonego punktu odejmujemy polowę słupka czyli 10
				var x_slupka = 30 + (i*80) - 10; 
				
				c.fillStyle = "rgba(193, 228, 252, 1)";	//ustawiamy kolor słupka
				
				//slupek rysujemy od pozycji 
				// x = x_slupka, 
				//y = 115 
				//rysujemy w górę - dlatego wysokość słupka (ostatni parametr) jest ujemna (-70)
				
				c.fillRect(x_slupka, 115, 20, -70);
			}
			
		}
		
		tlo.src = 'background.gif';
	
Twoja przeglądarka nie obsługuje canvas

Oczywiście w tak prostej formie nasz skrypt raczej nie będzie przydatny. Dodaj my więc do niego jakieś sensowne dane i wyrysujmy je:

    var c = document.getElementById('canvas_slupki').getContext('2d');
	
	//sensowne dane do rysowania :)
	var dane = [100, 80, 70, 20, 10, 50]; //dane które narysujemy
	
	var width_wykresow = 560; //nie chcemy zajmować całej powierzchni obrazka
	var od_x = 600 - 560; //pierwszy słupek rysujemy od jakiegoś miejsca
	var co_ile = Math.round(560 / dane.length)-10; //co ile będziemy rysować kolejny słupek
			
    var tlo = new Image();		
	
		//ustawiamy zdarzenie onload
		tlo.onload = function(){
				
			c.drawImage(tlo,0,0);					

			//prosta pętla po tablicy danych
			for (i=0; i<dane.length; i++) {				
				//cien słupka
				var x_slupka = od_x + (i*co_ile) - 20 //20 to polowa szerokości slupka. 
				
				//cień dla słupków - zwykły słupek nieco przesunięty
				c.fillStyle = "rgba(0, 0, 0, 0.3)"; //ustawiamy kolor dla cienia								
				c.fillRect(x_slupka, 115, 45, -dane[i]); //rysujemy słupek cienia
								
				c.fillStyle = "rgba(193, 228, 252, 1)";	//ustawiamy kolor słupka							
				c.fillRect(x_slupka, 115, 40, -dane[i]); //rysujemy słupek
			}
			
		}
		
		tlo.src = 'background.gif';
	
Twoja przeglądarka nie obsługuje canvas

Równie dobrze możemy zastosować inne receptury do rozmieszczania takich słupków. Dodatkowo możemy upiększyć nasz wykres poprzez dodanie innych kolorów - trzymanych np w dodatkowej tablicy. Poniżej zamieszczam przykładową implementację tego rozwiązania. Największym problemem jest tutaj rzutowanie danej wartości na tablicę kolorów, która przecież długością wcale nie musi się pokrywać z tablicą danych.

	<canvas width="500" height="150" id="canvas_slupki_kolor">Twoja przeglądarka nie obsługuje canvas</canvas>
	
	<script>
    var c = document.getElementById('canvas_slupki_kolor').getContext('2d');
	var dane = [100,80,70,20,10,50,30,90,43];	
	var max = 100; //dla ułatwienia maksymalną wartość ustawiamy z palca
	var min = 10; //to samo dla min
	
	var kolory = ['#d31111','#f55625','#f5bd25','#f5e925','#c6f525'];
	
	//dana wielkość musi się zawierać w jakimś przedziale - np 0-25 - to pierwszy kolor, 25-50 - drugi kolor itp.
	var przedzial = Math.floor(max / kolory.length);
	var width_wykresow = 560;
	var od_x = 600 - 560;
	var co_ile = Math.round(560 / dane.length)-10;
			
	var tlo = new Image();		
		tlo.onload = function(){	
		
			c.drawImage(tlo,0,0);
			
			for (i=0; i<dane.length; i++) {				
				var x_slupka = od_x + i*co_ile - 20
				c.fillStyle = "rgba(0, 0, 0, 0.3)";
				c.fillRect(x_slupka,115,45,-dane[i]);
				
				var kolor = kolory[ Math.floor((dane[i]-min) / przedzial) ]
			
				c.fillStyle = kolor;				
				c.fillRect(x_slupka,115,40,-dane[i]);
			}
		}
	tlo.src = 'background.gif';	
	</script>
	
Twoja przeglądarka nie obsługuje canvas

Skalowanie grafiki

Kolejny wariant użycia metody drawImage ma następującą postać:

drawImage(image, x, y, width, height) - pierwsze atrybuty już znamy. Dodatkowe width i height określają nowe rozmiary rysowanego obrazka. Możemy je wykorzystać do skalowania obrazka.

Grafika bez przekształceń:

domek

	<canvas width="160" height="160" id="canvas_scale">Twoja przeglądarka nie obsługuje canvas</canvas>
	
	var c = document.getElementById('canvas_scale').getContext('2d');
	
	var house = new Image();		
		house.onload = function(){
			//wczytujemy tło
			for (x=0; x<4; x++) {
				for (y=0; y<4; y++) {
					c.drawImage(angel, x*40,y*40,40,40);
				}
			}			
			c.drawImage(angel,40,40,80,80);
		}
		house.src = 'domek.jpg';
	

Po przekształceniach i narysowaniu na płótnie:

Twoja przeglądarka nie obsługuje canvas

Wycinanie kawałka grafiki

Ostatni wariant metody drawImage służy do wycinania kawałka grafiki i rysowaniu go na płótnie.

Wariant ten ma postać:

drawImage(image, sx, sy, swidth, sheight, dx, dy, dWidth, dHeight): atrybut image znamy. Atrybuty sx, sy, swidth, sheight określają położenie oraz rozmiar obszaru do wycięcia na obrazku źródłowym. Atrybuty dx, dy, dWidth, dHeight określają położenie oraz rozmiar wyciętego wycinka nałożonego na canvas. Najlepiej zilustruje to schemat:

crop canvas

Grafika bez przekształceń:

Aniołek

Wycinamy pani twarzyczkę i wrzucamy na płótno:

    var c = document.getElementById('canvas_croop').getContext('2d');
    var angel = new Image();		
		angel.onload = function(){
			c.drawImage(angel, 78, 31, 80, 80, 10, 10, 160, 160);
		}
		angel.src = 'aniolek.jpg';
	
Twoja przeglądarka nie obsługuje canvas

CSI niech się od nas uczy :)

Manipulacja pixelami

Jeżeli popatrzymy na nasze płótno dojdziemy do wniosku, że jest to w zasadzie uporządkowany zbiór pixeli. Lewy górny róg to początek, a dolny prawy do koniec.
I tak jest w rzeczy samej!
Aby manipulować poszczególnymi pixelami wykorzystamy do tego obiekt typu ImageData. Obiekt taki jest "zapisem grafiki" i zawiera 3 właściwości: width, height i data. Width i height to nic innego jak wymiary "grafiki". Data zawiera tablicę danych o poszczególnych pixelach.

Element canvas udostępnia nam metody, dzięki którym możemy obsłużyć pixele:

Więcej informacji o ImageData znajdziesz pod adresem https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas.

Przykładowe użycie powyższych metod pokazuje poniższy skrypt.
Rysujemy na płótnie super Fantomasa - to już znamy z porrzedniego przykładu.
Następnie pobieramy dane z naszego płótna do zmiennej myImgData. Pobieramy jej poszczególne składowe RGBa (do zmiennej data) i modyfikujemy każdą z nich.
Po tej operaji zapisujemy nasz zmodyfikowany obiekt myImgData na płótno.

        var canvas = document.getElementById('canvas_pxls');
        var c = canvas.getContext('2d');
        var fantomek = new Image();

		fantomek.onload = function(){

            //w pozycji 0,0 rysujemy oryginalny obraz
			c.drawImage(fantomek, 0, 0);

            //pobieramy dane z płótna
            var myImgData = c.getImageData(0, 0, canvas.width, canvas.height);

            //manipulujemy kolorami - odwracamy je :)
            for (i=0, i<myImgData.data.length; i+=4) {
                myImgData.data[i] =   255 - myImgData.data[i]; //czerwony
                myImgData.data[i+1] = 255 - myImgData.data[i+1]; //zielony
                myImgData.data[i+2] = 255 - myImgData.data[i+2]; //niebieski
                // i+3 to alpha koloru
            }

            //w pozycji 276,0 rysuejmy zmieniony obraz
            c.putImageData(myImgData, 276, 0);
        }

		fantomek.src = 'fantomus.jpg';
    
Twoja przeglądarka nie obsługuje canvas

Powyższy przykład może i działa, ale nie robi tego zbyt efektownie. Czemu?
Głównie dlatego, że manipulacje kolorami przeprowadzamy BEZPOŚREDNIO na płótnie.
Wiemy już, że wszystkie operacje wykonywane na wyświetlanych na stronie elementach są bardzo wolne. Powyższe działanie nie jest wyjątkiem. Aby to poprawić, wystarczy przeprowadzić standardowy manewru super bohaterów, czyli podstawić pobraną "myImgData.data" podstawić pod zmienną. Pokazuje to poniższy bardzo podobny przykład. Tym razem zamiast odwracać kolory postaramy się je "wyrównać".

        var canvas = document.getElementById('canvas');
        var c = canvas_pxl2.getContext('2d');
        var fantomek = new Image();

		fantomek.onload = function(){
			c.drawImage(fantomek, 0, 0);

            //pobieramy dane z płótna
            var myImgData = c.getImageData(0, 0, canvas.width, canvas.height);

            //pobieramy tablicę z kolorami powyższego obiektu i podstawiamy ją pod zmienną
            var data = myImgData.data;

            //manipulujemy kolorami
            for (i=0; i<data.length; i+=4) {
                var r = data[i];
                var g = data[i+1];
                var b = data[i+2];
                var srednia = (r+g+b) / 3
                data[i] = data[i+1] = data[i+2] = srednia;
            }

            //naszą zmienną data wstawiamy ponownie do myImgData.data
            myImgData.data = data;

            //rysujemy na płótnie nasz zmieniony obraz
            c.putImageData(myImgData, 276, 0);
        }

		fantomek.src = 'fantomus.jpg';
    
Twoja przeglądarka nie obsługuje canvas

Małe, proste podstawienie pod zmienną, a szybkość działania znacznie się zwiększa. Więcej na ten temat możecie znaleźć tutaj: http://www.onaluf.org/en/entry/13

Ciekawy tutorial o działaniu na pixelach canvasu możesz znaleźć pod adresem: http://net.tutsplus.com/tutorials/javascript-ajax/canvas-from-scratch-pixel-manipulation/