携帯用は別ページ

== コピペでできる簡単なcanvasの作図 ==

〇 この文書は,HTML5上のcanvasをJavascriptを使って利用するための入門的な事柄を述べたものです.
〇 専門的に詳しいものではなく,枠内の参考文をコピー&ペーストすればそのまま作動テストできる簡単なものです.
〇 実際には,筆者が10年もすれば忘れてしまいそうなポイントを,自分用にメモしたもので,見ていただいたり,参考にしていただくのは自由です.枠内をコピペすればそのまま使える.筆者は型版を1つ作っておいて,数値を変えるだけで1つの作業を仕上げるようにしている.(関数の定義が,関数の実行よりも前にあればよい.)

【はじめに】

 Web文書(HTML文)の上で,右図のような図形を表示する方法
 (ただし,Twitter(X)やFacebookのようにWeb画面上で編集するものではなく,メモ帳などのテキストエディタで書き込んで,ローカルファイル[自分のパソコン内のファイル]で作動を確認する使い方を前提とする.うまく作動できた場合に,転送できるサーバがあるのはさらに良い.自分のwebページがない場合,Googleブロガーのように,ソースコードでも編集できるものなら使える.)
 黒字の部分は必須,赤字の部分は各自で名前や数値を指定する.
ジョーク:油絵を描く布生地がcanvas,ダイハツの車がcanbus,大学の敷地はcampus → 筆者は覚える気がないので,以下の枠内をコピペで使う.

canvasの設置
//名前を付けてcanvasのオブジェクトを作る
<canvas id="my_canvas1_1" width="100" height="160">
</canvas>
 HTML本文の中で,上記の識別子(id)の記述よりも後に
<script type="text/javascript"></script>を書いて,その中に次の記述を入れる(Javascriptが識別子を参照して,プログラムとして働くためには,そのプログラムよりも前にcanvas id=' 'と書いてなければならない.)
Javascriptの記述
//ビルドイン・オブジェクトを利用して,
//2次元キャンバスのコンテキストを得る
cname1_1 = document.getElementById("my_canvas1_1");
ctx1_1 = cname1_1.getContext("2d");

【ビューポートの設定】

ビューポートの設定
//スクリーン座標と数学座標を対応させる
ctx1_1.translate(50, 60);
ctx1_1.scale(10, -10);
〇 viewportを直訳すると「のぞき窓」になる.(viewportは,スマホなどの画面に表示される画像の比率を表す用語にも使われており,その上にcanvasでの変換の話が重なると込み入ってくるが,単純化して右図で解説する)
〇 まず,translate(\(x_0,y_0\))によって,screen座標の原点が(\(x_0,y_0\))に移動される
screen座標は,y座標が大きいほど下になることに注意
〇 translate(50, 60)により,width="100" height="160で確保された,横幅100ピクセル,縦幅160ピクセルの領域は,\(-50\leqq x\leqq 50\),\(-60\leqq y\leqq 100\)になる.
〇 次に,scale(\(k_x,k_y\))により,数学座標の\(k_x\)倍がscreen \(x\)座標になり,数学座標の\(k_y\)倍がscreen \(y\)座標になる.
通常,数学座標は,y座標が大きいほど上になるので,scale(10, −10)により,x座標は単純に10倍されるが,y座標は符号が逆になって,上が正になる事に注意
〇 結局,これら2段階の手続きにより,数学座標の\(-5\leqq x\leqq 5,-10\leqq y\leqq 6\)が,横幅100ピクセル,縦幅160ピクセルのscreen領域に投影されることになる.
scale(\(k_x,k_y\))の\(x_k\)と\(y_k\)の比率(横方向と縦方向の倍率)は,scale(\(20,-10\))のような大きさの異なる値を指定することもできるが,後に述べるテキスト文字(A,X,3など)も使う場合,結果的に縦横比が1になる組合せでないと奇妙な文字になる
◎ 実際の作業に沿って,逆算で手順を要約すると
//@ canvasで横縦の領域を確保する:
<canvas id="my_canvas1_1" width="100" height="160">
</canvas>
//A数学座標の原点とする場所を目印として,translate:
ctx1_1.translate(50, 60);
//B数学座標→screen座標の倍率を書く:
ctx1_1.scale(10, -10);

 とりあえず,ここまでの解説に出てきた内容で,実際に表示できるソース・コード(初めに示した例のソースコード)
