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.