Tworzenie rysunków i animacji w przeglądarce internetowej jest bardzo przydatną umiejętnością kiedy zajmujemy się kodowaniem stron i programowaniem aplikacji internetowych. Często pojawia się potrzeba wykonania dynamicznych rysunków, które reagują na wprowadzone przez użytkownika dane lub na konkretne akcje – np. ruch myszą i kliknięcie.

Wiele kreatorów produktu, konfiguratorów, gier i wizualizacji danych korzysta właśnie z technologii Canvas. Pozwala ona za pomocą HTML i czystego JavaScriptu generować zarówno proste ale też skomplikowane kształty, gradienty i animacje. HTML Canvas pozwala załadować obrazek i wykonać w nim pewne modyfikacje. Modyfikacje takich obrazków nie ograniczają się do samych filtrów i predefiniowanych właściwości. W Canvas możemy dokonywać transformacji piksel po pikselu.

Co to jest HTML Canvas?

HTML Canvas to element technologii HTML5, która pozwala na dynamiczne i skryptowe renderowanie grafiki. Aktualizacja bitmapy przebiega proceduralnie i niskopoziomowo. Element <canvas> udostępnia własne API do rysowania i przetwarzania grafik 2D i wspiera WebGL API umożliwiając renderowanie grafiki 3D za pomocą OpenGL ES.

Krótka historia HTML Canvas

Element canvas został wprowadzony już w 2004 roku za sprawą firmy Apple, która chciała umożliwić tworzenie widżetów dla aplikacji Dashboard i rozszerzenia możliwości generowania grafiki w przeglądarce Safari. Później zaczęto wykorzystywać i obsługiwać <canvas> w przeglądarce Firefox, Opera i Google Chrome. Canvas stał się standardem w 2014 wraz z wprowadzeniem standardu HTML5. HTML 5 Canvas był jedną z trzech technologii nowej generacji. Pisałem o tym w 2014 roku w jednym z pierwszych artykułów na tym blogu: HTML5 – podstawy i technologie nowej generacji.

Tworzenie elementu Canvas w HTML

Sam element Canvas może być stworzony z poziomu kodu HTML. Deklaracja elementu canvas o podanej szerokości i wysokości może wyglądać tak:

<!DOCTYPE html>
<html lang="pl-PL">
<head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Canvas</title>
</head>
<body>
   <canvas id="canvas" width="640" height="480"></canvas>
</body>
</html>

Element <canvas> zostanie już wyrenderowany w przeglądarce. Na stronie bez żadnych stylów CSS, nie będzie jednak widoczny, ponieważ nie zawiera żadnej grafiki a kolor tła dziedziczy po elemencie nadrzędnym. Kiedy jednak klikniemy na nim prawym przyciskiem myszy, menu kontekstowe pozwoli zapisać canvas jako zwykły obrazek:

Pusty element HTML Canvas

Kanwa HTML składa się zatem z obszaru do rysowania, który zdefiniowaliśmy w kodzie HTML. Kod JavaScript może uzyskać dostęp do tego obszaru za pomocą pełnego zestawu funkcji rysowania podobnych do innych popularnych interfejsów API 2D, umożliwiając w ten sposób dynamiczne generowanie grafiki.

Przykład rysowania w HTML Canvas

Interakcja z elementem Canvas polega na zdefiniowaniu kontekstu renderowania, który określa, czy użyć Canvas API, WebGL czy WebGL2. Dla przykładu – narysujmy prostokąt. Aby uprościć kod, sam element canvas przypisujemy do zmiennej e zaś kontekst do zmiennej c:

<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.fillStyle = "#333333";
   c.fillRect(16, 16, 320, 160);
</script>

Kod odpowiedzialny za rysowanie na płótnie musi być wykonany po wczytaniu całej strony dlatego zaleca się dodawać kod JS na samym końcu kodu HTML (przed znacznikiem zamykającym body) lub stosować wykrywanie zdarzenia window.onload.