(枠内をマウスでドラッグ→右クリック→コピー→テキストエディタ上で貼り付け[以降のソースコードも同様])
<canvas id="my_canvas1_1" width="100" height="160">
</canvas>

<script type="text/javascript">
cname1_1 = document.getElementById("my_canvas1_1");
ctx1_1 = cname1_1.getContext("2d");
ctx1_1.translate(50, 60);
ctx1_1.scale(10, -10);
ctx1_1.strokeStyle = "black";
ctx1_1.lineWidth = 0.1;

ctx1_1.beginPath();
ctx1_1.moveTo(0, 4);
ctx1_1.lineTo(-4, -7);
ctx1_1.lineTo(2, -8);
ctx1_1.lineTo(4, -6);
ctx1_1.lineTo(0, 4);
ctx1_1.lineTo(2, -8);
ctx1_1.stroke();

ctx1_1.setLineDash([0.3, 0.2]);

ctx1_1.beginPath();
ctx1_1.moveTo(-4, -7);
ctx1_1.lineTo(4, -6);
ctx1_1.stroke();

ctx1_1.setLineDash([]);

ctx1_1.scale(1, -1);
ctx1_1.strokeStyle = "black";
ctx1_1.fillStyle = 'black';
ctx1_1.font = 'normal 1.5px "Times New Roman"';
ctx1_1.fillText('O',-0.7, -4.2);
ctx1_1.fillText('A',-5.1, 7);
ctx1_1.fillText('B',2, 9);
ctx1_1.fillText('C',4, 7.5);
</script>

【viewport環境でのテキストの書き込み】

viewport環境でのテキストの書き込み
//上下を逆にする
ctx1_1.scale(1, -1);
ctx1_1.font = 'normal 1.5px "Times New Roman"';
//数学座標\(x,y\)の場所を\(x,-y\)と見なして文字を書く
ctx1_1.fillText('O',-0.7, -4.2);
〇 はじめに,canvasで横50ピクセル×縦60ピクセルの描画領域を確保した場合,ビューポートも何も設定せずにそのまま使うと,screen座標はxの増加する向きは右,yの増加する向きは下となって,文字は上向きに表示される.
図1
〇 右の図1は,50×60の描画領域で,x=10,y=20の位置にfontSizeが20ピクセルの\(A\)という文字を書き込み,x=30,y=40の位置にfontSizeが20ピクセルの\(B\)という文字を書き込んだものです.
 赤丸で示した点がx=10,y=20 及び,x=30,y=40の位置で,その位置を左下端として「x座標,y座標が大きくなる向きに」fontSizeの文字が書かれる.
図2
〇 これに対して,右の図2は,scale(1,-1)により,数学座標に合わせて上向きにy座標が増えるようにしたものです.
 この場合,赤丸で示した点がx=10,y=20 及び,x=30,y=40の位置で,その位置を左上端として「x座標,y座標が大きくなる向きに」fontSizeの文字が書かれる.
 このように,y座標が増える向きが逆になると「文字の形が上下逆になって困る」ので,通常,次の方法で解決する.
@ 図形については,viewportを設定して,数学座標の通りに描く.
A 点や線などに文字で名前を書くときだけ,scale(1,−1)として「座標を上下逆にする」.このとき,図形で\(x=3,y=4\)などとなっている点に重ねて文字を書くには\(x=3,y=-4\)のように,y座標だけ符号が反対の数字を指定すればよい.
 このとき,その位置を左下端として「x座標,y座標が大きくなる向きに」fontSizeの文字が書かれる.

