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';
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';
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>
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ń:
<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:
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:
Grafika bez przekształceń:
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';
CSI niech się od nas uczy :)
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:
naszImageData.data =[r,g,b,a, r,g,b,a, r,g,b,a, ....];szerokość_canvas * wysokość_canvas * 4 a każda jej komórka zawiera wartość z przedziału 0-255Wię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';
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';
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/