Canvas JavaScript

Rotating a Cube with Faces

This JavaScript program demonstrates how to rotate a cube with faces on a canvas element using arrow key controls.

RotateCubeWithFaces.html

<!DOCTYPE html>
<html>
  <head>
    <title>XoaX.net's Javascript Perceptron Learning</title>
    <script type="text/javascript" src="RotateCubeWithFaces.js"></script>
    <style>
      .cFocus { border: 1px red solid; }
      .cBlur { border: none; }
    </style>
  </head>
  <body onload="Initialize()">
     <canvas id="idCanvas" width="640" height ="480" style="background-color: #F0F0F0;"></canvas>
  </body>
</html>

RotateCubeWithFaces.js

class CCanvasPlane {
	constructor() {
		var qCanvas = document.getElementById("idCanvas");
		this.mqContext = qCanvas.getContext("2d");
		this.mqAlpha = Math.PI/6;
		this.mqBeta = Math.PI/6;
	}
	
	Clear() {
		this.mqContext.clearRect(0, 0, 640, 480);
	}
	
	Left() {
		this.mqAlpha +=  Math.PI/12;
	}
	Right() {
		this.mqAlpha -=  Math.PI/12;
	}
	Up() {
		if (this.mqBeta + Math.PI/12 < Math.PI/2 - .01) {
			this.mqBeta +=  Math.PI/12;
		}
	}
	Down() {
		if (this.mqBeta - Math.PI/12 > -Math.PI/2 + .01) {
			this.mqBeta -=  Math.PI/12;
		}
	}
	
	CreateCoordinateVectors() {
		var daaA = [];
			daaA[0] = [Math.cos(this.mqAlpha), Math.sin(this.mqAlpha), 0];
			daaA[1] = [-daaA[0][1]*Math.sin(this.mqBeta), daaA[0][0]*Math.sin(this.mqBeta), Math.cos(this.mqBeta)];
			daaA[2] = [daaA[0][1]*daaA[1][2] - daaA[0][2]*daaA[1][1],
				daaA[0][2]*daaA[1][0] - daaA[0][0]*daaA[1][2],
				daaA[0][0]*daaA[1][1] - daaA[0][1]*daaA[1][0]];
		return daaA;
	}
	
	// The projection onto y (up) is in the opposite direction of the canvas pixels
	// The z-axis points into the viewing plane
	ProjectPointToCoordinates(daP) {
		var daaCoord = this.CreateCoordinateVectors();
		var daProj = [];
		for (var i = 0; i < 3; ++i) {
			daProj[i] = daaCoord[i][0]*daP[0] + daaCoord[i][1]*daP[1] + daaCoord[i][2]*daP[2];
		}
		return daProj;
	}
	
	ProjectionToPixel(daP) {
		return [320 + daP[0], 240 - + daP[1]];
	}
	
	
	DrawLine(dX1, dY1, dX2, dY2, sColor) {
		this.mqContext.globalAlpha = 1.0;
		this.mqContext.strokeStyle = sColor;
		this.mqContext.beginPath();
		this.mqContext.moveTo(dX1, dY1);
		this.mqContext.lineTo(dX2, dY2);
		this.mqContext.stroke();
	}