図3
〇 右の図3は,50×60のcanvasに対して,tanslate(0,60); scale(10, -10)でviewportを設定して,数学座標\(0\leqq x\leqq 5,0\leqq y\leqq 6\)で図形を描けるようにしたものです.
 この場合,赤丸で示した点がx=1,y=2 及び,x=3,y=4の位置で,その位置を左下端として「x座標,y座標が大きくなる向きに」fontSizeの文字が書かれる.
【viewport環境でのテキストの書き込み(2)】
〇 viewportの設定のときに,縦横の倍率を異なる値にしたとき,直線や円のような図形要素は普通に描かれるが,テキストを重ねて書き込むときに,少し煩わしいことが起きる.
〇 右図では,screen領域150×100に対して,原点を\((50,60)\)に移してから,数学座標を横20倍,縦10倍の比率でviewportを設定したものです.
ctx1_5_2.translate(50,60);
ctx1_5_2.scale(20,-10);
 \(50\div 20= 2.5, 100\div 20=5\)だから,\(-2.5\leqq x\leqq 5\)
 \(60\div 10= 6, 40\div 10=4\)だから,\(-4\leqq y\leqq 6\)
〇 この図形に対して,scale(1, −1);を使って上下逆にしてから,文字を書き込むと,黒字で示したように,横長の文字になる.
〇 他方で,scale(1, −2);を使って,縦横倍率を1:1に戻してから,文字を書き込むと,赤字で示したように画像のy座標と文字のy座標とが,符号だけでは済まない煩わしい対応関係になる.
〇 このようにして,画像も文字も書き込む場合は,viewportの設定において,なるべく縦横比 \(scale(kx, ky)\);の\(kx, ky\)は同じ比率にした方が扱いやすい.

【viewport環境でのmathjax数式の書き込み】
※以下の解説は,多数説とは異なる筆者独自の説です.多数説は,svgグラフィックスで描いた数式を画像イメージとして導入するようですが,筆者はもっと単純に重ねて書く方法を使っている
mathjax数式の書き込み
<div style="... position:relative;">
<canvas id=".." style="position:absolute;"></canvas>
<span style="position:absolute;
left:??px;top:??px;">\(2\sqrt{3}\)
</span>
</div>
図4 \(2\sqrt{3}\)
〇 canvasのコンテキストで,fillText('文字列',x座標, y座標) の形で書き込むと,普通の文字列は表示されるが,mathjaxを使った\(2\sqrt{3}\)のような式を,文字列の箇所にそのまま代入しても,表示されない.
〇 ネットには,SVGグラフィックスを使ってimg形式で取り込む方法などが書かれているが,筆者はもっと直接的に上から書き込む方法を使っている.すなわち,初めに
 <div style="... position:relative;"></div> で囲った式を作り,その中に「canvasの描画」と「mathjaxの数式」をposition:absoluteで書き込むと,好きな場所に重ねて書き込むことができる.使うときは,次の形になる.
<div style="position:relative;">@
<canvas id=".." style="position:absolute;"></canvas>A
<span style="position:absolute;left:??px;top:??px;">
ここにmathjaxの数式を書くB
</span>
</div>
 これにより,@の場所にAのcanvas描画もBのmathjax数式も重ねて書かれる.その際,Bはleft:#$px;top:&%px;の指定により,canvasの左上端から右へ#$ピクセル,下へ&%ピクセル移動した点に書かれる.(canvasのviewport設定とは無関係に,初めのscreen座標で考える)

図4のソースコード(コピペ可)
<div style="width:100px;height:160px;
position:relative;float:right;margin-left:10px;">
<canvas id="my_canvas1_6" width="100" height="160" 
style="position:absolute;"></canvas>
<span style="position:absolute;left:20px;top:60px;">
\(2\sqrt{3}\)</span>
</div>

<script type="text/javascript">
cname1_6 = document.getElementById("my_canvas1_6");
ctx1_6 = cname1_6.getContext("2d");
ctx1_6.translate(50, 60);
ctx1_6.scale(10, -10);

ctx1_6.strokeStyle = "black";
ctx1_6.lineWidth = 0.1;

