Canvas

Canvas czyli płótno pozwala nam dynamicznie rysować po swojej powierzchni.

Ściągawkę zawierającą zbiór metod elementu canvas możesz ściągnąć tutaj

Aby utworzyć canvas, skorzystamy z kodu:


<canvas width="200" height="200" id="can">
    ...treść alternatywna...
</canvas>

Atrybuty width i height są opcjonalne. Jeżeli ich nie podamy, wówczas element przyjmie standardowe dla siebie rozmiary 300 x 150. Treść alternatywna będzie wyświetlana, gdy przeglądarka nie obsługuje tego typu elementów.

Odwołanie się do canvas

Aby zacząć rysowanie po naszym polu, musimy pobrać jego zawartość. Służy do tego metoda getContext('2d'). Tryb 2d jest obecnie jedynym formatem, który obsługuje płótno, chociaż i to nie jest przeszkodą. W sieci można znaleźć bardzo dużo przykładów na obsługę grafiki 3d w canvasie.


var canvas = document.getElementById('canvas');
if (canvas.getContext) {
    var c = canvas.getContext('2d');
    //...rysujemy
}

Wersje do 8 włączenie zupełnie sobie nie radzą z elemenem canvas. Pobrać element pod IE możemy, ale wywołanie metody getContext('2d') zwróci błąd.
Aby temu zaradzić trzeba posiłkować się pluginem - np. explorecanvas,

Rysowanie prostokątów

Element canvas udostępnia nam metody:

  • fillRect(x,y,width,height) - rysuje wypełniony prostokąt
  • strokeRect(x,y,width,height) - rysuje obramowanie prostokąta
  • clearRext(x,y,width,height) - czyści określony obszar i czyni go przezroczystym

var canvas = document.getElementById('canvas1');
if (canvas.getContext){
    var c = canvas.getContext('2d');

    //rysujemy niebieski kwadrat
    c.fillRect(25,25,100,100);
    //wycinamy jego srodek
    c.clearRect(45,45,60,60);

    //rysujemy obramowanie drugiego kwadratu
    c.strokeRect(50,50,50,50);
}
Twoja przeglądarka nie obsługuje canvas

Tekst

Aby wypisać tekst, możemy skorzystać z dwóch metod:

  • fillText("tekst",x,y) - która wypisuje wypełniony tekst w pozycji x, y
  • strokeText("tekst",x,y) - która wypisuje obrysowany tekst w pozycji x, y

Dodatkowo mamy do dyspozycji kilka właściwości, które pozwalają nam zmieniać wygląd tekstu:

  • font = "wartość" - opis wyglądu czcionki taki sam jaki stosujemy w CSS
  • textAlign = "wartość" - wyrównanie tekstu w poziomie. Możliwe wartości to: start, end, left, right, center. Domyślną wartością jest start.
  • textBaseline = "wartość" - Określa pionowe wyrównanie tekstu względem linii. Możliwe wartości to: top, hanging, middle, alphabetic, ideographic, bottom. Domyślną jest alphabetic.

var canvas = document.getElementById('canvasTestText');
if (canvas.getContext){
    var c = canvas.getContext('2d');

    c.font = "italic bold 30px Arial";
    c.textBaseline = "middle";
    c.fillText('Super Fantomas', 0, 30);

    c.font = "italic bold 20px Arial";
    c.textBaseline = "bottom";
    c.strokeText('Super Fantomas', 30, 70);

    c.font = "normal 10px Arial";
    c.textBaseline = "top";
    c.strokeText('Super Fantomas', 130, 80);
}
Twoja przeglądarka nie obsługuje canvas

