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: siatka z uwzględnieniem zakresów 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.