ctx1_6.beginPath();
ctx1_6.moveTo(0, 4);
ctx1_6.lineTo(-4, -7);
ctx1_6.lineTo(2, -8);
ctx1_6.lineTo(4, -6);
ctx1_6.lineTo(0, 4);
ctx1_6.lineTo(2, -8);
ctx1_6.stroke();

ctx1_6.setLineDash([0.3, 0.2]);

ctx1_6.beginPath();
ctx1_6.moveTo(-4, -7);
ctx1_6.lineTo(4, -6);
ctx1_6.stroke();

ctx1_6.setLineDash([]);

ctx1_6.scale(1, -1);
ctx1_6.strokeStyle = "black";
ctx1_6.fillStyle = 'black';
ctx1_6.font = 'normal 1.5px "Times New Roman"';
ctx1_6.fillText('O',-0.7, -4.2);
ctx1_6.fillText('A',-5.1, 7);
ctx1_6.fillText('B',2, 9);
ctx1_6.fillText('C',4, 7.5);
</script>
 図と数式が重なるときに,どちらかを上に(重なりとして手前に)見せるには,CSS上のz-index:2,3,4...;などを大きくすればよい.
 図4-2
\(2\sqrt{3}\)

<div style="width:100px;height:160px;
position:relative;float:right;margin-left:10px;">
<canvas id="my_canvas1_6" width="100" height="160" 
style="position:absolute;"></canvas>
<span style="position:absolute;left:15px;top:80px;
font-size:12px;
color:red;z-index:2;background-color:white;"
>\(2\sqrt{3}\)</span>
</div>

【よく使う小道具の作成(直角の記号)】

〇 右図のような立体があるときに,直角を示す補助記号を付けたいことが多い.
 このような場合に,個別に直角の記号を作るのは,結構煩わしい作業になる.
 また,直角の記号ではあるが,立体を見ている図なので,投影図が直角になるとは限らない.
〇 そこで,右図において,点B(a, b)の補助として,\(\mathrm{\overrightarrow{BC}}=(p,q)\)と\(\mathrm{\overrightarrow{BA}}=(s,t)\)に沿って,1辺の長さが\(r\)の直角記号を描くために,\(\mathrm{B}\)以外に3点\((x_1,y_1),(x_2,y_2),(x_3,y_3)\)を機械的に計算し,それらをmoveTo(), lineTo()で結ぶようにしたい.なお,\(\mathrm{\overrightarrow{BC}}\)から\(\mathrm{\overrightarrow{BA}}\)の間は,劣角(180°よりも小さい角度)で結ぶものとし,左回りに進むようにとる.
よく使う小道具の作成(直角の記号)
//角の点の座標(a,b), ベクトル(p,q),(r,s),直角記号の1辺r
//ctx_nameは,canvasコンテキストの名前
function sub_sq(a,b,p,q,s,t,r, ctx_name)
{if(p*p+q*q == 0)
	return 0;
 if(s*s+t*t == 0)
	return 0;

 xx1 = a+r*p/Math.sqrt(p*p+q*q);
 yy1 = b+r*q/Math.sqrt(p*p+q*q);
 xx2 = xx1+r*s/Math.sqrt(s*s+t*t);
 yy2 = yy1+r*t/Math.sqrt(s*s+t*t);
 xx3 = a+r*s/Math.sqrt(s*s+t*t);
 yy3 = b+r*t/Math.sqrt(s*s+t*t);

ctx_name.beginPath();
ctx_name.moveTo(xx1,yy1);
ctx_name.lineTo(xx2,yy2);
ctx_name.lineTo(xx3,yy3);
ctx_name.stroke();
}
//BCとBAの間の直角記号
sub_sq(0,2, 5,3, -3,1, 0.5, ctx2_1);
〇 上記のように関数sub_sq()を作ると,点E(0, -2)の補助として,\(\mathrm{\overrightarrow{EF}}=(5,3)\)と\(\mathrm{\overrightarrow{EB}}=(0,4)\)に沿って,直角記号を描くには,次の文を書き込めばよい.
sub_sq(0, -2, 5,3, 0,2,  0.5, ctx2_1);
〇 同様にして,点D(-3, -1)の補助として,\(\mathrm{\overrightarrow{DE}}=(3,-1)\)と\(\mathrm{\overrightarrow{DA}}=(0,4)\)に沿って,直角記号を描くには,次の文を書き込めばよい.
sub_sq(-3,-1, 3,-1, 0,4,  0.5, ctx2_1);
【よく使う小道具の作成(線分の長さの表示)】

