HTML5 & CSS3

HTML5 Canvas – podstawy

Ostatnio przyszło mi napisać o wykorzystaniu grafiki wektorowej w prostej animacji. Tym razem również o grafice na stronach, ale rastrowej, a jeśli mowa o grafice rastrowej to nie sposób nie zatrzymać się przy kanwach. Element canvas umożliwia nam tworzenie grafiki rastrowej na stronach internetowych i jest relatywnie nowym elementem. To co daje mu przewagę nad prozaicznym elementem img to możliwość dynamicznego manipulowania obrazem i bynajmniej nie chodzi tu o animacje do jakich przyzwyczaiły nas, niestety nadal popularne, gify (zwłaszcza ta część Internetu bogata w koty i drogowe perypetie naszych sąsiadów za wschodniej granicy). Element canvas daje nam możliwość manipulowania obrazem w czasie rzeczywistym dzięki wykorzystaniu języka JavaScript do tego stopnia by tworzyć za jego pomocą proste gry. Nie od razu Rzym zbudowano, więc skupimy się na podstawach.

HTML

Tworząc element canvas dobrze jest zdefiniować za pomocą atrybutów stałą szerokość i wysokość obrazu. Nie da się tego zrobić za pomocą CSS, w ten sposób będziemy jedynie skalować obraz.

<canvas width="150" height="150"></canvas>

JavaScript

By zacząć rysowanie musimy pobrać kontekst (nam starczy dwu wymiarowy) elementu canvas. Jak widać na poniższym kodzie, stworzenie rozwiązania zastępczego dla przeglądarek nie wspierających canvas  (IE 8.0 i starsze) jest dziecinnie proste i spokojnie obejdziemy się bez Modernizr.

canvas_line = document.getElementById('line');
if (canvas_line.getContext)
{
  var c = canvas_line.getContext('2d');
  c.beginPath();
  c.moveTo(20,20);
  c.lineTo(130,130);
  c.moveTo(130,20);
  c.lineTo(20,130);
  c.stroke();
} else {
  //rozwiązanie zastępcze
}

UWAGA: Poniższe funkcje nie są metodami, ani właściwościami obiektu window, a kontekstu kanwy, więc muszą być używane wg. schematu: kontekst.metoda() lub kontekst.wlasciwosc.

//linie
c.beginPath();
c.moveTo(20,20);
c.lineTo(130,130);
c.moveTo(130,20);
c.lineTo(20,130);
c.stroke();

//trójkąt
c.beginPath();
c.moveTo(75,20);
c.lineTo(130,130);
c.lineTo(20,130);
c.closePath();
c.stroke();

beginPath()

Rozpoczęcie ścieżki.

closePath()

Zamknięcie ścieżki.

moveTo(x, y)

Przenosi nas na wskazany punkt na kanwie. Warto w tym momencie zaznaczyć, że punkt o współrzędnych (0,0) na kanwie znajduje się w lewym górnym rogu.

  • x – współrzędna pozioma na kanwie
  • y – współrzędna pionowa na kanwie

lineTo(x, y)

Tworzy linie z aktualnego punktu do punktu wskazanego przez parametry.

  • x – współrzędna pozioma na kanwie
  • y – współrzędna pionowa na kanwie

stroke()

Rysuje aktualną ścieżkę.

c.rect(20,20,110,110);
c.stroke();

rect(x, y, szerokosc, wyskokosc)

Rysuje prostokąt.

  • x – współrzędna pozioma na kanwie lewego, górnego wierzchołka prostokąta
  • y – współrzędna pionowa na kanwie lewego, górnego wierzchołka prostokąta
  • szerokosc – długość prostokąta
  • wysokosc – długość prostokąta

function degToRad(deg){
  return deg*Math.PI/180;
}
//okrąg
c.beginPath();
c.arc(75,75,55,0,degToRad(360));
c.stroke();

//półokrąg
c.beginPath();
c.arc(75,75,55,degToRad(90),degToRad(270));
c.closePath();
c.stroke();

//łuk
c.beginPath();
c.moveTo(20,20);
c.lineTo(40,20);
c.arcTo(130,20,130,110,90);
c.lineTo(130,130);
c.stroke();

arc(x, y, r, start, stop, [odwrotniedowskazowek])

Rysuje łuk.

  • x – środek okręgu na którym zbudowana łuk
  • y – środek okręgu na którym zbudowana łuk
  • start – miejsce na okręgu podawane w radianach, od którego zaczyna się łuk (za zero przyjmuje się punkt wysunięty maksymalnie na prawo)
  • stop – miejsce na okręgu podawane w radianach, na którym kończy się łuk
  • odwrotniedowskazowek – opcjonalny parametr boolowski określający rysowanie odwrotnie z ruchem wskazówek zegara, w praktyce ustawiony na prawdę „odwraca” łuk.