Wygeneruje nam to prostokąt w kolorze #333333 o szerokości 320px i wysokości 160px. Lewy góry wierzchołek prostokąta będzie umieszczony w punkcie 16;16. Pierwszy argument to wartość współrzędnej x punktu początkowego a drugi argument to wartość y punktu początkowego. Początek układu odniesienia znajduje się zatem w lewym górnym rogu.

Prostokąt narysowany za pomocą metody fillRect();

Należy pamiętać, że rysunki wykonane w technologii Canvas to bitmapy. Każdy kolejny piksel „nadpisuje” poprzedni piksel jaki znajdował się w tym konkretnym miejscu – być może stąd wzięła się nazwa „płótno”, które miało być analogią do płótna malarskiego.

Rysowanie linii w Canvas

Rozpocznijmy naukę korzystania z Canvas od najprostszych kształtów. Linia to prosty odcinek, mający swój początek i koniec:

Linia narysowana w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='black';
   c.moveTo(20,20);
   c.lineTo(620, 460);
   c.stroke();
</script>

Jeszcze raz prześledźmy jak działa kod. Wybraliśmy canvas w, w którym chcemy rysować za pomocą document.getElementById(). Zdefiniowaliśmy kontekst za pomocą getContext() czyli zdecydowaliśmy, że chcemy poruszać się w przestrzeni dwuwymiarowej (2d). Przed narysowaniem linii ustawiliśmy jej grubość na 8 pikseli za pomocą właściwości lineWidth. Jej kolor zmieniliśmy na czarny za pomocą właściwości strokeStyle. Ustawiliśmy nasz wirtualny pędzel w puncie 20,20 za pomocą moveTo() i narysowaliśmy prostą linię odrywając pędzel od płótna w punkcie 620,460 za pomocą lineTo(). Metoda stroke() wyświetla narysowaną linię.

Okazuje się, że lineWidth i strokeStyle to nie jedyne właściwości jakie można wykorzystać przy rysowaniu linii. Można wykorzystać styl zakończenia linii za pomocą właściwości lineCap. Możliwe wartości to: butt (domyślna), round (dodająca zaokrąglenie równe szerokości linii) i square (dodająca końcówkę o długości szerokości linii).

c.lineCap = 'round';

Warto wyrabiać sobie zawczasu nawyk parametryzowania wartości długości i szerokości w odniesieniu do wielkości canvasa. Spowoduje to, że nasze rysunki będą responsywne i będą wypełniać cały canvas. W naszym przykładzie można tego dokonać za pomocą e.width – co daje nam szerokość canvasu oraz e.height co zwróci nam jego wysokość.

Linię, która pójdzie po skosie – niezależnie od wielkości naszego płótna można narysować za pomocą takiego kodu:

c.moveTo(0,0);
c.lineTo(e.width, e.height);

Rysowanie łuku w Canvas

Narysowanie łuku to nieco trudniejsze zadanie, bo jest on rysowany na podstawie okręgu, któremu definiujemy punkt środkowy, promień oraz kąt początkowy łuku i jego punkt końcowy:

Łuk narysowany w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='orange';
   c.fillStyle='blue';
   c.fillRect(0, 0, e.width, e.height);
   c.arc(e.width / 2, e.height / 2, 200, 1.2 * Math.PI, 1.8 * Math.PI, false);
   c.stroke();
</script>

Wartości e.width/2 i e.height/2 skierowują środek okręgu na środek sam środek płótna. Wartość 200 to promień. Kolejne dwa argumenty to kąty. Zachęcam do „pobawienia się” tymi wartościami i sprawdzenia jak wpływają na kształt łuku. Zauważymy wtedy, że rysowanie łuku odbywa się od wartości 0π z prawej strony okręgu do 2π zataczając pełny okrąg.

Ostatni parametr false instruuje Canvas aby rysowanie łuku odbywało się w prawo czyli zgodnie z ruchem wskazówek zegara. Wartość true spowoduje rysowanie łuku w kierunku przeciwnym do ruchu wskazówek zegara, czyli w lewo. Dla urozmaicenia, dodaliśmy też znajomy nam już prostokąt, który stanowi za tło dla naszego nowego dzieła.

Rysowanie krzywej kwadratowej w Canvas

Krzywa kwadratowa pozwala na znacznie większą elastyczność.