〇 右図のような立体について,辺の長さを表示することを考える.
〇 具体的には,2点A, Bの座標\(\mathrm{A}(aa,bb),\mathrm{B}(cc,dd)\)及び円弧の半径\(rr\)が決まれば,機械的に円弧が引ける関数を作る.ただし,\(6\hspace{2px}cm\)のような長さを書き込む空白を開けて,2つの円弧を描く.そのために,長さを表す破線の円弧を「始線〜40%まで」「60%〜終線まで」の2つに分けて描く.

〇 次に試作した関数では,
@ \(\mathrm{A}(aa,bb)\)から\(\mathrm{B}(cc,dd)\)へ\(x\)成分が増加するように2点\(\mathrm{A,B}\)を選ぶ.
A \(aa=cc\)のときは,\(bb\gt dd\)となるように選ぶものとする.(\(arcsin\theta\)の関数の使い方で,逆にすると優弧を描いてしまい,うまく行かない)
B \(\mathrm{A\rightarrow B}\)の向きに対して,左側に円弧を作るときは,flag=0,右側はflag=1とする.
〇 右の例では,ACの長さは,赤破線では辺と重なって見にくいので,黒破線の方が好まれるでしょう.
 BCの長さは,黒破線では辺と重なって見にくいので,赤破線の方が好まれるでしょう.
 このように,具体的な図に応じて,辺の左に表示するか右に表示するかを選べばよい.

よく使う小道具の作成(線分の長さの表示)
<div style="width:150px;height:150px;position:relative;
float:right;margin-left:10px;">
<canvas id="my_canvas2_2" width="150" height="150"></canvas>
</div>

<script type="text/javascript">
dx2_2 = 0.01;
cname2_2 = document.getElementById("my_canvas2_2");

ctx2_2 = cname2_2.getContext("2d");

ctx2_2.translate(60, 105);
ctx2_2.scale(15, -15);//x=10倍, y=10倍

ctx2_2.lineWidth = 0.08;

