Przewodnik po canvas:Zastosowanie stylów i kolorów
z Mozilla Developer Center, polskiego centrum programistów Mozilli.
Spis treści |
W rozdziale o rysowaniu kształtów stosowaliśmy tylko linie i kontury z domyślnym wypełnieniem. W tym rozdziale poznamy wszystkie możliwości canvas, jakie mamy do dyspozycji, aby uatrakcyjnić nasze rysunki.
[edytuj] Kolory
Dotąd poznaliśmy tylko metody służące do rysowania. Jeżeli będziemy potrzebowali zastosować kolory do kształtu, możemy zastosować dwie ważne własności: fillStyle i strokeStyle.
fillStyle = kolor
strokeStyle = kolor
Własność strokeStyle jest stosowana do określenia koloru linii konturu, druga fillStyle jest stosowana do określenia koloru wypełnienia. Wartość color może być łańcuchem znaków wyznaczającym wartość koloru CSS, gradientu lub wzoru. Gradienty i wzory poznamy później. Domyślnie dla konturu i wypełnienia jest ustawiony kolor czarny (wartość koloru CSS #000000).
Prawidłowe łańcuchy jakie możemy przypisać powinny być zgodne ze specyfikacją: Własności koloru CSS3. Wszystkie poniższe przykłady określają ten sam kolor.
// Te wszystkie ustawienia określają kolor pomarańczowy ctx.fillStyle = "orange"; ctx.fillStyle = "#FFA500"; ctx.fillStyle = "rgb(255,165,0)"; ctx.fillStyle = "rgba(255,165,0,1)";
Uwaga: Aktualnie nie wszystkie wartości koloru w CSS 3 są obsługiwane przez silnik Gecko. Przykładowe wartości kolorów hsl(100%,25%,0) lub rgb(0,100%,0) nie są dozwolone. Wstawienie wartości podobnych do podanych obok przykładów (procentowe wartości), spowoduje wiele problemów z uruchomieniem skryptu.
Uwaga: Jeżeli zmienisz własność strokeStyle lub fillStyle, to nowa wartość staje się domyślną dla wszystkich kolejnych rysowanych kształtów. Jeżeli będziesz potrzebował dla każdego kształtu zastosować odmienne kolory, będziesz musiał ponownie wyznaczyć własność fillStyle lub strokeStyle.
[edytuj] Przykład fillStyle
W tym przykładzie, ponownie użyjemy dwóch pętli for do sporządzenia siatki złożonej z prostokątów, każdego w odmiennym kolorze. Powstały obraz powinien być podobny do obrazka po prawej stronie. Nie dzieje się tu nic szczególnego. Użyliśmy dwóch zmiennych i oraz j do wygenerowania niepowtarzalnego koloru RGB dla każdego kwadratu z osobna. Modyfikując tylko wartość koloru czerwonego i zielonego. Kanał koloru niebieskiego ma stałą wartość. Zmieniając kanały, możemy stworzyć różne rodzaje palety kolorów. Zwiększając stopniowo kroki pętli, możemy otrzymać paletę kolorów podobną do tej używanej przez program Photoshop.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i=0;i<6;i++){
for (j=0;j<6;j++){
ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' +
Math.floor(255-42.5*j) + ',0)';
ctx.fillRect(j*25,i*25,25,25);
}
}
}
[edytuj] Przykład strokeStyle
Ten przykład jest podobny do powyższego, tylko używa własności strokeStyle. Tutaj użyliśmy metody arc do narysowania okręgów zamiast kwadratów.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i=0;i<6;i++){
for (j=0;j<6;j++){
ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
Math.floor(255-42.5*j) + ')';
ctx.beginPath();
ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
ctx.stroke();
}
}
}
[edytuj] Przezroczystość
Oprócz rysowania nieprzezroczystych kształtów, za pomocą canvas możemy także rysować półprzezroczyste kształty. Można to zrobić za pomocą własności globalAlpha, albo możemy wyznaczyć półprzezroczysty kolor dla stylu konturu lub wypełnienia.
globalAlpha = wartość przezroczystości
Wartość tej własności zostanie zastosowana dla wszystkich kształtów narysowanych za pomocą canvas. Prawidłowy zakres wartości jest od 0.0 (całkowita przezroczystość) do 1.0 (brak przezroczystości). Domyślnym ustawieniem tej własności jest 1.0 (czyli brak przezroczystości).
Własność globalAlpha może być użyteczna, jeżeli będziemy potrzebowali narysować wiele kształtów odwołujących się do canvas z podobną przezroczystością. Jak sądzę, następna opcja jest jednakże trochę bardziej praktyczna.
Ponieważ własność strokeStyle i fillStyle akceptuje wartości kolorów CSS 3, możemy zastosować następującą notację do przypisania koloru stopnia przezroczystości.
// Przypisanie przezroczystości kolorów do stylu konturu i wypełnienia ctx.strokeStyle = "rgba(255,0,0,0.5)"; ctx.fillStyle = "rgba(255,0,0,0.5)";
Funkcja rgba() jest podobna do funkcji rgb() tylko posiada jeden dodatkowy parametr. Ostatnim parametrem jest szczególna wartość przezroczystości koloru. Prawidłowy zakres wartości zawiera się pomiędzy 0.0 (całkowita przezroczystość) i 1.0 (brak przezroczystości).
[edytuj] Przykład globalAlpha
W bieżącym przykładzie mamy narysowane tło złożone z czterech różnych kolorowych kwadratów. Na nim, mamy narysowane kilka kompletnych półprzezroczystych okręgów. Własność globalAlpha jest ustawiona na 0.2 i zostanie użyta dla wszystkich dalszych kształtów narysowanych po jej wyznaczeniu. Każdy krok pętli for rysuje kompletny okrąg ze zwiększonym promieniem. Końcowym rezultatem jest gradient promieniowy. Nakładając wciąż większą ilość okręgów na siebie, w rezultacie zmniejszymy przezroczystość okręgów, które mamy już narysowane. Zwiększając liczbę kroków rysujących okręgi, w efekcie całkowicie zasłoniliśmy rysunek tła w centrum obrazu.
- Ten przykład nie działa na Safari ponieważ kolor nie jest określony prawidłowo. Kolor w przykładzie jest określony jako '#09F)', jednak taka wartość koloru nie jest zgodna ze specyfikacją. Firefox jednakże przyjmuje taką formę definicji koloru.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// draw background
ctx.fillStyle = '#FD0';
ctx.fillRect(0,0,75,75);
ctx.fillStyle = '#6C0';
ctx.fillRect(75,0,75,75);
ctx.fillStyle = '#09F';
ctx.fillRect(0,75,75,75);
ctx.fillStyle = '#F30';
ctx.fillRect(75,75,150,150);
ctx.fillStyle = '#FFF';
// Ustawienie wartości przezroczystości
ctx.globalAlpha = 0.2;
// Rysowanie półprzezroczystych okręgów
for (i=0;i<7;i++){
ctx.beginPath();
ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
ctx.fill();
}
}
[edytuj] Przykład zastosowania rgba()
Drugi przykład zrobiliśmy trochę podobnie do tego powyżej, tylko zamiast rysowania na sobie okręgów, narysowaliśmy niewielkie prostokąty zwiększając nieprzezroczystość. Używanie rgba() zwiększa trochę elastyczność i kontrolę nad kolorem, ponieważ możemy go zastosować dla stylu wypełnienia i konturu indywidualnie.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// Rysowanie tła
ctx.fillStyle = 'rgb(255,221,0)';
ctx.fillRect(0,0,150,37.5);
ctx.fillStyle = 'rgb(102,204,0)';
ctx.fillRect(0,37.5,150,37.5);
ctx.fillStyle = 'rgb(0,153,255)';
ctx.fillRect(0,75,150,37.5);
ctx.fillStyle = 'rgb(255,51,0)';
ctx.fillRect(0,112.5,150,37.5);
// Rysowanie półprzezroczystych prostokątów
for (i=0;i<10;i++){
ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
for (j=0;j<4;j++){
ctx.fillRect(5+i*14,5+j*37.5,14,27.5)
}
}
}
[edytuj] Style linii
Mamy kilka własności, które pozwalają nam zmieniać styl linii.
lineWidth = wartość
lineCap = typ
lineJoin = typ
miterLimit = wartość
Można byłoby opisać je bardziej szczegółowo, lecz byłyby one mniej czytelne niż przykłady przedstawione poniżej.
[edytuj] Przykład lineWidth
Ta własność określa bieżącą grubość linii. Wartość musi być liczbą dodatnią. Domyślną jej wartością jest 1.0.
W poniższym przykładzie narysowaliśmy 10 prostych linii podnosząc szerokość kolejnych linii. Domyślnym ustawieniem jest, tak że jednostka 1 równa się 1 pikselowi. Szerokość linii z lewej strony wynosi 1 piksel (faktycznie wynosi 2 piksele), a szerokość linii z prawej wynosi 10 pikseli. Interesujące jest to, że linie z parzystymi szerokościami są narysowane prawidłowo lecz linie o nieparzystych szerokościach są grubsze o 1 piksel. Zobacz ten przykład
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i=0;i<10;i++){
ctx.lineWidth = 1+i;
ctx.beginPath();
ctx.moveTo(5+i*14,5);
ctx.lineTo(5+i*14,140);
ctx.stroke();
}
}
[edytuj] Przykład lineCap
Własność lineCap określa w jaki sposób ma być zakończona rysowana linia. Są trzy możliwe wartości dla tej własności, są nimi: butt, round i square. Domyślną wartością jest butt.
W tym przykładzie narysujemy trzy linie, każda odwołująca się do innej wartości własności lineCap. Dodaliśmy linie pomocnicze, aby zobaczyć szczegółowe różnice pomiędzy nimi. Każda z owych trzech linii zaczyna się i kończy dokładnie na liniach pomocniczych.
Linia z lewej używa domyślnej wartości butt. Jak zauważyłeś jest pociągnięta tak, że zupełnie nie wychodzi poza linie pomocnicze.
Druga używa wartości round. Dodaje półokrągłe zakończenie, którego promień jest równy połowie szerokości linii.
Linia z prawej strony używa wartości square. Dodaje prostokątne zakończenie o takiej samej szerokości i połowie wysokości grubości linii.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineCap = ['butt','round','square'];
// Rysowanie poziomych linii pomocniczych
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(140,10);
ctx.moveTo(10,140);
ctx.lineTo(140,140);
ctx.stroke();
// Rysowanie linii
ctx.strokeStyle = 'black';
for (i=0;i<lineCap.length;i++){
ctx.lineWidth = 15;
ctx.lineCap = lineCap[i];
ctx.beginPath();
ctx.moveTo(25+i*50,10);
ctx.lineTo(25+i*50,140);
ctx.stroke();
}
}
[edytuj] Przykład lineJoin
Własność lineJoin określa jak łączyć dwie linie w kształt będący złączony razem. Są trzy możliwe wartości tej własności: round, bevel i miter. Jako domyślna jest ustawiona wartość miter.
Znowu narysujmy trzy różne ścieżki, każda odwołująca się do innej wartości własności lineJoin. Górna ścieżka używa opcji round. To ustawienie zaokrągla rogi kształtu. Promień tego zaokrąglenia jest równy połowie szerokości linii. Druga linia używa opcji bevel (skos) i linia na dole używa opcji miter. Kiedy ustawimy na miter, linie są połączone przez przedłużenie zewnętrznych krawędzi, aż się połączą w jednym punkcie. To ustawienie jest dokonywane przez własność miterLimit, która jest wyjaśniona poniżej.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineJoin = ['round','bevel','miter'];
ctx.lineWidth = 10;
for (i=0;i<lineJoin.length;i++){
ctx.lineJoin = lineJoin[i];
ctx.beginPath();
ctx.moveTo(-5,5+i*40);
ctx.lineTo(35,45+i*40);
ctx.lineTo(75,5+i*40);
ctx.lineTo(115,45+i*40);
ctx.lineTo(155,5+i*40);
ctx.stroke();
}
}
[edytuj] Demo własności miterLimit
Jak widzieliśmy w poprzednim przykładzie, kiedy łączymy dwie linie wybierając miter, zewnętrzne krawędzie dwóch łączonych linii są przedłużone, aż do punktu ich połączenia. Dla linii które mają duże kąty, ten punkt jest niedaleko wewnętrznego punktu połączenia. Jednak, kiedy kąty pomiędzy każdą linią są mniejsze, to dystans (długość miter) pomiędzy tymi punktami wzrasta wykładniczo.
Własność miterLimit określa jak daleko zewnętrzny punkt połączenia może być umieszczony od wewnętrznego punktu połączenia. Jeżeli obie linie przewyższają tą wartość, zostaną narysowane z obcięciem ukośnych łączy.
Stwórzmy niewielkie demo, w którym dynamicznie można zmieniać własność miterLimit i zobaczyć wpływ tego działania na rysowany na płótnie (canvas) kształt. Niebieskie linie pokazują, gdzie znajdują się punkty początkowy i końcowy dla każdej z linii w zygzakowatym wzorze.
[edytuj] Gradienty
Zupełnie jak w dowolnym programie do rysowania, możemy używać dla wypełnienia i konturu kształtu gradientu liniowego i promieniowego. Tworząc obiekt canvasGradient używamy jednej z poniższych metod. Możemy użyć tego obiektu przydzielając go do własności fillStyle lub strokeStyle.
createLinearGradient(x1,y1,x2,y2)
createRadialGradient(x1,y1,r1,x2,y2,r2)
Metoda createLinearGradient pobiera cztery argumenty reprezentujące punkt startowy (x1,y1) i końcowy (x2,y2) gradientu.
Metoda createRadialGradient pobiera sześć argumentów. Pierwsze trzy argumenty określają współrzędne okręgu(x1,y1) o promieniu r1, kolejne trzy określają współrzędne drugiego okręgu (x2,y2) o promieniu r2.
var lineargradient = new ctx.createLinearGradient(0,0,150,150); var radialgradient = new ctx.createRadialGradient(75,75,0,75,75,100);
Skoro stworzyliśmy obiekt canvasGradient możemy przypisać do niego kolory używając metody addColorStop.
addColorStop(pozycja, kolor)
Ta metoda pobiera dwa argumenty. Pozycja position musi być liczbą pomiędzy 0.0 i 1.0 i określa względne położenie koloru wewnątrz gradientu. Przykładowo ustawienie jej na 0.5 umieszcza kolor dokładnie pośrodku gradientu. Argument color musi być łańcuchem reprezentującym wartość koloru CSS (ie #FFF, rgba(0,0,0,1), itd.).
Możesz dodać tyle przejść kolorów ile tylko będziesz potrzebował. Poniżej stworzyliśmy prosty biało-czarny gradient.
var lineargradient = new ctx.createLinearGradient(0,0,150,150); lineargradient.addColorStop(0,'white'); lineargradient.addColorStop(1,'black');
[edytuj] Przykład createLinearGradient
W tym przykładzie, utworzymy dwa różne gradienty. Pierwszy gradient utworzymy dla tła. Jak widać, przydzieliliśmy dwa kolory do tego samego położenia. Robiąc to stworzymy bardzo ostre przejście koloru - w tym przypadku z białego do zielonego. Zwykle, jest obojętne w jakim porządku określimy kolor przejścia, jednak w tym szczególnym przypadku ma to znaczenie. Jeżeli utrzymamy te wyznaczniki w porządku w jakim chcemy aby się ukazywały, to nie będzie stanowiło problemu
W drugim gradiencie, nie przyporządkowujemy początkowego koloru (dla położenia 0.0) skoro nie jest to ściśle wymagane. Przypisując czarny kolor do pozycji 0.5 automatycznie utworzymy gradient, od jego początku do końca, czarny.
Jak tutaj widzimy, zarówno własność strokeStyle jak i fillStyle mogą przyjmować obiekt canvasGradient jako prawidłową wartość.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// Utworzenie gradientów
var lingrad = ctx.createLinearGradient(0,0,0,150);
lingrad.addColorStop(0, '#00ABEB');
lingrad.addColorStop(0.5, '#fff');
lingrad.addColorStop(0.5, '#26C000');
lingrad.addColorStop(1, '#fff');
var lingrad2 = ctx.createLinearGradient(0,50,0,95);
lingrad2.addColorStop(0.5, '#000');
lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
// Przyporządkowanie gradientów do stylu wypełnienia i konturu
ctx.fillStyle = lingrad;
ctx.strokeStyle = lingrad2;
// Rysowanie kształtów
ctx.fillRect(10,10,130,130);
ctx.strokeRect(50,50,50,50);
}
[edytuj] Przykład createRadialGradient
W tym przykładzie utworzymy pięć rożnych gradientów promieniowych. Ponieważ mamy kontrolę nad punktem początkowym i końcowym gradientu, możemy osiągnąć więcej złożonych efektów, niż moglibyśmy normalnie uzyskać w klasycznym gradiencie promieniowym jaki widzimy na przykład w programie Photoshop (gradient o pojedynczym środkowym punkcie, gdzie; gradient rozszerza się na zewnątrz w kulisty kształt).
W tym przypadku, przesuwając lekko punkt początkowy od punktu końcowego osiągnęliśmy kulisty efekt 3D. Najlepiej jest unikać pozwolenia wewnętrznemu okręgowi całkowicie zasłonić lub wyjść poza zewnętrzny okrąg, ponieważ powoduje to dziwne efekty które są trudne do przewidzenia.
Ostatnie przejście koloru we wszystkich czterech gradientach używa całkowitej przezroczystości koloru. Jeśli będziecie potrzebowali mieć ładne przejście od poprzedniego przejścia koloru, to obydwie wartości powinny być równe. To nie jest zbyt oczywiste z kodu, ponieważ wykorzystano w nim dwie inne metody koloru CSS, z wyjątkiem pierwszego gradientu #019F62 = rgba(1,159,98,1)
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// Utworzenie gradientów
var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
radgrad.addColorStop(0, '#A7D30C');
radgrad.addColorStop(0.9, '#019F62');
radgrad.addColorStop(1, 'rgba(1,159,98,0)');
var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
radgrad2.addColorStop(0, '#FF5F98');
radgrad2.addColorStop(0.75, '#FF0188');
radgrad2.addColorStop(1, 'rgba(255,1,136,0)');
var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
radgrad3.addColorStop(0, '#00C9FF');
radgrad3.addColorStop(0.8, '#00B5E2');
radgrad3.addColorStop(1, 'rgba(0,201,255,0)');
var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);
radgrad4.addColorStop(0, '#F4F201');
radgrad4.addColorStop(0.8, '#E4C700');
radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
// Rysowanie kształtów
ctx.fillStyle = radgrad4;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad3;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad2;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad;
ctx.fillRect(0,0,150,150);
}
[edytuj] Wzorce
W jednym z przykładów na poprzedniej stronie, użyliśmy serii pętli do stworzenia wzorca (deseniu) z obrazów. Istnieje jednak dużo prostsza metoda: metoda createPattern.
createPattern(obraz,typ)
Ta metoda pobiera dwa argumenty. Obraz odnosi się zarówno do obiektu Image oraz innych elementów canvas. Typ musi być łańcuchem posiadającym jedną z następujacych wartości: repeat, repeat-x, repeat-y i no-repeat.
Image nie działa w Firefoksie 1.5 (Gecko 1.8)Użyjemy tej metody do stworzenia obiektu wzorca, który jest bardzo podobny do metod gradientu widzianych powyżej.
Skoro tylko utworzymy wzorzec, możemy go przydzielić do własności fillStyle lub strokeStyle.
var img = new Image(); img.src = 'someimage.png'; var ptrn = ctx.createPattern(img,'repeat')
Uwaga: W przeciwieństwie do metody drawImage, musimy być pewni, że obraz którego używamy jest załadowany przed wywołaniem tej metody, bo wtedy wzorzec może być narysowany niepoprawnie.
Uwaga: Firefox obecnie obsługuje tylko własność repeat. Jeżeli wyznaczymy jakąkolwiek inną, nie zobaczymy żadnych zmian.
[edytuj] Przykład createPattern
W ostatnim przykładzie, stworzymy wzorzec, któremu przypiszemy własność fillStyle. Jedyną rzeczą wartą zaznaczenia jest fakt użycia obiektu Image poprzez onload. To zapewnia załadowanie obrazu przed przypisaniem do wzorca.
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// Utworzenie nowego obiektu obrazu do użycia jako wzorca
var img = new Image();
img.src = 'images/wallpaper.png';
img.onload = function(){
// Utworzenie wzorca
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0,0,150,150);
}
}