	DrawCubeFaces() {
		// The z-vector points toward us. So, if its coordinate is postive, we draw the low side first.
		var daBasis = this.CreateCoordinateVectors();
		var saColors = ["#FF8080", "#80FF80", "#8080FF", "#80FFFF", "#FF80FF", "#FFFF80"];
		const kdLow = -100;
		const kdHigh = 100;
		var daRange = [kdLow, kdHigh];
		
		for (var j = 0; j < 2; ++j) {
		  // Select the axis: 0 = x, 1 = y, 2 = z
		  for (var i = 0; i < 3; ++i) {
		  	// Select the far side and then the close side
			  var saSideColors = [saColors[i+3], saColors[i]];
			  var daSides = [kdLow, kdHigh];
			  if (daBasis[2][i] < 0) {
				  daSides[0] = kdHigh;
				  daSides[1] = kdLow;
				  saSideColors[0] = saColors[i];
				  saSideColors[1] = saColors[i+3];
			  }
			  // Choose the first vextex on the side with the lowest coordinates
				this.mqContext.beginPath();
				var daP1 = [0,0,0];
				daP1[i] = daSides[j];
				daP1[(i+1)%3] = daRange[0];
				daP1[(i+2)%3] = daRange[0];
				var daProj1 = this.ProjectPointToCoordinates(daP1);
				var daPixel1 = this.ProjectionToPixel(daProj1);
				this.mqContext.moveTo(daPixel1[0], daPixel1[1]);
				// Draw the other three vertices of the side
				for (var k = 0; k < 3; ++k) {
					var iC1 = ((k < 2) ? 1 : 0);
					var iC2 = ((k > 0) ? 1 : 0);
					daP1[(i+1)%3] = daRange[iC1];
					daP1[(i+2)%3] = daRange[iC2];
					daProj1 = this.ProjectPointToCoordinates(daP1);
					daPixel1 = this.ProjectionToPixel(daProj1);
					this.mqContext.lineTo(daPixel1[0], daPixel1[1]);
				}
				this.mqContext.globalAlpha = .5;
				this.mqContext.closePath();
				this.mqContext.strokeStyle = "black";
				this.mqContext.lineWidth = 1;
				this.mqContext.stroke();
				this.mqContext.globalAlpha = .9;
				this.mqContext.fillStyle = saSideColors[j];
				this.mqContext.fill();
				this.mqContext.globalAlpha = 1.0;
			}
		}
	}
	
	DrawAxes() {
		this.mqContext.lineWidth = 1;
		var saColors = ["red", "green", "blue"];
		var daProjOrigin = this.ProjectPointToCoordinates([0,0,0]);
		var daOriginPixel = this.ProjectionToPixel(daProjOrigin);
		const kdLength = 300;
		for (var i = 0; i < 3; ++i) {
			var daEndpoint = [0,0,0];
			daEndpoint[i] = kdLength;
			var daProjEnd = this.ProjectPointToCoordinates(daEndpoint);
			var daPixelEnd = this.ProjectionToPixel(daProjEnd);
			this.DrawLine(daOriginPixel[0], daOriginPixel[1], daPixelEnd[0], daPixelEnd[1], saColors[i]);
		}
	}
}

var qCP = null;

function Initialize() {
	qCP = new CCanvasPlane();
	qCP.DrawCubeFaces();
	qCP.DrawAxes();
	window.onkeydown=KeyDownFunction;
	window.addEventListener("focus", FocusFunction);
	window.addEventListener("blur", BlurFunction);
	window.focus();
	FocusFunction();
}

function FocusFunction() {
	document.getElementById("idCanvas").className ='cFocus';
}

function BlurFunction() {
	document.getElementById("idCanvas").className ='cBlur';
}

function KeyDownFunction(e) {
	var iKeyUp = 38;
	var iKeyLeft = 37;
	var iKeyRight = 39;
	var iKeyDown = 40;
	var iKeyCode = 0;
	if (e) {
		iKeyCode = e.which;
	} else {
		iKeyCode = window.event.keyCode;
	}
	qCP.Clear();
	switch (iKeyCode) {
		case iKeyUp: {
			qCP.Up();
			// Prevent the window scrolling
			e.preventDefault();
			break;
		}
		case iKeyLeft: {
			qCP.Left();
			// Prevent the window scrolling
			e.preventDefault();
			break;
		}
		case iKeyRight: {
			qCP.Right();
			// Prevent the window scrolling
			e.preventDefault();
			break;
		}
		case iKeyDown: {
			qCP.Down();
			// Prevent the window scrolling
			e.preventDefault();
			break;
		}
		default: {
			break;
		}
	}
	qCP.DrawCubeFaces();
	qCP.DrawAxes();
}
 

Output

 
 

© 2007–2025 XoaX.net LLC. All rights reserved.