c.beginPath();
c.moveTo(20,20);
c.lineTo(40,20);
c.quadraticCurveTo(130,20,130,110);
c.lineTo(130,130);
c.stroke();

quadraticCurveTo(xpktk, ypktk, x, y)

Rysuje krzywą Beziera drugiego stopnia (kwadratową).

  • xpktk – współrzędna pozioma punktu kontrolnego
  • ypktk – współrzędna pionowa punktu kontrolnego
  • x – współrzędna pozioma zakończenia krzywej
  • y – współrzędna pionowa zakończenia krzywej

c.beginPath();
c.moveTo(20,20);
c.lineTo(40,20);
c.bezierCurveTo(130,20,20,130,130,130);
c.lineTo(130,130);
c.stroke();

bezierCurveTo(xpktk1, ypktk1, xpktk2, ypktk2, x, y)

Rysuje krzywą Beziera trzeciego stopnia (sześcienną).

  • xpktk1 – współrzędna pozioma 1. punktu kontrolnego
  • ypktk1 – współrzędna pionowa 1. punktu kontrolnego
  • xpktk2 – współrzędna pozioma 2. punktu kontrolnego
  • ypktk2 – współrzędna pionowa 2. punktu kontrolnego
  • x – współrzędna pozioma zakończenia krzywej
  • y – współrzędna pionowa zakończenia krzywej

Przyznacie jednak, że kształty, które rysowaliśmy do tej pory wyglądają dość ascetycznie. Teraz nadamy im trochę blasku.

c.beginPath();
c.moveTo(20,20);
c.lineTo(130,130);
c.moveTo(130,20);
c.lineTo(20,130);
c.strokeStyle = "#3F8FFF";
c.lineWidth = 10;
c.lineCap = "round";
c.stroke();

Nie jest to morze dzieło sztuki, no ale jak to mówią: „Lepszy wróbel w garści…”

strokeStyle

Pozwala nadać kolor obrysowi

lineWidth

Pozwala nadać obrysowi grubość wyrażoną w pikselach. Warto zaznaczyć, że na kanwach kontur jest wyśrodkowany. Oznacza to tyle, że w przypadku konturu o grubości 10px, 5px będzie „na zewnątrz” i 5px „wewnątrz” figury.

lineCap

Pozwala nadać styl zakończenia linii. Może przyjąć jedną z trzech wartości: butt (domyślna), round, squared.

c.rect(20,20,110,110);
c.fillStyle = "#FFFB3F";
c.strokeStyle = "#3F8FFF";
c.lineWidth = 10;
c.lineJoin = "round";
c.fill();
c.stroke();

fillStyle

Pozwala nadać styl wypełnienia. Może to być jednolity kolor, gradient lub wzór (createPattern()).

lineJoin

Pozwala określić sposób w jaki łączą się linie. Może przyjąć jedną z trzech wartości: miter (domyślna), round, bevel.

fill()

Wypełnia aktualną ścieżkę.

var gradient = c.createLinearGradient(20,20,130,130);
gradient.addColorStop(0, "#3F8FFF");
gradient.addColorStop(0.3, "#3F8FFF");
gradient.addColorStop(0.7, "#FFFB3F");
gradient.addColorStop(1, "#FFFB3F");
c.beginPath();
c.moveTo(75,20);
c.lineTo(130,130);
c.lineTo(20,130);
c.closePath();
c.fillStyle = gradient;
c.fill();

createLinearGradient(x, y, xk, yk)

Tworzy obiekt gradientu liniowego. Istnieje też gradient kołowy.

  • x – współrzędna pozioma początkowego punktu
  • y – współrzędna pionowa początkowego punktu
  • xk – współrzędna pozioma końcowego punktu
  • yk – współrzędna pionowa końcowego punktu

addColorStop(n, color)

Dodaje kolor do gradientu.

  • n – miejsce dodania nowego koloru, podawane w procentach, gdzie 1 = 100%, a 0 = 0%
  • color – nowo dodawany kolor

c.save();
  c.beginPath();
  c.shadowColor = "#3F8FFF";
  c.shadowBlur = 30;
  c.shadowOffsetX = 10;
  c.shadowOffsetY = -10;
  c.fillStyle = "#FFFB3F";
  c.arc(75,75,55,0,degToRad(360));
  c.fill();