ctx2_2.strokeStyle = "black";
//円弧を2つに分けて描く関数の作成
//円弧を2つに分けて描く関数の作成
// 端1(aax, bby),端2(ccx, ddy),コンテキスト名context_name,
// rr_d = 幅デフォルトは1, left_right = デフォルト左-1, 右1, 数値用空白sp_d = 0.2 第6,7,8引数は省略可能
function sub_arc_quod(aax, bby, ccx, ddy,context_name, rr_d, left_right, sp_d)
{if(sp_d == undefined)
	sp_d = 0.2;//デフォルトは0.2=0.4〜0.6
 if(rr_d == undefined)
	rr_d = 1;//デフォルトは1
 if(left_right == undefined)
	left_right = -1;//デフォルトは左

 sub_x = ccx-aax;
 sub_y = ddy-bby;
 len_v = Math.sqrt(sub_x ** 2+ sub_y **2);
 if(len_v <= 0)
	return;
 delta_x = sub_x / len_v;
 delta_y = sub_y / len_v;
 if(left_right >= 0)
	{delta_nxr = delta_y;
	 delta_nyr = -delta_x;
	}
 else
	{delta_nxl = -delta_y;
	 delta_nyl = delta_x;
	}
 if(left_right >= 0)
		{spx2 = aax + (0.5+sp_d/2)*len_v*delta_x + rr_d * delta_nxr * (0.5+sp_d/2) *(1-(0.5+sp_d/2));
		 spy2 = bby + (0.5+sp_d/2)*len_v*delta_y + rr_d * delta_nyr * (0.5+sp_d/2) * (1-(0.5+sp_d/2));
		}
	 else
		{spx2 = aax + (0.5+sp_d/2)*len_v*delta_x + rr_d * delta_nxl * (0.5+sp_d/2) *(1-(0.5+sp_d/2));
		 spy2 = bby + (0.5+sp_d/2)*len_v*delta_y + rr_d * delta_nyl * (0.5+sp_d/2) * (1-(0.5+sp_d/2));
		}

 context_name.beginPath();
 context_name.moveTo(aax, bby);
 for(kk = 0; kk <=0.5-sp_d/2; kk += 0.01)
	{if(left_right >= 0)
		{xx_k = aax + kk*len_v*delta_x + rr_d * delta_nxr * kk *(1-kk);
		 yy_k = bby + kk*len_v*delta_y + rr_d * delta_nyr * kk * (1-kk);
		}
	 else
		{xx_k = aax + kk*len_v*delta_x + rr_d * delta_nxl * kk *(1-kk);
		 yy_k = bby + kk*len_v*delta_y + rr_d * delta_nyl * kk * (1-kk);
		}
	 context_name.lineTo(xx_k, yy_k);
	}
 context_name.stroke();

 context_name.beginPath();
 context_name.moveTo(spx2, spy2);
 for(kk = 0.5+sp_d/2; kk <= 1; kk += 0.01)
	{if(left_right >= 0)
		{xx_k = aax + kk*len_v*delta_x + rr_d * delta_nxr * kk *(1-kk);
		 yy_k = bby + kk*len_v*delta_y + rr_d * delta_nyr * kk * (1-kk);
		}
	 else
		{xx_k = aax + kk*len_v*delta_x + rr_d * delta_nxl * kk *(1-kk);
		 yy_k = bby + kk*len_v*delta_y + rr_d * delta_nyl * kk * (1-kk);
		}
	 context_name.lineTo(xx_k, yy_k);
	}
 context_name.stroke();
}
ctx2_2.strokeStyle = "black";
ctx2_2.setLineDash([0.3, 0.2]);

//ACで左
ctx2_2.strokeStyle = "black";
sub_arc_quod(-3,3, 5,5, ctx2_2, 3)
//ACで右
ctx2_2.strokeStyle = "red";
sub_arc_quod(-3,3, 5,5, ctx2_2, 2,1)

//BCで左
ctx2_2.strokeStyle = "black";
sub_arc_quod(0,2,5,5, ctx2_2, 1,-1);

//BCで右
ctx2_2.strokeStyle = "red";
sub_arc_quod(0,2,5,5, ctx2_2, 1,1);

//ADで左
ctx2_2.strokeStyle = "black";
sub_arc_quod(-3,3,-3,-1, ctx2_2,2,-1);
//ADで右
ctx2_2.strokeStyle = "red";
sub_arc_quod(-3,3,-3,-1, ctx2_2,2,1);

//DEで左
ctx2_2.strokeStyle = "black";
sub_arc_quod(-3,-1,0,-2, ctx2_2,2,-1);
//BEで左
sub_arc_quod(0,2,0,-2, ctx2_2,2,-1);

ctx2_2.strokeStyle = "red";
//DEで右
sub_arc_quod(-3,-1,0,-2, ctx2_2,2,1);
//BEで右
sub_arc_quod(0,2,0,-2, ctx2_2,2,1);
</script>
【よく使う小道具の作成(矢印の表示)】

