3D Canvas in HTML5

Creating a 3D Canvas Hexagon in HTML5

 
In this article, we will learn how to create a 3D object in HTML5. In this article, we will learn how to make a hexagon that will rotate around its axis.
 
Before creating our own 3D engine, we should first understand how the illusion of 3D space is created on a 2D screen. These illusions are called 3D projections. 3D projections map points onto a two-dimensional plane. In our case, the three-dimensional points define an object we wish to render, and the two-dimensional plane is the computer screen.
 
If we are defining the rotation of the plane with an angle, theta, the 3D projection calculations suddenly become quite simple! Any point on our three-dimensional plane can be defined with the following two equations that describe a point along the edges of an ellipse: x = A * cos(theta) y = B * sin(theta)
 
where A is half of the ellipse width, and B is half of the ellipse height.
 
We do all this in JavaScript. In this we first draw the front bottom of the Hexagon with the following:
  1. c.beginPath();  
  2. c.moveTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // top left  
  3. c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // top right  
  4. c.lineTo(myShape.bottomPlane.topRightX, myShape.bottomPlane.topRightY); // bottom right  
  5. c.lineTo(myShape.bottomPlane.topLeftX, myShape.bottomPlane.topLeftY); // bottom left  
  6. c.closePath();  
  7.    
  8. c.lineJoin = "round";  
  9. c.strokeStyle = "black";  
  10. c.lineWidth = lineWidth;  
  11. c.stroke();  
  12. c.fillStyle = "blue";  
  13. c.fill(); 
Then we draw the back bottom of the Hexagon with the following:
  1. c.beginPath();  
  2. c.moveTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // top left  
  3. c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // top right  
  4. c.lineTo(myShape.bottomPlane.bottomRightX, myShape.bottomPlane.bottomRightY); // bottom right  
  5. c.lineTo(myShape.bottomPlane.bottomLeftX, myShape.bottomPlane.bottomLeftY); // bottom left  
  6. c.closePath();  
  7.    
  8. c.lineJoin = "round";  
  9. c.strokeStyle = "black";  
  10. c.lineWidth = lineWidth;  
  11. c.stroke();  
  12. c.fillStyle = "blue";  
  13. c.fill(); 
After that  we draw the front top of the Hexagon with the following:
  1. c.beginPath();  
  2. c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left  
  3. c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right  
  4. c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // bottom right  
  5. c.lineTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // bottom left  
  6. c.closePath();  
  7.    
  8. c.lineJoin = "round";  
  9. c.strokeStyle = "black";  
  10. c.lineWidth = lineWidth;  
  11. c.stroke();  
  12. c.fillStyle = "green";  
  13. c.fill(); 
Then we draw the back top of the Hexagon with the following:
  1. c.beginPath();  
  2. c.moveTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // top left  
  3. c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // top right  
  4. c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // bottom right  
  5. c.lineTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // bottom left  
  6. c.closePath();  
  7.    
  8. c.lineJoin = "round";  
  9. c.strokeStyle = "black";  
  10. c.lineWidth = lineWidth;  
  11. c.stroke();  
  12. c.fillStyle = "blue";  
  13. c.fill(); 
Finally draw the top of the Hexagon with the following:
  1. c.beginPath();  
  2. c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left  
  3. c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right  
  4. c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // bottom right  
  5. c.lineTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // bottom left  
  6. c.closePath();  
  7.    
  8. c.strokeStyle = "black";  
  9. c.lineWidth = lineWidth;  
  10. c.stroke();  
  11. c.fillStyle = "green";  
  12. c.fill(); 
Similarily we draw the left and right side of the hexagon.
 
