Rysowanie wykresów funkcji w html5 canvas
W tym artykule napiszemy program rysujący wykresy funkcji 2d. Zanim rozpoczniesz czytanie tego tutoriala warto zapoznać się z artykułem podstawy html5 canvas. Zaczniemy od stworzenia bazowego dokumentu html, na którym będziemy opierać skrypty. Najważniejszym elementem jest tu canvas o id obrazek. To na nim będziemy rysować.
<!doctype html> <html><head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Rysowanie wykresów funkcji 2d</title> </head> <body> <canvas style="border:1px solid black;" id="obrazek" width="800" height="600"></canvas> <input type="text" name="function" id="function" value="" /> <input type="submit" name="button" id="button" value="Rysuj" /> <button id="clear">czyść</button><br /> Kolor linii: <select id="color"> <option value="green">Zielony</option> <option value="red">Czerwony</option> <option value="blue">Niebieski</option> </select><br/> <script type="text/javascript" src="jquery-1.4.2.js"></script> <script type="text/javascript" src="main.js"></script> </body> </html>Teraz stwórzmy plik main.js, w którym będzie się znajdował skrypt rysujący. Na początku określmy w nim kilka zmiennych. Poz zmienną canvas zapisujemy nasz element canvas, następnie łapiemy context. Kolejne zmienne to zmienne pomocnicze takie jak szerokość i wysokość canvasa, zakres (range) - oznacza ile pikseli na ekranie będzie miała rzeczywista jedna jednostka.
var canvas = document.getElementById('obrazek');
var ctx = canvas.getContext('2d');
var w = canvas.width;
var h = canvas.height;
var range = 100;
var bl = 1;
var rangex = w/range;
var rangey = h/range;
var color = "green";
Najpierw stworzymy funkcję grid, rysującą siatkę czyli układ współrzędnych.
function grid()
{
ctx.strokeStyle = "black";
for(i=1;i<=Math.floor(rangex*2);i++)
{
ctx.lineWidth = 1/6;
ctx.beginPath();
ctx.moveTo((range/2)*i,0);
ctx.lineTo((range/2)*i,h);
ctx.closePath();
ctx.stroke();
}
for(i=1;i<=Math.floor(rangey*2);i++)
{
ctx.lineWidth = 1/6;
ctx.beginPath();
ctx.moveTo(0,(range/2)*i);
ctx.lineTo(w,(range/2)*i);
ctx.closePath();
ctx.stroke();
}
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(0,h/2);
ctx.lineTo(w,h/2);
ctx.closePath();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(w/2,0);
ctx.lineTo(w/2,h);
ctx.closePath();
ctx.stroke();
for(i=-(rangex/2);i<=(rangex/2);i++)
{
ctx.fillText(i,(i+(rangex/2))*range,h/2);
}
ctx.save();
}
grid();
Ustawiamy kolor linii na czarny. Kolejne dwie pętle for odpowiedzialne są za narysowanie siatki. Pierwszy for rysuje tylko pionowe linie, drugi poziome.
Omówię tylko pierwszy for. Wykonujemy pętlę od 1 do Math.floor(rangex*2). Ustawiamy szerokość linii i rozpoczynamy ścieżkę. Na początek określamy punkt
początkowy rysowania na ((range/2)*i,0) i prowadzimy od niego linię do (range/2)*i,h). W celu zakończenia rysowania danej linii musimy jeszcze
dopisać
ctx.closePath(); ctx.stroke();Najlepiej przedstawi to rysunek:
Po pętlach rysujemy dwie linie (pionową i poziomą) o grubości 1 biegnące przez środek canvasa. Będą to osie układu współrzędnych. Ostatnia pętla dodaje
podpisy jednostek do osi x. Na koniec zapisujemy stan canvas metodą save. W kolejnej funkcji, którą napiszemy określimy ustawienia startowe.
function start_settings()
{
ctx.translate(w/2, h/2);
ctx.scale(range,range);
}
start_settings();
Funkcją translate przesuwamy punkt (0,0) na środek naszego canvasa. Jest to bardziej intuicyjne z matematycznego punktu widzenia.
Funkcją scale powiększamy nasz rysunek stukrotnie. Robimy tak ponieważ chcemy odwzorować nie rzeczywiste jednostki canvasa lecz stworzoną przez nas skalę (100px = 1 jednostka).
Pozostała już tylko funkcja rysująca wykres. Przedstawia się następująco.
function plot()
{
ctx.strokeStyle = color;
ctx.lineWidth = 1/50;
var f = document.getElementById("function").value;
var fun1 = new Function("x", "return "+f);
ctx.beginPath();
for(x=-rangex;x<=rangex;x+=0.01)
{
y = fun1(x);
if(y!='')
{
if (isNaN(y) || (y == Number.NEGATIVE_INFINITY) ||
(y == Number.POSITIVE_INFINITY) || (Math.abs(y) > 2e5)) {
bl = 2;
y = 0.0;
}
if (bl > 0)
{
if (bl == 1)
{
ctx.moveTo(x, -y);
}
--bl;
}
else {
ctx.lineTo(x, -y);
}
}
}
ctx.stroke();
}
Ustawiamy kolor i grubośc linii. Zapisujemy w zmiennej f funkcję wpisaną do inputa o id function. Funkcje muszą być wpisywane w postaci: Math.sin(x) + x*x, czyli korzystając z obiektu Math i
dostępnych w javascripcie operatotów. Natomiast pod zmienną fun1 tą samą funkcję evalowaną jako
funkcja zmiennej x.
Rozpoczynamy rysowanie ścieżki (beginPath). Pętlą for przechodzimy przez x. Do zmiennej y zapisujemy wartość funkcji dla określonego x. Następnie wykonujemy ifa czy y jest
różne od pustego ciągu znaków i dopiero wtedy wykonujemy właściwe rysowanie. Sprawdzamy w kolejnym warunku czy obliczona wartość y jest liczbą, jest równa nieskończoności itd.
Jeśli tak nie jest ustawiamy zmienną bl na 2 i zmiennej y przypisujemy wartość 0. Następnie w zależności od wartości bl rysujemy linię za pomocą lineTo lub ustawiamy punkt
startowy rysowania (moveTo). W obu metodach mamy znak minusa przed y, ponieważ układ współrzędnych w przeglądarce jest odwrócony (wartości na osi y rosną do dołu) w stosunku do tego znanego ze szkoły.
Funkcję plot kończymy wywołaniem metody stroke. Musimy jeszcze przypisać wykonanie funkcji plot do zdarzenie kliknięcia na przycisku.
document.getElementById("button").onclick = plot;
Działanie skryptu rysującego wykresy możesz zobaczyć tutaj.
