#目標
- JavaScriptのcanvasで、テンキーを押したときに立方体を回転させてみる。
#下準備
まず、次のコードを保存しておきます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>3D</title>
</head>
<body>
<canvas id="canvas" width="400" height="400"></canvas>
<script>
</script>
</body>
</html>
以下、この<script></script>
内にJavaScriptのコードを書いていきます。
#描画関数の作成
##canvasを扱う準備
次の関数を書きます。
function draw(){ // 描画
const ctx = document.getElementById("canvas").getContext("2d");
ctx.clearRect(0, 0, 400, 400); // 初期化
}
このコードのctx.clearRect(0, 0, 400, 400);
で、画面を消去しています。
これで、JavaScriptのcanvasを扱う準備ができました。
##座標を記録
次に、頂点の座標を変数coord
に、面の情報をfaces
に記録します。
const coord = [ // 座標
[-100, -100, -100],
[ 100, -100, -100],
[-100, 100, -100],
[ 100, 100, -100],
[-100, -100, 100],
[ 100, -100, 100],
[-100, 100, 100],
[ 100, 100, 100]
];
const faces = [ // 面
[0,1,3,2],[5,4,6,7],
[0,1,5,4],[3,2,6,7],
[0,2,6,4],[3,1,5,7]
];
座標は、[x座標, y座標, z座標]
という形で記録します。
面の座標は、頂点の番号です。
##回転のための準備
回転させた後の座標を返す関数を作成しておきます。
let ang = Date.now()/810;
let ang2 = Date.now()/1081;
function rotat(x, y, z){ // 角度の分だけ回転
[x, y] = [x*Math.cos(ang1) - y*Math.sin(ang1), x*Math.sin(ang1) + y*Math.cos(ang1)];
[y, z] = [y*Math.cos(ang2) - z*Math.sin(ang2), y*Math.sin(ang2) + z*Math.cos(ang2)];
return [x,y,z];
}
ここで、ang1
はz軸を軸として回転する角度で、ang2
はx軸を軸として回転する角度です。
このとき、回転されているかを確認するため、暫定的に回転角度を時間によって変化するようにしておきます。
##描画開始
関数draw
内に描画させる関数を作成しておきます。
function draw(){ // 描画
const ctx = document.getElementById("canvas").getContext("2d");
ctx.clearRect(0, 0, 400, 400); // 初期化
for(let i in faces){ // 面の描画
let rtteds = []; //回転後の座標を記録する配列
for(let j of faces[i]){
rtteds.push(rotat(...coord[j])) // 回転後の座標
}
ctx.beginPath(); //描画開始
ctx.fillStyle = ["#333","#fff","#0f0","#f0f","#9ff","#ff9"][i];
ctx.moveTo(200+rtteds[0][0], 200+rtteds[0][2]);
for(let j of rtteds){
ctx.lineTo(200+j[0], 200+j[2]);
}
ctx.fill();
}
}
###実行結果
実際に実行してみると、不自然なことに気づきます。
シアンの面が表示されないのです。
これを解消するために、y座標が遠い順に描画するようにします。
そのために、遠さを調べる関数を用意します。
function rem(ary){ // 遠さを調べる関数
return ary.map(a=>rotat(...coord[a])[1]).reduce((a,b)=>a+b)/ary.length;
}
この関数は[1,2,4,3]
のように、座標の番号が与えられたときに、それぞれの回転後のy座標の平均を返します。
それで、draw()
を以下のように書き換えます。
function draw(){ // 描画
const ctx = document.getElementById("canvas").getContext("2d");
ctx.clearRect(0, 0, 400, 400); // 初期化
let sorted = faces.map((a,i)=>[a,i]).sort((a,b)=>rem(a[0])-rem(b[0])); // facesをsortしたもの
for(let i of sorted){ // 面の描画
let rtteds = []; //回転後の座標を記録する配列
for(let j of i[0]){
rtteds.push(rotat(...coord[j])) // 回転後の座標
}
ctx.beginPath(); //描画開始
ctx.fillStyle = ["#a05","#a00","#a50","#aa0","#5a0","#0a0"][i[1]];
ctx.moveTo(200 + rtteds[0][0], 200 - rtteds[0][2]);
for(let j of rtteds){
ctx.lineTo(200 + j[0], 200 - j[2]);
}
ctx.fill();
}
}
これで実行すると、遠い順に描画されるようになりました。
#キーでの移動
キーで移動させるには、次のプログラムを使います。
let ang1 = 0;
let ang2 = 0;
document.body.onkeydown = e =>{
switch(e.keyCode){
case 37:
ang1-=0.1; // 左
break;
case 38:
ang2-=0.1; // 上
break;
case 39:
ang1+=0.1; // 右
break;
case 40:
ang2+=0.1; // 下
break;
}
};
#完成
そうして、完成したのがこちらです。
3d test