4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

JavaScriptのcanvasで立方体を回転させてみる

Posted at

#目標

  • 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

4
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?