Krzywa kwadratowa narysowana w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.moveTo(e.width/8, e.height - e.height/8);
   c.quadraticCurveTo(e.width/2, -300, e.width - e.width/8, e.height-e.height/8);
   c.stroke();
</script>

Jak widać, krzywa kwadratowa jest definiowana przez punkt początkowy, punkt kontrolny i punkt końcowy.

Rysowanie krzywej Béziera w Canvas

Krzywe Béziera są powszechnie stosowane w projektowaniu i grafice komputerowej.

Krzywa Béziera narysowana w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.moveTo(80, 240);
   c.bezierCurveTo(320,0, 320,480, 560,240);
   c.stroke();
</script>

Dla zwiększenia czytelności, kodu wprowadziłem wartości w postaci liczb naturalnych. Krzywa Béziera w tym przykładzie jest trzeciego stopnia. Jest ona definiowana poprzez punkt początkowy, 2 punkty kontrolne i punkt końcowy końcowy.

Rysowanie łamanej w Canvas

Łamane można rysować za pomocą podścieżek.

Łamana narysowana w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.beginPath();
   c.moveTo(80,80);
   c.lineTo(160,390);
   c.lineTo(240,80);
   c.lineTo(320,390);
   c.lineTo(400,80);
   c.lineTo(480,390);
   c.lineTo(560,80);
   c.stroke();
</script>

Możemy wykorzystać przewagę pochodzącą z faktu, że posługujemy się kodem i tworzyć pętle, funkcje i procedury, która zautomatyzują rysowanie bardziej złożonych kształtów:

Rysowanie łamanej za pomocą pętli for w technologii Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.beginPath();
   var startX = 80;
   var stepX = 20;
   c.moveTo(startX,80);
   for(var i = 0; i< 24; i++ ){
      if(i % 2 == 0){
         c.lineTo(startX+stepX*i, 80);
      } else{
         c.lineTo(startX+stepX*i, 390);
      }
   }
   c.stroke();
</script>

Jeżeli nie wiesz co się tutaj stało zapraszam do kursu JavaScript od postaw, gdzie tłumaczyłem jak działają pętle w JavaScript.

Mamy wpływ na sposób łączenia się linii. Za pomocą opcji lineJoin możemy określić czy łączenie ma być szpiczase – 'miter’, zaokrąglone – ’round’ czy ukośne – 'bevel’. Na przykład:

c.lineJoin = 'round'; 

Rysowanie spirali w Canvas

Spiralę możemy narysować za pomocą zwykłej metody lineTo(). Wystarczy wykorzystać pętlę i wiadomości z podstaw matematyki.

Spirala narysowana w Canvas

Spirala jest narysowana za pomocą sekwencji bardzo krótkich linii. Każda linia zaczyna się na końcu poprzedniej. Zachęcam pozmieniać parametry i sprawdzić jak zachowuje się kształt generowanej spirali.

<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.beginPath();
   var r = 0;
   var angle = 0;
   c.moveTo(e.width/2, e.height/2);
   for(var i = 0; i < 450; i++){
      r = r + 0.5;
      angle = angle + (Math.PI * 2) / 100;
      var x = e.width/2+r*Math.cos(angle);
      var y = e.height/2 + r * Math.sin(angle);
      c.lineTo(x,y);
   }
   c.stroke(); 
</script>

Generowanie tekstu w Canvas

Generowanie tekstu w Canvas da nam wiele praktycznych możliwości. Zacznijmy od praktycznego standardowego Hello World!

Wygenerowany tekst w Canvas

Nazewnictwo właściwości i metod chyba nie wymaga wyjaśnień. Istnieje jeszcze metoda, która wygeneruje obrys napisu. Wystarczy podmienić ostatnią linię na:

c.strokeText('Hello World!', e.width/2, e.height/2);

Można wykorzystać obie metody i wygenerujemy wtedy wypełniony napis i obrys – np. w innym kolorze. Na wygląd linii mamy wpływ za pomocą znanych nam już właściwości lineWidth i strokeStyle.

Tekstom wygenerowanym w canvasie można w prosty sposób dodać cień za pomocą kilku opcji.