We generate the hexagon and rotate it using the "generate()" and "rotate()" methods, as in the following:
  1. function Shape(topPlane, centerPlane, bottomPlane)  
  2. {  
  3.     this.topPlane = topPlane;  
  4.     this.centerPlane = centerPlane;  
  5.     this.bottomPlane = bottomPlane;  
  6.   
  7.     this.rotate = function (newTheta)  
  8.     {  
  9.         topPlane.rotate(newTheta);  
  10.         centerPlane.rotate(newTheta);  
  11.         bottomPlane.rotate(newTheta);  
  12.     }  
  13.   
  14.     this.generatePlanes = function ()  
  15.     {  
  16.         topPlane.generate();  
  17.         centerPlane.generate();  
  18.         bottomPlane.generate();  
  19.     }  
Example:
  1. <!DOCTYPE html>  
  2.    
  3. <html lang="en" xmlns="http://www.w3.org/1999/xhtml">  
  4. <head>  
  5.     <meta charset="utf-8" />  
  6.     <title>ThreeD HTML5</title>  
  7.     <style>  
  8.         body {  
  9.             margin: 0px;  
  10.             padding: 0px;  
  11.         }  
  12.   
  13.         #myCanvas  
  14.         {  
  15.             border: 1px solid black;  
  16.         }  
  17.     </style>  
  18.     <script>  
  19.         var canvas = null;  
  20.         var c = null;  
  21.         var canvasWidth = 576;  
  22.         var canvasHeight = 400;  
  23.         var t = 0;  
  24.    
  25.         var myShape = null;  
  26.    
  27.         function Shape(topPlane, centerPlane, bottomPlane) {  
  28.             this.topPlane = topPlane;  
  29.             this.centerPlane = centerPlane;  
  30.             this.bottomPlane = bottomPlane;  
  31.    
  32.             this.rotate = function (newTheta) {  
  33.                 topPlane.rotate(newTheta);  
  34.                 centerPlane.rotate(newTheta);  
  35.                 bottomPlane.rotate(newTheta);  
  36.             }  
  37.    
  38.             this.generatePlanes = function () {  
  39.                 topPlane.generate();  
  40.                 centerPlane.generate();  
  41.                 bottomPlane.generate();  
  42.             }  
  43.         }  
  44.    
  45.         function Plane(centerX, centerY, planeLength, planeWidth, planeTilt, planeTheta) {  
  46.             this.centerX = centerX;  
  47.             this.centerY = centerY;  
  48.             this.planeLength = planeLength;  
  49.             this.planeTheta = planeTheta;  
  50.    
  51.             var lastPerspectiveX = null;  
  52.             var lastPerspectiveX2 = null;  
  53.             var planeNextCornerAngle = 2 * Math.asin(planeWidth / planeLength);  
  54.    
  55.             this.rotate = function (newTheta) {  
  56.                 planeTheta = newTheta - planeNextCornerAngle / 2;  
  57.             }  
  58.    
  59.             this.translate = function (newCenterX, newCenterY) {  
  60.                 centerX = newCenterX;  
  61.                 centerY = newCenterY;  
  62.             }  
  63.    
  64.             this.generate = function () {  
  65.                 var ovalLength = planeLength;  
  66.                 var ovalWidth = ovalLength * planeTilt;  
  67.    
  68.                 var perspectiveX = (ovalLength / 2) * Math.cos(planeTheta);  
  69.                 var perspectiveY = (ovalWidth / 2) * Math.sin(planeTheta);  
  70.                 var perspectiveX2 = (ovalLength / 2) * Math.cos(planeTheta + planeNextCornerAngle);  
  71.                 var perspectiveY2 = (ovalWidth / 2) * Math.sin(planeTheta + planeNextCornerAngle);  
  72.    
  73.                 this.topLeftX = (perspectiveX * 1) + centerX;  
  74.                 this.topLeftY = (perspectiveY * -1) + centerY;  
  75.                 this.bottomRightX = (perspectiveX * -1) + centerX;  
  76.                 this.bottomRightY = (perspectiveY * 1) + centerY  
  77.                 this.topRightX = (perspectiveX2 * 1) + centerX;  
  78.                 this.topRightY = (perspectiveY2 * -1) + centerY;  
  79.                 this.bottomLeftX = (perspectiveX2 * -1) + centerX;  
  80.                 this.bottomLeftY = (perspectiveY2 * 1) + centerY;  
  81.             }  
  82.         }  
  83.    
  84.         function clearCanvas() {  
  85.             canvas.width = 1;  
  86.             canvas.width = canvasWidth;  
  87.         }  
  88.    
  89.         function init() {  
  90.             canvas = document.getElementById("myCanvas");  
  91.             c = canvas.getContext("2d");  
  92.             canvas.width = canvasWidth;  
  93.             canvas.height = canvasHeight;  
  94.    
  95.             var topPlane = new Plane(288, 150, 100, 70, 0.5, theta);  
  96.             var centerPlane = new Plane(288, 200, 600, 70, 0.5, theta);  
  97.             var bottomPlane = new Plane(288, 250, 100, 70, 0.5, theta);  
  98.    
  99.             myShape = new Shape(topPlane, centerPlane, bottomPlane);  
  100.    
  101.             //drawScreen();  
  102.    
  103.             updateScreen();  
  104.         }  
  105.    
  106.         var theta = 0; // top left  
  107.         function drawScreen() {  
  108.    
  109.             var lineWidth = 2;  
  110.    
  111.             myShape.rotate(theta);  
  112.             //myPlane.translate(tankX, 100);  
  113.             myShape.generatePlanes();  
  114.    
  115.             // draw front bottom of Hexagon  
  116.             c.beginPath();  
  117.             c.moveTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // top left  
  118.             c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // top right  
  119.             c.lineTo(myShape.bottomPlane.topRightX, myShape.bottomPlane.topRightY); // bottom right  
  120.             c.lineTo(myShape.bottomPlane.topLeftX, myShape.bottomPlane.topLeftY); // bottom left  
  121.             c.closePath();  
  122.    
  123.             c.lineJoin = "round";  
  124.             c.strokeStyle = "black";  
  125.             c.lineWidth = lineWidth;  
  126.             c.stroke();  
  127.             c.fillStyle = "blue";  
  128.             c.fill();  
  129.    
  130.             // draw back bottom of Hexagon  
  131.             c.beginPath();  
  132.             c.moveTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // top left  
  133.             c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // top right  
  134.             c.lineTo(myShape.bottomPlane.bottomRightX, myShape.bottomPlane.bottomRightY); // bottom right  
  135.             c.lineTo(myShape.bottomPlane.bottomLeftX, myShape.bottomPlane.bottomLeftY); // bottom left  
  136.             c.closePath();  
  137.    
  138.             c.lineJoin = "round";  
  139.             c.strokeStyle = "black";  
  140.             c.lineWidth = lineWidth;  
  141.             c.stroke();  
  142.             c.fillStyle = "blue";  
  143.             c.fill();  
  144.    
  145.             // draw front top of Hexagon  
  146.             c.beginPath();  
  147.             c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left  
  148.             c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right  
  149.             c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // bottom right  
  150.             c.lineTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // bottom left  
  151.             c.closePath();  
  152.    
  153.             c.lineJoin = "round";  
  154.             c.strokeStyle = "black";  
  155.             c.lineWidth = lineWidth;  
  156.             c.stroke();  
  157.             c.fillStyle = "green";  
  158.             c.fill();  
  159.    
  160.             // draw back top of Hexagon  
  161.             c.beginPath();  
  162.             c.moveTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // top left  
  163.             c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // top right  
  164.             c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // bottom right  
  165.             c.lineTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // bottom left  
  166.             c.closePath();  
  167.    
  168.             c.lineJoin = "round";  
  169.             c.strokeStyle = "black";  
  170.             c.lineWidth = lineWidth;  
  171.             c.stroke();  
  172.             c.fillStyle = "blue";  
  173.             c.fill();  
  174.    
  175.             // draw top of Hexagon  
  176.             c.beginPath();  
  177.             c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left  
  178.             c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right  
  179.             c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // bottom right  
  180.             c.lineTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // bottom left  
  181.             c.closePath();  
  182.    
  183.             c.strokeStyle = "black";  
  184.             c.lineWidth = lineWidth;  
  185.             c.stroke();  
  186.             c.fillStyle = "green";  
  187.             c.fill();  
  188.    
  189.             if (isRightSideOfShapeInFront(myShape)) {  
  190.                 // draw right of Hexagon  
  191.                 c.beginPath();  
  192.                 c.moveTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // front  
  193.                 c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // front  
  194.                 c.lineTo(myShape.bottomPlane.topRightX, myShape.bottomPlane.topRightY); // front  
  195.                 c.lineTo(myShape.bottomPlane.bottomRightX, myShape.bottomPlane.bottomRightY); // back  
  196.                 c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // back  
  197.                 c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // back  
  198.                 c.closePath();  
  199.    
  200.                 c.strokeStyle = "black";  
  201.                 c.lineWidth = lineWidth;  
  202.                 c.stroke();  
  203.                 c.fillStyle = "red";  
  204.                 c.fill();  
  205.             }  
  206.             else {  
  207.                 // draw left of Hexagon  
  208.                 c.beginPath();  
  209.                 c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // front  
  210.                 c.lineTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // front  
  211.                 c.lineTo(myShape.bottomPlane.topLeftX, myShape.bottomPlane.topLeftY); // front  
  212.                 c.lineTo(myShape.bottomPlane.bottomLeftX, myShape.bottomPlane.bottomLeftY); // back  
  213.                 c.lineTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // back  
  214.                 c.lineTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // back  
  215.                 c.closePath();  
  216.    
  217.                 c.strokeStyle = "black";  
  218.                 c.lineWidth = lineWidth;  
  219.                 c.stroke();  
  220.                 c.fillStyle = "red";  
  221.                 c.fill();  
  222.             }  
  223.    
  224.         }  
  225.    
  226.         function isRightSideOfShapeInFront(tankObj) {  
  227.             if (tankObj.topPlane.topRightY > tankObj.topPlane.topLeftY) {  
  228.                 return true;  
  229.             }  
  230.             else {  
  231.                 return false;  
  232.             }  
  233.         }  
  234.    
  235.         function updateScreen() {  
  236.             theta += .01; // radians  
  237.             clearCanvas();  
  238.             drawScreen();  
  239.             setTimeout("updateScreen()", 10);  
  240.         }  
  241.    
  242.         function handleMouseDown(e) {  
  243.             var mouseX = e.clientX;  
  244.             var mouseY = e.clientY;  
  245.         }  
  246.    
  247.         function handleMouseMove(e) {  
  248.             var mouseX = e.clientX;  
  249.             var mouseY = e.clientY;  
  250.         }  
  251.    
  252.         function handleMouseUp(e) {  
  253.    
  254.         }  
  255.    
  256.     </script>  
  257. </head>  
  258. <body onload="init()" onmousedown="return false;">  
  259.     <canvas id="myCanvas"></canvas>  
  260. </body>  
  261. </html> 
Output
 
As the Hexagon is rotating we get:
 
Image 1
 
3D.jpg
 
Image 2
 
3d2.jpg