〇 右図のような図形について,線分の端に矢印を追加することを考える.
〇 具体的には,2点A, Bの座標\(\mathrm{A}(aa,bb),\mathrm{B}(cc,dd)\)及び矢印の長さ\(rr\)が決まれば,機械的に矢印が描ける関数を作る.矢先を塗り込むタイプと塗り込まないタイプを選べるようにする.
よく使う小道具の作成(矢印の表示)
//始点(a1,b1),終点(c1,d1),矢尻の大きさarr_r1,
//コンテキスト名c_name,塗り込む:第7引数あり
function sub_arrow2(a1,b1,c1,d1,ar_r1, c_name, nuri)
{ar_rr = Math.sqrt((c1-a1)*(c1-a1)+(d1-b1)*(d1-b1));
 Mx = c1-(c1-a1)/ar_rr*ar_r1;
 My = d1-(d1-b1)/ar_rr*ar_r1;
 arx1 = (2*Mx+Mx+ar_r1*(d1-b1)/ar_rr)/3;
 ary1 = (2*My+My+ar_r1*(a1-c1)/ar_rr)/3;

 arx2 = (2*Mx+Mx-ar_r1*(d1-b1)/ar_rr)/3;
 ary2 = (2*My+My-ar_r1*(a1-c1)/ar_rr)/3;

 arx0 = c1;
 ary0 = d1;

c_name.beginPath();
c_name.moveTo(arx1, ary1);
c_name.lineTo(arx0, ary0);
c_name.lineTo(arx2, ary2);
//--第7引数がないときは,黒塗りなしになる
if(nuri != undefined)
 {c_name.closePath();
  c_name.fill();
 }
//----------
c_name.stroke();

c_name.beginPath();
c_name.moveTo(a1,b1);
c_name.lineTo(c1,d1);
c_name.stroke();
}

sub_arrow2(-3,3,-3,-1, 0.7, ctx3_1);
sub_arrow2(5,5,0,2, 0.8, ctx3_1);
sub_arrow2(-3,-1,0,-2, 0.7, ctx3_1, 1);
sub_arrow2(5,1,5,5, 0.6, ctx3_1, 1);

【数学関数のグラフの表示】

〇 次の図のような関数のグラフを,canvas上に表示することを考える.
〇 コンピュータ画面上では,画素数は1600×800などと限られた数であるので,どんなに細かく描いても画素よりも小さい分け方はできない.
 だから,例えば,canvasで横幅100ピクセルの領域を確保して,そこに\(-5\leqq x\leqq 5\)の範囲でグラフを描く場合,\(0\leqq x\leqq 1\)で10ピクセル以上細かく描いても,画面の表示が対応できない.この場合,\(x\)の増分が\(\Delta x=0.1\)で点を結んでいけば,十分正確で滑らかなグラフになる.
〇 初めの1点だけは moveTo()で,残りはlineTo()で結んでいくと,グラフが描ける.
図1 \(y=x^3-3x\)
図2 \(y=2\sin x+\cos 2x\)

図3 \(y=x^2e^{-x}\)
図4 \(\displaystyle y=\frac{x^3}{x^2-1}\)

グラフを描くための関数の作成
//図1の場合
function fx4_1(xx)
{return xx*xx*xx-3*xx;
}

ctx4_1.beginPath();
ctx4_1.moveTo(-3, fx4_1(-3));
for( vx = -3; vx <= 3; vx += 0.01)
	ctx4_1.lineTo(vx, fx4_1(vx));
ctx4_1.stroke();

//図2の場合
function fx4_2(xx)
{return 2*Math.sin(xx)+Math.cos(2*xx);
}

ctx4_2.beginPath();
ctx4_2.moveTo(-1, fx4_2(-1));
for( vx = -1; vx <= 6; vx += 0.01)
	ctx4_2.lineTo(vx, fx4_2(vx));
ctx4_2.stroke();

//図4の場合⇒分母が0となる値を避ける
function fx4_4(xx)
{return xx * xx * xx / (xx * xx - 1);
}
ctx4_4.beginPath();
ctx4_4.moveTo(-6, fx4_4(-6));
for( vx = -6; vx < -1; vx += dx4_4)
	ctx4_4.lineTo(vx, fx4_4(vx));
ctx4_4.stroke();

ctx4_4.beginPath();
ctx4_4.moveTo(-1+0.01, fx4_4(-1+0.01));
for( vx = -1+0.01; vx < 1; vx += 0.01)
	ctx4_4.lineTo(vx, fx4_4(vx));
ctx4_4.stroke();

ctx4_4.beginPath();
ctx4_4.moveTo(1+0.01, fx4_4(1+0.01));
for( vx = 1+0.01; vx < 6; vx += 0.01)
	ctx4_4.lineTo(vx, fx4_4(vx));