<script>
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   
   c.font = '64px Arial';
   c.fillStyle='#555555';
   c.textAlign = 'center';
   c.textBaseline = 'middle';
   
   c.shadowColor='#6666ff';
   c.shadowBlur=10;
   c.shadowOffsetX = 6;
   c.shadowOffsetY = 6;
   
   c.fillText('Hello World!', e.width/2, e.height/2);
</script>

Rysowanie prostokąta w Canvas

Prostokąt to najprostsza figura geometryczna jaką można narysować w Canvas. Rysowaliśmy go w każdym przykładzie za pomocą metody fillRect() aby nadać szare tło obrazkowi. Teraz poznamy metodę rect():

Prostokąt narysowany w Canvas
<script>
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   c.strokeStyle='#333';
   c.fillStyle='#ffffff';
   c.rect(e.width/4,e.height/4,e.width/2,e.height/2);
   c.fill();
   c.stroke();
</script>

Dwa pierwsze argumenty funkcji rect() to punkty x i y lewego górnego wierzchołka. Pozostałe dwa argumenty to odpowiednio szerokość i wysokość.

Dodając ostatnią linię c.fill() wypełniliśmy prostokąt kolorem białym (#ffffff). Zwróć uwagę, że gdy zamienimy kolejność dwóch ostatnich linii, wpłynie to na grubość obramowania, bo w pierwszym przypadku jest zakrywane przez wypełnienie a w drugim to obramowanie zakrywa część wypełnienia.

Rysowanie koła i okręgu w Canvas

Okrąg, potrafimy już rysować – wystarczy zatoczyć łuk w 360 stopniach czyli od wartości 0 do 2π.

Okrąg i koło narysowane w Canvas
<script>
   c.lineWidth = 8; 
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   c.strokeStyle='#333333';
   c.fillStyle='#ffffff';
   c.arc(e.width / 2, e.height / 2, e.height/4, 0 * Math.PI, 2 * Math.PI, false);
   c.fill();
   c.stroke();
</script>

Rysowanie trójkątów i innych figur geometrycznych w Canvas

Canvas nie posiada, żadnych innych funkcji do rysowania innych figur geometrycznych. Praktycznie tylko prostokąt możemy narysować za pomocą wygodnej funkcji rect(). Możemy jednak łatwo narysować dowolny kształt zamknięty i wypełnić go kolorem za pomocą ścieżek, którymi rysowaliśmy łamane.

Trójkąt narysowany w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, e.width, e.height);
   c.beginPath();
   c.moveTo(e.width/4,e.height/4*3);
   c.lineTo(e.width/2,e.height/4);
   c.lineTo(e.width/4*3,e.height/4*3);
   c.closePath();
   c.fillStyle='#fff';
   c.fill();
   c.stroke();
</script>

Kluczowa jest tutaj funkcja closePath(). W ten sposób możemy narysować dowolny zamknięty kształt w Canvas. Brak dedykowanych funkcji do rysowania powtarzalnych kształtów może być problematyczne. Nic nie stoi jednak na przeszkodzie aby stworzyć własne funkcje z parametrami, w których przekażemy punkty wierzchołków porządanego trójkąta lub innej figury. Na przykład:

function triangle(x1,y1,x2,y2,x3,y3){
   c.beginPath();
   c.moveTo(x1,y1);
   c.lineTo(x2,y2);
   c.lineTo(x3,y3);
   c.closePath();
}
triangle(160,360, 320,120, 480,360);

Zmiana styli Canvas

Chcąc narysować dwa prostokąty nałożone na siebie w różnych kolorach wystarczy przed zarysowaniem każdego z nich zmienić kolor za pomocą włąsciwości fillStyle():

Dwa prostokąty nałożone na siebie w canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   c.strokeStyle='#333';
   c.fillStyle='#4444aa';
   c.fillRect(e.width/4+e.width/16,e.height/4+e.height/16,e.width/2/2,e.height/2/2);
   c.fillStyle='#44aa44';
   c.fillRect(e.width/4*2-e.width/16,e.height/4*2-e.height/16,e.width/2/2,e.height/2/2);
</script>