W praktyce nie jest już tak różowo. Starsze przeglądarki (IE<9, Firefox2/3.0, Opera 9+, Safari 3.x, Chrome 1.0) nie rozumieją powyższych metod. Nawet użycie dla IE wspomnianej wcześniej biblioteki explorecanvas nic tutaj nie pomoże :(.
A teraz pytanie? Co powinniśmy zrobić, aby wymienione starocia obsługiwały tekst w canvasie? Ha, ha. Oczywiście powinniśmy użyć komentarzy warunkowych i dołączyć KOLEJNĄ bibliotekę...

Smutne to, a zarazem prawdziwe.

Tym razem będzie to http://code.google.com/p/canvas-text/wiki/QuickStart.


    <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
    <script type="text/javascript" src="canvas.text.js"></script>

Jak czytamy w dokumentacji tej biblioteki, nazwa pliku z tą biblioteką musi mieć nazwę canvas.text.js. Po takim zabiegu nawet przeglądarkowe dziadki mogą pisać po canvasie.

Ścieżki

Do rysowania większości rzeczy po canvasie korzystać będziemy ze ścieżek. Ich działanie jest identyczne jak zasada działania piórka w programach wektorowych. Rysowanie takie odbywa się w kilu krokach:

  • rozpoczynamy rysowanie niewidzialnej ścieżki - beginPath()
  • rysujemy poszczególne części ścieżki
  • obrysowujemy stroke() lub wypełniamy fill() naszą ścieżkę

var canvas = document.getElementById('canvas2');
if (canvas.getContext){
    var c = canvas.getContext('2d');

    c.beginPath();
    //...rysujemy scieżke
    c.fill() //wypełniamy scieżke
}

Do przesuwania piórka bez wyznaczania ścieżki służy metoda moveTo(x,y), która przesuwa punkt rysowania do punku x, y.

Rysujemy linie w ścieżce

Do rysowania linii wykorzystujemy metodę lineTo(x,y), która rysuje ścieżkę z aktualnej pozycji do pozycji x, y.

Przykładowo narysujmy trójkąt:


c.beginPath();
c.moveTo(35,10); //rysowanie zaczynamy od punktów 35,10 - tam więc przesuwamy nasze piórko
c.lineTo(60,40);
c.lineTo(10,40);
c.lineTo(35,10);
c.stroke();

c.fillText('a',30,60);
c.fillText('c',110,60);
c.fillText('b',70,130);
Twoja przeglądarka nie obsługuje canvas

Podczas rysowania drugiego trójkąta skorzystaliśmy z metody closePath(), która łączy ostatni punkt ścieżki z pierwszym, tym samym zamykając całą ścieżkę. Przy stosowaniu wypełnienia fill() możemy ten krok pominąć, gdyż wypełniane kształty są automatycznie zamykane.


var canvas = document.getElementById('canvas_3');
if (canvas.getContext){
    var data = [30,30,40,100,30,20,50,10,5,7,3,15,20,60,28,15,10,20,10,70];
    var stepSize = parseInt(400 / data.length);
    var c = canvas.getContext('2d');

    c.beginPath();
    //górne ramie
    c.moveTo(0, 150-data[0]);
    c.fillText(data[0], 0, 150-data[0]-10);

    for (var x=1; x<data.length; x++) {
        c.lineTo(x*stepSize, 150-data[x]);
        c.fillText(data[x], x*stepSize, 150-data[x]-10);
    }
    //zamykamy kształt od dołu
    c.lineTo(x*stepSize, 150);
    c.lineTo(0, 150);
    c.closePath();
    //obrysowujemy
    c.fill();
}
Twoja przeglądarka nie obsługuje canvas

Rysowanie łuków w ścieżkach

Kolejnym kształtem który możemy dołączyć do naszej ścieżki jest łuk:

arc(x, y, r, start, koniec, kierunek rysowania [false lub true])

Atrybuty x i y określają miejsce postawienia igły cyrkla. Start i koniec określają graniczne kąty, w których będzie zawierał się nasz łuk. W przypadku canvas są one podawane w radianach, dlatego musimy je skonwertować do stopni za pomocą wzoru:

radians = (Math.PI/180)*kat

Ostatni parametr określa, czy łuk ma być rysowany zgonie z ruchem wskazówek czy nie.


function radianAngle(angle) {
    return radians = (Math.PI/180)*angle;
}

var canvas = document.getElementById('canvas_4');
if (canvas.getContext){
    var c = canvas.getContext('2d');
    for (var x=0; x<4; x++) {
        c.beginPath();
        c.arc(75,75,40+(x*10), radianAngle(10), radianAngle(300),false);
        c.stroke();
    }
}
Twoja przeglądarka nie obsługuje canvas

Rysowanie krzywych w ścieżkach

Aby narysować krzywe skorzystamy z funkcji:

  • quadraticCurveTo(pk1x, pk1y, x, y) - rysuje kwadratową ścieżkę do punktu x, y. Atrybuty pk1x i pk1y określają położenie punktu kontrolnego wyginającego ścieżkę
  • bezierCurveTo(pk1x, pk1y, pk2x, pk2y, x, y) - rysuje ścieżkę beziera do punktu x, y. Atrybuty pk1x, pk1y, pk2x, pk2y określają położenie punktów kontrolnych.

krzywe

Tak samo jak poprzednio przyda się doświadczenie przy pracy z krzywymi w programach graficznych (piórkiem w Adobe). Jako, że pierwsza z nich nie jest obsługiwana przez Firefox 1.5, polecam skupienie się na drugiej.


var canvas = document.getElementById('canvas5');
if (canvas.getContext){
    var c = canvas.getContext('2d');                
    c.beginPath();

    //rysujemy oczy
    c.moveTo(60,20);
    c.lineTo(60,50);
    c.moveTo(90,20);
    c.lineTo(90,50);

    //brwi
    c.moveTo(50,20);
    c.lineTo(70,5);
    c.moveTo(80,5);
    c.lineTo(100,20);

    //rysujemy uśmiech
    c.moveTo(10,80);
    c.bezierCurveTo(30,130,120,130,140,80);
    c.moveTo(10,80);
    c.bezierCurveTo(30,150,120,150,140,80);
    c.stroke();
}
Twoja przeglądarka nie obsługuje canvas

Rysiek jest zadowolony.

Wykorzystując krzywe, możemy w łatwy sposób napisać funkcję, która będzie tworzyła prostokąty z zaokrąglonymi rogami


function roundedRect(canvas, x ,y, width, height, radius){
    canvas.beginPath();
    canvas.moveTo(x,y+radius);
    canvas.lineTo(x,y+height-radius);
    canvas.quadraticCurveTo(x,y+height,x+radius,y+height);
    canvas.lineTo(x+width-radius,y+height);
    canvas.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
    canvas.lineTo(x+width,y+radius);
    canvas.quadraticCurveTo(x+width,y,x+width-radius,y);
    canvas.lineTo(x+radius,y);
    canvas.quadraticCurveTo(x,y,x,y+radius);
    canvas.stroke();
}

var canvas = document.getElementById('canvas_rounded');
if (canvas.getContext){
    var c = canvas.getContext('2d');
    roundedRect(c, 10, 10, 130, 130, 20);
}
Twoja przeglądarka nie obsługuje canvas

Rysowanie prostokątów w ścieżkach

Już rysowaliśmy prostokąty bezpośrednio na canvasie. To samo możemy zrobić dla ścieżki. Wystarczy skorzystać z metody rect(x, y, width, height). Kiedy ta metoda jest wykonywana, to metoda moveTo jest wywołana automatycznie z parametrami (0,0) (np. przechowuje punkty startowe do jego domyślnej lokalizacji).

Łącząc wszystkie poznane techniki możemy rysować już całkiem ciekawe rzeczy. Piękne dynamiczne wykresy to jedna z możliwości, lub...


var canvas = document.getElementById('canvas_6')
if (canvas.getContext) {
    var c = canvas.getContext('2d');
    //jajo tułów
        c.beginPath();
        c.moveTo(100,10);
        c.bezierCurveTo(-10,10,-40,290,100,290);
        c.moveTo(100,10);
        c.bezierCurveTo(210,10,240,290,100,290);
        c.stroke();
        c.moveTo(10,120);
        c.bezierCurveTo(20,140,100,160,190,120);
        c.moveTo(14,220);
        c.bezierCurveTo(20,240,100,260,186,220);
    //nogi
        c.moveTo(45,270);
        c.lineTo(40,300);
        c.moveTo(155,270);
        c.lineTo(160,300);
    //klamra
        c.stroke();
        roundedRect(c, 85, 225, 30, 30, 5);
        roundedRect(c, 90, 230, 20, 20, 3);
    //lewe oko
        c.beginPath();
        c.moveTo(100,110);
        c.lineTo(10,80);
        c.moveTo(100,120);
        c.bezierCurveTo(100,190,10,110,10,80);
        c.moveTo(100,115);
        c.lineTo(10,85);
        c.moveTo(100,115);
        c.bezierCurveTo(100,180,10,100,15,85);            
        c.moveTo(90,130);
        c.lineTo(80,125);        
    //prawe oko
        c.moveTo(100,110);
        c.lineTo(190,80);
        c.moveTo(100,120);
        c.bezierCurveTo(100,180,190,110,190,80);
        c.moveTo(100,115);
        c.lineTo(190,85);
        c.moveTo(100,115);
        c.bezierCurveTo(100,170,190,100,185,85);            
        c.moveTo(110,130);
        c.lineTo(120,125);        
    //uśmiech
        c.moveTo(130,150);
        c.bezierCurveTo(130,160,140,160,140,150);        
    //ręce
        c.moveTo(5,150);
        c.lineTo(0,180);
        c.moveTo(195,150);
        c.lineTo(200,180);        
    //F        
        c.moveTo(90,40);
        c.lineTo(120,40);
        c.lineTo(120,50);
        c.lineTo(100,50);
        c.lineTo(100,60);
        c.lineTo(110,60);
        c.lineTo(110,70);
        c.lineTo(100,70);
        c.lineTo(100,80);
        c.lineTo(90,80);
        c.closePath();
        c.stroke();
}
    
Twoja przeglądarka nie obsługuje canvas

Kolory, przezroczystość i wygląd linii

Powyższe wiadomości mogły by nam w zasadzie wystarczyć. Istnieje jednak mały powód, dla którego nie wystarczą - nuda. Aby zwiększyć atrakcyjność naszych dzieł, dodamy im trochę koloru i stylu.

Do zmiany koloru obrysu lub wypełnienia służą własności strokeStyle i fillStyle. Aby zmienić kolor, przypisujemy im jego wartość podaną wartością CSS, składową RGB lub nazwą.


c.fillStyle = "#6DCF00";
c.fillStyle = "rgb(109,207,0)";
c.strokeStyle = "green";

var canvas = document.getElementById('canvasColor');
if (canvas.getContext){
    var c = canvas.getContext('2d');
    c.strokeStyle = "green";
    c.beginPath();
    c.moveTo(60,10); //rysowanie zaczynamy od górnego kąta
    c.lineTo(120,120);
    c.lineTo(10,120);
    c.lineTo(60,10);
    c.stroke();

    c.fillStyle = "rgb(201,224,54)";
    c.beginPath();            
    c.moveTo(90,20);
    c.lineTo(150,130);
    c.lineTo(10,130);
    c.closePath();
    c.fill();
}
Twoja przeglądarka nie obsługuje canvas

Aby ustawić przezroczystość rysowania, możemy wykorzystać dwa sposoby. Pierwszy z nich to użycie właściwości globalAlpha. Wartość tej własności zostanie zastosowana dla wszystkich następnie rysowanych kształtów na canvasie. Drugi sposób - o wiele przyjemniejszy - to podawanie kolorów w notacji CSS3:


c.strokeStyle = "rgba(255,0,0,0.5)"; //ostatni atrybut określa przezroczystość z przedziału 0.0 - 0.1
c.fillStyle = "rgba(255,0,0,0.3)";

Wartość przezroczystości zawiera się w przedziale 0.0 - 1.0.


var canvas = document.getElementById('canvas_kolor');
if (canvas.getContext){
    var c = canvas.getContext('2d');
    c.strokeStyle = "green";
    c.beginPath();
    c.moveTo(60,10); //rysowanie zaczynamy od górnego kąta
    c.lineTo(120,120);
    c.lineTo(10,120);
    c.lineTo(60,10);
    c.stroke();

    c.fillStyle = "rgba(201,224,54,0.4)";
    c.beginPath();            
    c.moveTo(90,20);
    c.lineTo(150,130);
    c.lineTo(10,130);
    c.closePath();
    c.fill();
}
Twoja przeglądarka nie obsługuje canvas

Tak samo jak w programach graficznych do wypełniania lub obrysowywania kształtów możemy używać gradientów. Zasada tworzenia gradientu jest podobna do tej z programów. Ustalamy położenie skrajnych punktów gradientu, a następnie określamy kolory gradientu.

Do tworzenia obiektów gradientów korzystamy z metod:

  • createLinearGradient(x1,y1,x2,y2) - tworzy obiekt gradientu, który możemy wykorzystać do obrysowani albo wypełnienia rysowanych figur. Gradient liniowy biegnie z punktu x1,y1 do punktu x2,y2
  • createRadialGradient(x1,y1,r1,x2,y2,r2) - ustawia gradient radialny. Atrybuty x1,y1,r1 - określają położenie i promień 1 okręgu, natomiast x2,y2,r2 - drugiego
  • addColorStop(pozycja, kolor) - dodaje nowy kolor do gradientu w pozycji z przedziału 0.0 - 1.0. Możesz dodawać takich punktów do woli.

var canvas = document.getElementById('canvasGradientTriangle');
if (canvas.getContext){
    var c = canvas.getContext('2d');
    var gradient = c.createLinearGradient(0,0,150,150);
        gradient.addColorStop(0,"#F5F8D6");
        gradient.addColorStop(0.5,"#CBDE22");                
        gradient.addColorStop(1,"#51590E");
        c.fillStyle = gradient;
        c.beginPath();
        c.moveTo(60,10); //rysowanie zaczynamy od górnego kąta
        c.lineTo(120,120);
        c.lineTo(10,120);
        c.lineTo(60,10);
    c.fill();
}
Twoja przeglądarka nie obsługuje canvas

var canvas = document.getElementById('canvas_gradient');
if (canvas.getContext) {
    var canvas1 = document.getElementById('canvas_gradient1');
    if (canvas1.getContext){
        var c1 = canvas1.getContext('2d');
        var grad = c1.createLinearGradient(0,0,100,50);
        grad.addColorStop(0, '#00ABEB');
        grad.addColorStop(0.5, '#fff');
        grad.addColorStop(1, '#D14F2E');
        c1.fillStyle = grad;
        c1.fillRect(10,10,180,80);
    }

    var canvas2 = document.getElementById('canvas_gradient2');
    if (canvas2.getContext){
        var c2 = canvas2.getContext('2d');
        var grad = c2.createRadialGradient(50,50,100,150,50,100);
        grad.addColorStop(0, '#00ABEB');
        grad.addColorStop(0.5, '#fff');
        grad.addColorStop(1, '#B3DA4C');
        c2.fillStyle = grad;
        c2.fillRect(10,10,180,80);
    }
}
Twoja przeglądarka nie obsługuje canvas Twoja przeglądarka nie obsługuje canvas

Wygląd linii obrysowania ustawiamy wykorzystując metody:

  • lineWidth = wartość - określa grubość linii

    Twoja przeglądarka nie obsługuje canvas
  • lineCap = typ - określa wygląd zakończenia rysowanej linii. Może przyjąć wartość butt, round lub square.

    Twoja przeglądarka nie obsługuje canvas
  • lineJoin = typ - określa sposób łączenia 2 linii. Może przyjąć wartość round, bevel lub miter.

    Twoja przeglądarka nie obsługuje canvas
  • miterLimit = wartość - określa jak daleko kąt połączenia 2 linii metodą miter może wychodzić.

    Twoja przeglądarka nie obsługuje canvas

Zapisywanie i odczytywanie stylu

Na zakończenie poznamy jeszcze 2 bardzo przydatne metody. Metoda save() służy do zapisania aktualnie ustawionych właściwości rysowania (które poznaliśmy powyżej).
Metoda restore() jak sama nazwa wskazuje służy do odczytania zapisanych wcześniej właściwości.
Ale uwaga! Metoda save i restore działają na zasadzie stosu. Save kładzie coś na stos, a restore pobiera OSTATNI odłożony zapisany stan.

Jak to wygląda w praktyce? Przykładowo rysujemy sobie jakiś obrazek. Ustawiamy sobie kilka właściwości rysowania - np grubość linii czy kolor. Zapisujemy je metodą save(). Dalej sobie rysujemy przy okazji kolejny raz zmieniając właściwości rysowania. Znowu zapisujemy. Jeżeli teraz wykonamy restore(), to pobierzemy ostatnie zapisane właściwości rysowania. Jako że nie mam talentu do tłumaczenia, posłużę się przykładem:


var ctx = document.getElementById('canvas').getContext('2d');

ctx.fillRect(0,0,150,150);
ctx.save(); //Zapisuje domyślny stan

ctx.fillStyle = '#09F'; //Robimy zmiany w ustawieniach
ctx.fillRect(15,15,120,120);

ctx.save(); //Zapisuje aktualny stan
ctx.fillStyle = '#FFF'
ctx.globalAlpha = 0.5;
ctx.fillRect(30,30,90,90);

ctx.restore(); //Przywraca poprzedni stan (zapisany w linijce 9)
ctx.fillRect(45,45,60,60);

ctx.restore(); //Przywraca domyślny stan (zapisany w linijce 4)
ctx.fillRect(60,60,30,30);