Canvas grafika
Funkcja drawImage()
Do rysowania danej grafiki na płótnie służy funkcja drawImage(), która występuje w 3 wariantach.
ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);dx, dy
pozycja na płótnie gdzie będziemy rysować
dWidth, dHeight
rozmiary rysowanej grafiki na płótnie
sx, sy
pozycja pobieranego wycinka na grafice źródłowej
sWidth, sHeight
rozmiary pobieranego wycinka z grafiki źródłowej
Kilka przykładów
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const image = new Image();
image.addEventListener("load", () => {
//proste rysowanie
ctx.drawImage(image, 0, 0);
//rysowanie z przeskalowaniem
ctx.drawImage(image, 40, 40, canvas.width - 80, canvas.height - 80);
//obwódka dla czytelności
ctx.rect(40, 40, canvas.width - 80, canvas.height - 80);
ctx.stroke();
});
image.src = "image.png";Najedź kursorem na grafikę:

Przy omawianiu tablic wielowymiarowych pokusiliśmy się o wygenerowanie planszy. Za pomocą powyższych informacji moglibyśmy wreszcie wykonać je jak należy.
Pattern
Podobnie jak przy tle w CSS, także dla canvas możemy użyć powtarzania tła. Służy do tego funkcja createPattern(img, powtarzanie). Funkcja ta przyjmuje parametry:
repeat
sposób powtarzania. Przyjmuje jedną z wartości: repeat, repeat-x, repeat-y i no-repeat
Zauważ, że nasz wzorek zaczyna się krzywo. Podobnie do transformacji aby wzór rozpoczynał się wraz z figurą, musimy posłużyć się funkcją translate:
Podobnie będzie przy pozostałych typach powtórzeń:
Manipulacja pikselami
Każda grafika rastrowa z canvasem włącznie to uporządkowany zbiór pikseli. Lewy górny róg to początek, a dolny prawy do koniec.
Aby manipulować poszczególnymi pikselami wykorzystamy do tego obiekt typu ImageData. Obiekt taki jest "zapisem grafiki" i zawiera 3 właściwości:
imageData.data
zwraca 1 wymiarową tablicę, której poszczególnymi wartościami są składowe rgba kolejnych pikseli czyli
imageData.data = [r,g,b,a, r,g,b,a, r,g,b,a, ....].
Zwracana tablica jest wielkości szerokośćCanvas * wysokośćCanvas * 4 a każda jej komórka zawiera wartość z przedziału 0-255
Element canvas udostępnia nam też metody, dzięki którym możemy obsłużyć piksele:
context.createImageData(innyImageData)
zwraca obiekt typu ImageData o wymiarach takich samych jakie ma obiekt przekazany w parametrze. Tylko wymiary są kopiowane. Dane o pikselach nie są kopiowane.
context.getImageData(x, y, width, height)
pobiera obiekt typu ImageData, który jest wycinkiem canvasu o wymiarach podanych w atrybutach. Jeżeli x i y nie są podane, wtedy przyjmują wartości 0
context.putImageData(ImageData, x, y)
rysuje na canvasie w pozycji x,y piksele z imageData.
Przykładowe manipulacje
Mając powyższe informacje, możemy pokusić się o przeprowadzenie prostych manipulacji na naszych grafikach. Jedną z nich jest chociażby odwracanie kolorów. Z powyższych informacji wiemy, że tablica imageData.data zawiera informacje o składowych r,g,b,a (w skali 0-255) kolejnych pikseli.
W podobieństwie do powyższej funkcji możemy równie łatwo wykonać inne manipulacje kolorami:
wyszarzenie kolorów rozjaśnienie kontrast
Generowanie na bazie płótna
Podejrzewam, że jeszcze nie do końca dostrzegasz olbrzymich mocy, jakie drzemią w powyższych funkcjach. Jeżeli na dane płótno wrzucisz jakąkolwiek grafikę, możesz następnie pobrać te dane i na ich podstawie robić dowolne efekty na stronie. Wrzucana grafika może pochodzić z wielu miejsc. Możesz więc ją rysować za pomocą odpowiednich funkcji, możesz ją pobrać za pomocą querySelector, możesz stworzyć grafikę i podać jej jakiś zewnętrzny adres, czy też pobrać ją bezpośrednio z video lub kamery internetowej.
Gdy taką grafikę wrzucisz na płótno o małych rozmiarach, a następnie pobierzesz z niego informacje o pikselkach, otrzymasz porcję danych idealnie nadającą się do generowania różnych efektów.
Sprawdźmy to. Powiedzmy, że mamy na stronie grafikę:

Na jej bazie chcemy wygenerować sobie nowy element składający się z kolorowych divów, gdzie każdy div będzie odpowiadał danemu pikselowi. Powyższa grafika ma rozmiar 530x530 co zmusiło by nas do wygenerowania 530 x 530 = 2809000 nowych divów. Dużo za dużo. Wrzućmy ją więc na jakiś mniejszy canvas:
Stwórzmy teraz element, w którym będziemy tworzyć divy oraz funkcję generującą odpowiedni html:
I tyle. Osobiście raczej nie polecam tworzyć takiej dużej liczby divów, a zamiast tego generować odpowiedni obraz na canvas, albo wygenerować wartość odpowiedniej właściwości (np. background, czy box-shadow).
Możemy też pokusić się o pójście o krok dalej, i wrzucać na canvas grafikę pobieraną bezpośrednio z input:file. W poniższym przykładzie dodatkowo przed wrzuceniem lekko przekształcę wynik korzystając z wcześniejszych funkcji:
Zamiast pojedynczych grafik możesz też generować całe animacje. Wystarczy płynnie pobierać kolejne klatki video, a następnie wrzucać je na płótno. W kolejnym rozdziale zobaczysz, że do takich rzeczy idealnie się nadaje funkcja requestFrameRate():
A możliwości stają się jeszcze większe, gdy dla takiego video będziesz pobierał obraz z kamerki (część poniższego kodu skopiowałem z tamtej strony).
Rozpocznij przechwytywanie
Na koniec przydało by się sam canvas i video ukryć dowolną techniką, a i odejście od generowania divów na rzecz czegoś innego na pewno mocno by poprawiło wydajność (generowanie tekstu, generowanie box-shadow, może rysowanie po canvas?). Swego czasu podobną techniką zrobiłem mini eksperyment związany z Bad Apple, gdzie właśnie korzystam z box-shadow.
Last updated