c.closePath();
c.restore();
c.beginPath();
  c.fillStyle = "#3F8FFF";
  c.arc(75,75,20,0,degToRad(360));
  c.fill();
c.closePath();

shadowColor

Pozwala ustawić kolor cienia.

shadowBlur

Pozwala ustawić rozmazanie cienia.

shadowOffsetX

Współrzędna x przesunięcia cienia.

shadowOffsetY

Współrzędna y przesunięcia cienia.

save()

Zapisuje aktualny stan kontekstu.

restore()

Przywraca ostatnio zapisany stan kontekstu. Zasado kolejkowania stanów kontekstu działa na zasadzie stosu, buforu LIFO. Oznacza to tyle, że pierwszy zapisany stan kontekstu będzie ostatnim stanem, który będzie można odzyskać. Łatwo to sobie wyobrazić na przykładzie talerzy ułożonych jeden na drugim. Pierwszy talerz w stosie jest na samym spodzie więc aby do wyjąć musimy zdjąć wszystkie talerze, które znajdują się nad nim, a talerz ułożony jako ostatni zdejmiemy najszybciej.

var img = document.createElement("img");
img.setAttribute('src', 'http://sciezka/do/zdjecia.jpg');
c.drawImage(img,10,10);

drawImage(img, x, y)

Rysuję obraz na kanwie.

  • img – obiekt obrazu
  • x – współrzędna pozioma lewego górnego rogu obrazka
  • y – współrzędna pionowa lewego dolnego rogu obrazka

c.beginPath();
  c.font = "italic 28px Arial";
  c.textBaseline = "middle";
  c.textAlign = "center";
  c.fillText("Leonardo da Vinci, La Gioconda", 231,75);
c.closePath();

font

Właściwość pozwalająca określić czcionkę, jej styl i wielkość. Jej składnia jest identyczna, jak ta którą znamy z CSS.

textBaseline

Pozwala na ustawienie linii pisma (ang. baseline). Przyjmuje jedną z sześciu wartości: alphabetic (domyślna), top, hanging, middle, ideographic i bottom.

textAlign

Pozwala na ustawienie wyrównania tekstu. Przyjmuje jedną z pięciu wartości: start (domyślna), end, center, left, right.

fillText(tekst, x, y, [maxdl])

Rysuje tekst na kanwie. Istnieje też metoda strokeText(), która przyjmuje te same parametry jednak zamiast wypełniać tekst kolorem zaznacza jego kontur.

  • tekst – Ciąg znaków, który ma pojawić się na kanwie
  • x – współrzędna pozioma
  • y – współrzędna pionowa
  • maxdl – maksymalna długość tekstu, ten parametr jest opcjonalny

Przydatne linki

komentarze 2

  • Awatar
    Comandeer

    18 grudnia 2013 15:55

    > jest relatywnie nowym elementem.
    słowo-klucz: relatywnie ;) jak mnie pamięć nie myli, jego historia sięga roku 2007, więc nie tak znowu blisko (jak na Internet oczywiście)

    >Jak widać na poniższym kodzie, stworzenie rozwiązania zastępczego dla przeglądarek nie wspierających canvas (IE 8.0 i starsze) jest dziecinnie proste i spokojnie obejdziemy się bez Modernizr.

    Modernizr nigdy nie służył do stworzenia rozwiązania zastępczego – on tylko służy do wykrywania feature’ów. od rozwiązań zastępczych są polyfille, takie jak FlashCanvas (jedna z niewielu rzeczy zbudowanych na Flashu, które polecam). tak, wiem, czepiam się ;)

    jeśli mówimy o canvas bez różnych upraszczaczy (polecam CanvasQuery – znawcy innego JS-owego produktu z Query w nazwie powinni się szybko odnaleźć ;) polecam tym bardziej, że to produkt polski), to warto wspomnieć o takim fajnym tricku jak przeskalowanie całego układu współrzędnych kanwy. wówczas zamiast 1567 od topu będziemy mieli 0.5. często na takich liczbach/proporcjach lepiej się operuje (przynajmniej mnie).

    Odpowiedz
    • Awatar
      Michał Załęcki

      18 grudnia 2013 18:46

      O rozwiązaniach zastępczych i Modernizr było w kontekście prostego wykrycia braku tej funkcjonalności. Wziąłem to za pewniak, abstrahując to nawet nie bardzo mnie te narzędzie do siebie przekonuje ze względu na parę bugów, które i tak finalnie trzeba samemu poprawiać i/lub szukać rozwiązań w sieci.

      Ja osobiście zacząłem używać KineticJS.

      Odpowiedz

Zostaw odpowiedź