ctx4_4.stroke();
〇 図4のように,定義域に分母が0となる値が含まれる場合は,その値よりも小さい範囲,その値よりも大きい範囲に分けてつないでいく.
【参考:Excel上でのグラフの表示】

〇 ここまでに述べたcanvas上でのグラフの表示では,関数や式が前面に出てきて,理屈っぽいとことがあるが,実際に行っていることは「点をつないでいるだけ」です.

Excelグラフ
〇 上記と同じ作図は,Excelを使ってもできる.Excelでは,縦に数万個の座標を並べることができる,上記のcanvasで「関数と式」を書き込んだ代わりに,「関数と数値」を書き込むとグラフになる.
〇 具体的には,ワークシートのA1に-2を記入し,下向きに2になるまで,増分0.1で「フィル→連続データ」を行う(41個).
 B1に=A^3-3*A1と記入して,下向きにドラッグする.
 できたデータを反転表示して,挿入→グラフ(散布図.曲線付き)を選べばよい.(折れ線グラフを選ぶとx座標が入らずに,番号になってしまうので,x座標も使うためには散布図のほうがよい)
※ 高校生なら,やり方の説明15分,作業30分程度で作品として提出できるでしょう.

【透明度の設定】

透明度の設定:実際には不透明度の設定
ctx1_1.globalAlpha=0.3;
〇 色を重ねて塗り込むときに,下になる色も見えるように透明度を設定することができる.
〇 線と塗り込みに一律の透明度を設定するときに,
 globalAlpha =\(k\)
(\(0\leqq k\leqq 1\))などと指定する.
〇 右の図の上の部分は,赤青緑ともglobalAlpha =\(0.3\)とした場合の三原色の重なり具合を示したものであるが,「何と!」光の三原色ならば3色が重なれば白になるはずの話が,重なる部分が黒っぽく,絵具の3原色として働いている.
〇 右の図の下の部分は,赤で
  globalAlpha =\(0.1,0.3,0.5,0.7,0.9\)
とした場合の見え方で,globalAlphaの値が大きいほど(ただし,≦1)色が濃いことが分かる.
※globalAlphaに対する「透明度」という翻訳は,誤解されやすい.実際には,上記のようにその色の「塗り込み度」「不透明度」になっている.

その色の透明度:transparencyと考えると,上記のように逆ではないのかと錯覚しやすいが,この定義を考えた人が,カメラレンズの前に置く「フィルターの透明度」を想定しているのなら,1で透明,0で不透明という感覚と合う

※globalというのは,以下は一律にその設定が適用されるということで,個々のもの・色について塗り込みの透明度を指定するには
 ctx1_2.fillStyle = 'rgb(255 0 0 / 30%)';
 ctx1_2.fillStyle = 'rgb(0 0 255 / 0.5)';
 ctx1_2.fillStyle = 'rgb(0 255 0 / 0.7)';
などとすればよい.(下の四角3個がこれにあたる)
(透明度:続き)
〇 右の図は,Excelで「挿入→図形」の手順で作成した「透明度70%」の画像で,赤,青,緑の色を重ねて表示したものです.これが,canvasでのglobalAlpha = 0.3 の場合とほぼ一致する.
〇 【要約】
• canvasのglobalAlpha:色の塗り込み度=フィルターの透明度=大きいほど色が濃い
• Excelなど他の画像の透明度:そのものの透明度で=大きいほど淡く白くなる
• いずれも,液晶画面で光っているように見えるが,光の3原色の足し算ではなく,絵具の3原色の足し算になる??

※なぜAlphaというのか?きっちりと書かれたものを見たわけではないが,一般に欧米系の数学・統計の文書では,定数項,1次の係数を
  \(y=\alpha+\beta x\)
の形で書くことが多いようで,変化するものには\(\beta\),変化しないものには\(\alpha\)が好まれるようです.

〇 実際の使用例
〇 右の例では,ctx13_3.globalAlpha = 0.7;として,赤,青,緑の立体を重ねて描いています.
...メニューに戻る