Okazuje się, że nie uzyskamy takiego efektu kiedy skorzystamy z połączenia metod c.rect() oraz c.fill(); Bierze się to stąd, że wcześniej stworzony prostokąt jest rysowany ponownie drugim wywołaniem metody c.fill() z ostatnio ustawionym kolorem kontekstu. Aby temu zapobiec, wystarczy przed narysowaniem drugiego prostokąta wywołać funkcję c.beginPath().

<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.lineWidth = 8;
   c.strokeStyle='#353433';
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   c.strokeStyle='#333';
   c.fillStyle='#4444aa';
   c.rect(e.width/4+e.width/16,e.height/4+e.height/16,e.width/2/2,e.height/2/2);
   c.fill();
   c.fillStyle='#44aa44';
   c.beginPath();
   c.rect(e.width/4*2-e.width/16,e.height/4*2-e.height/16,e.width/2/2,e.height/2/2);
   c.fill();
</script>

Modyfikując style wiele razy możemy skorzystać z metod save() i restore(). Style umieszczone pomiędzy tymi instrukcjami będą działać tak jakby tymczasowo. Linia restore() przywraca wtedy wcześniej zapisany styl kontekstu.

Operacje złożone w Canvas

Kształty w Canvas można poddawać operacjom logicznym. W poprzednim przykładzie mieliśmy dwa kwadraty. W tym przykładzie wydzielimy część wspólną, sumę kształtów lub inne operacje jakie można robić z bryłami w świecie 3D i kształtami 2D w programach do obróbki grafiki.

Operacja XOR na dwóch prostokątach nałożonych na siebie.
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.globalCompositeOperation = 'xor';
   c.fillStyle='#4444aa';
   c.rect(e.width/4+e.width/16,e.height/4+e.height/16,e.width/2/2,e.height/2/2);
   c.fill();
   c.fillStyle='#44aa44';
   c.beginPath();
   c.rect(e.width/4*2-e.width/16,e.height/4*2-e.height/16,e.width/2/2,e.height/2/2);
   c.fill();
</script>

Sprawdź jak wyglądają w tym przykładzie tryby:

  • source-out,
  • destination-atop,
  • destination-over,
  • lighter.

Pełną listę wartości dla globalCompositeOperation można znaleźć tutaj: globalCompositeOperation

Wyświetlanie obrazków w Canvas

W Canvas możemy wyświetlić obrazy i wideo w różnych popularnych formatach. Na sam początek zajmijmy się obrazkami.

Wyświetlanie obrazka jpg w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   var image = new Image();
   image.src='photo.jpg';
   image.onload = function(){
      c.drawImage(this, 0,0);   
   }
</script>

Metoda drawImage() rysuje obrazek i umieszcza go w podanych punktach – w tym przypadku w 0; 0. Niezależnie od wielkości płótna i obrazka, „przenoszony jest” piksel po pikselu. Z tego powodu obrazek nie będzie widoczny w całości, kiedy płótno jest mniejsze od obrazka.

Skalowanie obrazka w Canvas

Aby dopasować grafikę do płótna, można zmniejszyć jej rozmiar za pomocą dwóch dodatkowych argumentów, które są odpowiedzialne za docelową szerokość i wysokość obrazka.

Skalowanie obrazka jpg w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   var image = new Image();
   image.src='photo.jpg';
   image.onload = function(){
      c.drawImage(this, 0,0, 640, 480);   
   }
</script>

Kadrowanie obrazków w Canvas

Funkcja drawImage() pozwala także kadrować zdjęcie źródłowe i umieścić je na płótnie. W przypadku podania dziewięciu argumentów najpierw określamy początek i rozmiar obrazka źródłowego a potem określamy początek i rozmiar obrazka docelowego.

Kadrowanie obrazka w Canvas
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.fillStyle='#e1e2e3';
   c.fillRect(0, 0, 640, 480);
   var image = new Image();
   image.src='photo.jpg';
   image.onload = function(){
      c.drawImage(this, 150, 150, this.width-300, this.height-300, 0, 0, e.width, e.height);   
   }
</script>

