JavaScriptで数式を入力したらその関数のグラフを書くアプリを作成します。
HTML部分
まずHTML部分を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>入力された数式でグラフを書く</title> <meta name = "viewport" content = "width=device-width, initial-scale = 1.0"> <link rel = "stylesheet" href = "./style.css" type = "text/css" media = "all"> <style> </style> </head> <body> <div><canvas id = "canvas"></canvas></div> <div><input type="text" id = "calc-string"></div> <div><button onclick="drawGraph()">グラフを描画する</button></div> <div><input type="range" min="-5" max="-1" step="0.1" value="-2" id = "range"></div> <div id = "per-dot-text"></div> <script src="./index.js"></script> </body> </html> |
style.css
1 2 3 |
#calc-string, #range { width: 300px; } |
ページが読み込まれたときの処理
JavaScript部分を示します。
ページが読み込まれたらcanvasのサイズを調整して背景を黒で塗りつぶしたあとでxy軸を描画します。
最初は1ピクセルを0.01とします。したがって原点に相当する部分から100ピクセル右の部分が(1,0)となります。レンジスライダーの値は-5 ~ -1までで、この値で10のn乗を計算して1ピクセルあたりの値を変化させます。
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// canvasのサイズ const CANVAS_WIDTH = window.innerWidth; const CANVAS_HEIGHT = window.innerHeight * 0.8; const $canvas = document.getElementById('canvas'); const ctx = $canvas.getContext('2d'); let perDot = 0.01; // 最初は1ドットを0.01とする。 // したがって原点に相当する部分から10ピクセル右の部分が(1,0)となる window.onload = () => { $canvas.width = CANVAS_WIDTH; $canvas.height = CANVAS_HEIGHT; document.getElementById('range').addEventListener('input', (ev) => setPerDot(ev.target.value)); setPerDot(document.getElementById('range').value); drawXY(); } function setPerDot(rangeValue){ perDot = Math.pow(10, rangeValue); document.getElementById('per-dot-text').innerText = `1ピクセルを ${perDot} とする`; } |
XY軸を描画する処理を示します。canvasの中央が原点になります。XY軸をはっきりと描画したいので、先に方眼を描画します。最後にX軸とY軸を描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
function drawXY(){ ctx.fillStyle = '#000'; ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); // 方眼の幅が30ピクセル以上の場合のみ方眼を描画する //(CANVAS_WIDTH / 2, CANVAS_HEIGHT / 2)が原点 ctx.lineWidth = 1; if(1 / perDot >= 30){ ctx.strokeStyle = '#fff'; for(let i = 1; CANVAS_WIDTH / 2 - 1 / perDot * i >= 0; i++){ ctx.beginPath(); ctx.moveTo(CANVAS_WIDTH / 2 + 1 / perDot * i, 0); ctx.lineTo(CANVAS_WIDTH / 2 + 1 / perDot * i, CANVAS_HEIGHT); ctx.stroke(); ctx.beginPath(); ctx.moveTo(CANVAS_WIDTH / 2 - 1 / perDot * i, 0); ctx.lineTo(CANVAS_WIDTH / 2 - 1 / perDot * i, CANVAS_HEIGHT); ctx.stroke(); } for(let i = 1; CANVAS_HEIGHT / 2 - 1 / perDot * i >= 0; i++){ ctx.beginPath(); ctx.moveTo(0, CANVAS_HEIGHT / 2 + 1 / perDot * i); ctx.lineTo(CANVAS_WIDTH, CANVAS_HEIGHT / 2 + 1 / perDot * i); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0, CANVAS_HEIGHT / 2 - 1 / perDot * i); ctx.lineTo(CANVAS_WIDTH, CANVAS_HEIGHT / 2 - 1 / perDot * i); ctx.stroke(); } } // X軸の描画 ctx.strokeStyle = '#0f0'; ctx.beginPath(); ctx.moveTo(0, CANVAS_HEIGHT / 2); ctx.lineTo(CANVAS_WIDTH, CANVAS_HEIGHT / 2); ctx.stroke(); // Y軸の描画 ctx.beginPath(); ctx.moveTo(CANVAS_WIDTH / 2, 0); ctx.lineTo(CANVAS_WIDTH / 2, CANVAS_HEIGHT); ctx.stroke(); } |
関数のグラフを描画する処理
テキストボックスに入力された数式を取得して関数のグラフを描画する処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
function drawGraph(){ const calc_string = document.getElementById('calc-string').value; drawXY(); let start = true; ctx.beginPath(); ctx.strokeStyle = '#f00'; ctx.lineWidth = 4; for(let i = 0; i <= CANVAS_WIDTH; i++){ const x = (i - CANVAS_WIDTH / 2) * perDot; const y = Function(`return (${calc_string.split('x').join(x)})`)() / perDot; if(!isNaN(y) && y != Infinity && y != -Infinity){ if(start){ start = false; ctx.moveTo(i, CANVAS_HEIGHT / 2-y); } else ctx.lineTo(i, CANVAS_HEIGHT / 2-y); } else { start = true; ctx.stroke(); ctx.beginPath(); } } ctx.stroke(); } |
動作させてみる
テキストボックスに Math.pow(x, 2) と入力してから[グラフを描画する]ボタンをクリックすると放物線が描画されます。
Math.pow(x, x)だと(xのx乗)だとこうなります。
受験数学でよくでてくる eのx乗×sin x(Math.pow(2.71828, x) * Math.sin(x))だとこうなります。
ただ困ったことにこの方法だと連続ではない関数がつながって描画されてしまいます。
tan x のグラフを書くと未定義の部分がつながってしまいます。