JSFiddle - React, Tailwind, and code Playground

by secretgspot

HTML

<canvas id='world' width='465' height='425'></canvas>
スクリーンまでの距離:<input type='text' id='f' value='200' onkeyup='fChanged(this);' />

CSS

body {
  background-color: #FFF;
  margin: 0;
  overflow: hidden;
}
#f {
  width: 4em;
}

JavaScript

/**
 * 1点透視図法
 * Z方向の直線はすべて消失点に収束されます
 * 
 * 遠近法
 * http://ja.wikipedia.org/wiki/%E9%81%A0%E8%BF%91%E6%B3%95
 */

var Vertex3D = function(x, y, z) {
  // 3D座標
  this.x = x || 0;
  this.y = y || 0;
  this.z = z || 0;
  
  // 2D座標
  this.screenX = 0;
  this.screenY = 0;
  
  this.project = function(scene) {
    this.screenX = f * (this.x - vx) / (f + this.z) + vx;
    this.screenY = f * (this.y - vy) / (f + this.z) + vy;
  };
};

var canvas = document.getElementById('world');
var cxt = canvas.getContext("2d");

// 消失点
var vx = Math.floor(canvas.width / 2);
var vy = Math.floor(canvas.height / 2);
// スクリーンまでの距離
var f = 200;

function render() {
  cxt.clearRect(0, 0, 600, 500);
  
  // 消失点を描画
  cxt.fillStyle = "#FF0000";
  cxt.beginPath();
  cxt.arc(vx, vy, 5, 0, Math.PI*2, false);
  cxt.fill();
  
  var cx = Math.floor(canvas.width / 2);
  var cy = Math.floor(canvas.height / 2);
  
  var v1 = new Vertex3D();
  var v2 = new Vertex3D();
  
  var i;
  
  v1.y = v2.y = cy;
  for(i = 0; i <= 400; i+=50) {
    v1.x = v2.x = cx + i - 200;
    v1.z = 0;
    v2.z = 400;
    drawLine(v1, v2);
    
    v1.x = cx - 200;
    v2.x = cx + 200;
    v1.z = v2.z = i;
    drawLine(v1, v2);
  }
  
  v1.x = v2.x = cx;
  for(i = 0; i <= 400; i+=50) {
    v1.y = v2.y = cy + i - 200;
    v1.z = 0;
    v2.z = 400;
    drawLine(v1, v2);
    
    v1.y = cy - 200;
    v2.y = cy + 200;
    v1.z = v2.z = i;
    drawLine(v1, v2);
  }
}

function drawLine(v1, v2) {
  v1.project();
  v2.project();
  cxt.lineWidth = 1;
  cxt.strokeStyle = "#FF0000";
  cxt.beginPath();
  cxt.moveTo(v1.screenX, v1.screenY);
  cxt.lineTo(v2.screenX, v2.screenY);
  cxt.closePath();
  cxt.stroke();
}

function mouseMove(e) {
  // 消失点を移動
  vx = e.clientX;
  vy = e.clientY;
  
  render();
}

function fChanged(txt) {
  var n = Number(txt.value);
  if(!isNaN(n)) {
    f = n;
    render();
  }
}

canvas.onmousemove = mouseMove;
render();