Źródłowy obrazek photo.jpg ma rozmiar: 1280x854px; Możemy te wartości otrzymać za pomocą własności this.width oraz this.height. W powyższym przykładzie wycięliśmy 150 pikseli obrazka z każdej jego krawędzi.

Pobieranie informacji z plików graficznych w Canvas

W Canvas możemy zbierać informację o plikach graficznych takie jak

Odczytywanie danych o plikach odbywa się za pośrednictwem systemu plików dlatego kolejne przykłady muszą być uruchamiane na serwerze (np. lokalnym lub zewnętrznym) z powodu polityki bezpieczeństwa.

Pobieranie informacji o obrazku w Canvas
<script>
var e = document.getElementById('canvas');
var c = e.getContext('2d');
var image = new Image();
image.src='photo.jpg';
image.onload = function(){
  c.drawImage(this, 0, 0, this.width, this.height, 0, 0, e.width, e.height);
  var imageData = c.getImageData(0, 0, e.width, e.height);
  c.font = "16px Arial";
  c.textBaseline = 'bottom';
  c.fillText('W: '+imageData.width + ', H: '+imageData.height + ', Rozmiar: ' + imageData.data.length ,16,32);
}
</script>

Obiekt imageData zawiera informacje o każdym pikselu obrazka, co może być przydatne w przetwarzaniu i zaawansowanej edycji w środowisku Canvas. Można podejrzeć jak to wygląda za pomocą:

console.log(imageData.data);

Zapisanie płótna na postać danych URL

Grafikę możemy przekształcić na postać danych URL co jest bardzo przydatne w przypadku aplikacji internetowych do przetwarzania grafiki. Narysujmy czarny kwadrat na szarym tle płótna i wygenerujmy na jego podstawie dane URL:

Przykładowy rysunek przeznaczony do zapisu w firmie danych URL w formacie PNG
<script>
   var e = document.getElementById('canvas');
   var c = e.getContext('2d');
   c.fillStyle="#eeeeee";
   c.fillRect(0,0, e.width, e.height);
   c.fillStyle="#000000";
   c.fillRect(e.width/2-100,e.height/2-100, 200, 200);
   var dataURL = e.toDataURL();
   console.log(dataURL);
</script>

Całą sprawę załatwia metoda toDataURL(). Domyślnie zapisuje ona płótno za pomocą formatu PNG i zwraca ciąg znaków, który można dalej przetwarzać w naszej aplikacji internetowej.

Dane URL obrazka w formacie PNG zapisane w base64 do przesłania za pomocą URL

Takie dane możemy też przekazać do elementu <img> np. w celu podglądu grafiki. Dodajmy na stronę pusty element img z identyfikatorem preview:

<img src="" id="preview">

Dane możemy teraz wkleić jako wartość atrybutu src:

document.getElementById('preview').src = dataURL;

Możemy też wykorzystać te dane aby wkleić je do innego płótna. Tworzymy drugie płótno z identyfikatorem canvas2 i dodajemy taki kod:

var e2 = document.getElementById('canvas2');
var c2 = e2.getContext('2d');
var image = new Image();
image.src=dataURL;
image.onload = function(){
   c2.drawImage(this, 0, 0, this.width, this.height, 0, 0, e.width, e.height); 
}

Bardzo łatwo teraz stworzyć płótno, które będzie pobierało dane z adresu URL. Kiedy prześlemy dane w adresie URL przykładowo w taki sposób:

canvas.html?dataURL=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...

Wystarczy pobrać zmienną dataURL w taki sposób:

var params = (new URL(document.location)).searchParams;
var dataURL = params.get("dataURL");

Zmienną dataURL możemy teraz wykorzystać jako dane obrazka:

var e = document.getElementById('canvas2');
var c = e.getContext('2d');
var image = new Image();
image.src=dataURL;
image.onload = function(){
   c.drawImage(this, 0, 0, this.width, this.height, 0, 0, e.width, e.height); 
}

Przekształcenia w Canvas

Większość programów graficznych pozwala wykonać pewne przekształcenia – nie inaczej jest w Canvas. W HTML5 Canvas przekształceniu ulega kontekst.

Przesuwanie obiektów w Canvas

Na samym początku zobaczmy jak działa translacja czyli przesuwanie względem osi pionowej albo poziomej.

Przekształcenie translate (przesunięcie) w Canvas
var e = document.getElementById('canvas');
var c = e.getContext('2d');
c.lineWidth=8;
c.strokeStyle="#000000";
c.fillStyle="#eeeeee";
c.fillRect(0,0, e.width, e.height);
c.fillStyle="#ffffff";
c.fillRect(e.width/2-100,e.height/2-100, 200, 200);
c.translate(50, 50);
c.strokeRect(e.width/2-100,e.height/2-100, 200, 200);

W tym przykładzie przesunęliśmy kontur kwadratu o 50 pikseli w prawo i 50 pikseli w dół. Odpowiedzialna za to jest oczywiście metoda translate().

Obrót w Canvas

Obrót to kolejne często spotykane przekształcenie w środowiskach graficznych.

Obrót w Canvas

Jak widać obrót jest wykonywany względem punktu 0,0 płótna. Aby kwadrat po obrocie pozostał na środku, będziemy musieli skorzystać najpierw z przesunięcia początku kontekstu na środek, obrotu i powrotnego przesunięcia kontekstu do początkowego stanu 0,0.

Obrót figury wokół jej środka w Canvas
var e = document.getElementById('canvas');
var c = e.getContext('2d');
c.lineWidth=8;
c.strokeStyle="#000000";
c.fillStyle="#eeeeee";
c.fillRect(0,0, e.width, e.height);
c.fillStyle="#ffffff";
c.strokeRect(e.width/2-100,e.height/2-100, 200, 200);
c.translate(e.width/2,e.height/2);
c.rotate(45*Math.PI/180);
c.translate(-e.width/2,-e.height/2);
c.fillRect(e.width/2-100,e.height/2-100, 200, 200);

Skalowanie w Canvas

Za skalowanie odpowiada metoda scale, która przyjmuje 2 argumenty.

Skalowanie w Canvas
var e = document.getElementById('canvas');
var c = e.getContext('2d');
c.lineWidth=8;
c.strokeStyle="#000000";
c.fillStyle="#eeeeee";
c.fillRect(0,0, e.width, e.height);
c.moveTo(0,e.height);
c.lineTo(e.width, 0);
c.stroke();
c.fillStyle="#ffffff";
c.scale(0.5, 0.5);
c.fillRect(0,0, e.width, e.height);
c.moveTo(0,e.height);
c.lineTo(e.width, 0);
c.stroke();

Odbicie lustrzane w Canvas

Odbicie lustrzane łatwo osiągnąć za pomocą negatywnych wartości funkcji scale();

Odbicie lustrzane w Canvas
var e = document.getElementById('canvas');
var c = e.getContext('2d');
c.fillStyle='#e1e2e3';
c.fillRect(0, 0, e.width, e.height);
c.translate(e.width/2, e.height/2);
c.font = '64px Arial';
c.fillStyle='#555555';
c.textAlign = 'center';
c.textBaseline = 'middle';
c.shadowColor='#6666ff';
c.shadowBlur=10;
c.shadowOffsetX = 6;
c.shadowOffsetY = 6;
c.scale(-1,1);
c.fillText('Hello World!', 0,0);

Własne transformacje metoda transform() w Canvas

W Canvas istnieje jeszcze metoda transform(), która pozwala stworzyć własną macierz przekształcenia.

Metoda transform w Canvas
var e = document.getElementById('canvas');
var c = e.getContext('2d');
c.fillStyle='#e1e2e3';
c.fillRect(0, 0, e.width, e.height);
c.translate(e.width/2, e.height/2);
c.font = '64px Arial';
c.fillStyle='#555555';
c.textAlign = 'center';
c.textBaseline = 'middle';
c.transform(1, 0.2, 0.8, 1, 0, 0);
c.fillText('Hello World!', 0,0);

Czytaj więcej na temat własnych przekształceń w dokumentacji:

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform

Oceń artykuł na temat: HTML5 Canvas – kurs od podstaw
Średnia : 4.9 , Maksymalnie : 5 , Głosów : 14