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. c.lineJoin = "round";
  8. c.strokeStyle = "black";
  9. c.lineWidth = lineWidth;
  10. c.stroke();
  11. c.fillStyle = "blue";
  12. 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. c.lineJoin = "round";
  8. c.strokeStyle = "black";
  9. c.lineWidth = lineWidth;
  10. c.stroke();
  11. c.fillStyle = "blue";
  12. 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. c.lineJoin = "round";
  8. c.strokeStyle = "black";
  9. c.lineWidth = lineWidth;
  10. c.stroke();
  11. c.fillStyle = "green";
  12. 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. c.lineJoin = "round";
  8. c.strokeStyle = "black";
  9. c.lineWidth = lineWidth;
  10. c.stroke();
  11. c.fillStyle = "blue";
  12. 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. c.strokeStyle = "black";
  8. c.lineWidth = lineWidth;
  9. c.stroke();
  10. c.fillStyle = "green";
  11. 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. this.rotate = function (newTheta)
  7. {
  8. topPlane.rotate(newTheta);
  9. centerPlane.rotate(newTheta);
  10. bottomPlane.rotate(newTheta);
  11. }
  12. this.generatePlanes = function ()
  13. {
  14. topPlane.generate();
  15. centerPlane.generate();
  16. bottomPlane.generate();
  17. }
  18. }
Example:
  1. <!DOCTYPE html>
  2. <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>ThreeD HTML5</title>
  6. <style>
  7. body {
  8. margin: 0px;
  9. padding: 0px;
  10. }
  11. #myCanvas
  12. {
  13. border: 1px solid black;
  14. }
  15. </style>
  16. <script>
  17. var canvas = null;
  18. var c = null;
  19. var canvasWidth = 576;
  20. var canvasHeight = 400;
  21. var t = 0;
  22. var myShape = null;
  23. function Shape(topPlane, centerPlane, bottomPlane) {
  24. this.topPlane = topPlane;
  25. this.centerPlane = centerPlane;
  26. this.bottomPlane = bottomPlane;
  27. this.rotate = function (newTheta) {
  28. topPlane.rotate(newTheta);
  29. centerPlane.rotate(newTheta);
  30. bottomPlane.rotate(newTheta);
  31. }
  32. this.generatePlanes = function () {
  33. topPlane.generate();
  34. centerPlane.generate();
  35. bottomPlane.generate();
  36. }
  37. }
  38. function Plane(centerX, centerY, planeLength, planeWidth, planeTilt, planeTheta) {
  39. this.centerX = centerX;
  40. this.centerY = centerY;
  41. this.planeLength = planeLength;
  42. this.planeTheta = planeTheta;
  43. var lastPerspectiveX = null;
  44. var lastPerspectiveX2 = null;
  45. var planeNextCornerAngle = 2 * Math.asin(planeWidth / planeLength);
  46. this.rotate = function (newTheta) {
  47. planeTheta = newTheta - planeNextCornerAngle / 2;
  48. }
  49. this.translate = function (newCenterX, newCenterY) {
  50. centerX = newCenterX;
  51. centerY = newCenterY;
  52. }
  53. this.generate = function () {
  54. var ovalLength = planeLength;
  55. var ovalWidth = ovalLength * planeTilt;
  56. var perspectiveX = (ovalLength / 2) * Math.cos(planeTheta);
  57. var perspectiveY = (ovalWidth / 2) * Math.sin(planeTheta);
  58. var perspectiveX2 = (ovalLength / 2) * Math.cos(planeTheta + planeNextCornerAngle);
  59. var perspectiveY2 = (ovalWidth / 2) * Math.sin(planeTheta + planeNextCornerAngle);
  60. this.topLeftX = (perspectiveX * 1) + centerX;
  61. this.topLeftY = (perspectiveY * -1) + centerY;
  62. this.bottomRightX = (perspectiveX * -1) + centerX;
  63. this.bottomRightY = (perspectiveY * 1) + centerY
  64. this.topRightX = (perspectiveX2 * 1) + centerX;
  65. this.topRightY = (perspectiveY2 * -1) + centerY;
  66. this.bottomLeftX = (perspectiveX2 * -1) + centerX;
  67. this.bottomLeftY = (perspectiveY2 * 1) + centerY;
  68. }
  69. }
  70. function clearCanvas() {
  71. canvas.width = 1;
  72. canvas.width = canvasWidth;
  73. }
  74. function init() {
  75. canvas = document.getElementById("myCanvas");
  76. c = canvas.getContext("2d");
  77. canvas.width = canvasWidth;
  78. canvas.height = canvasHeight;
  79. var topPlane = new Plane(288, 150, 100, 70, 0.5, theta);
  80. var centerPlane = new Plane(288, 200, 600, 70, 0.5, theta);
  81. var bottomPlane = new Plane(288, 250, 100, 70, 0.5, theta);
  82. myShape = new Shape(topPlane, centerPlane, bottomPlane);
  83. //drawScreen();
  84. updateScreen();
  85. }
  86. var theta = 0; // top left
  87. function drawScreen() {
  88. var lineWidth = 2;
  89. myShape.rotate(theta);
  90. //myPlane.translate(tankX, 100);
  91. myShape.generatePlanes();
  92. // draw front bottom of Hexagon
  93. c.beginPath();
  94. c.moveTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // top left
  95. c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // top right
  96. c.lineTo(myShape.bottomPlane.topRightX, myShape.bottomPlane.topRightY); // bottom right
  97. c.lineTo(myShape.bottomPlane.topLeftX, myShape.bottomPlane.topLeftY); // bottom left
  98. c.closePath();
  99. c.lineJoin = "round";
  100. c.strokeStyle = "black";
  101. c.lineWidth = lineWidth;
  102. c.stroke();
  103. c.fillStyle = "blue";
  104. c.fill();
  105. // draw back bottom of Hexagon
  106. c.beginPath();
  107. c.moveTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // top left
  108. c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // top right
  109. c.lineTo(myShape.bottomPlane.bottomRightX, myShape.bottomPlane.bottomRightY); // bottom right
  110. c.lineTo(myShape.bottomPlane.bottomLeftX, myShape.bottomPlane.bottomLeftY); // bottom left
  111. c.closePath();
  112. c.lineJoin = "round";
  113. c.strokeStyle = "black";
  114. c.lineWidth = lineWidth;
  115. c.stroke();
  116. c.fillStyle = "blue";
  117. c.fill();
  118. // draw front top of Hexagon
  119. c.beginPath();
  120. c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left
  121. c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right
  122. c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // bottom right
  123. c.lineTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // bottom left
  124. c.closePath();
  125. c.lineJoin = "round";
  126. c.strokeStyle = "black";
  127. c.lineWidth = lineWidth;
  128. c.stroke();
  129. c.fillStyle = "green";
  130. c.fill();
  131. // draw back top of Hexagon
  132. c.beginPath();
  133. c.moveTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // top left
  134. c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // top right
  135. c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // bottom right
  136. c.lineTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // bottom left
  137. c.closePath();
  138. c.lineJoin = "round";
  139. c.strokeStyle = "black";
  140. c.lineWidth = lineWidth;
  141. c.stroke();
  142. c.fillStyle = "blue";
  143. c.fill();
  144. // draw top of Hexagon
  145. c.beginPath();
  146. c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // top left
  147. c.lineTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // top right
  148. c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // bottom right
  149. c.lineTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // bottom left
  150. c.closePath();
  151. c.strokeStyle = "black";
  152. c.lineWidth = lineWidth;
  153. c.stroke();
  154. c.fillStyle = "green";
  155. c.fill();
  156. if (isRightSideOfShapeInFront(myShape)) {
  157. // draw right of Hexagon
  158. c.beginPath();
  159. c.moveTo(myShape.topPlane.topRightX, myShape.topPlane.topRightY); // front
  160. c.lineTo(myShape.centerPlane.topRightX, myShape.centerPlane.topRightY); // front
  161. c.lineTo(myShape.bottomPlane.topRightX, myShape.bottomPlane.topRightY); // front
  162. c.lineTo(myShape.bottomPlane.bottomRightX, myShape.bottomPlane.bottomRightY); // back
  163. c.lineTo(myShape.centerPlane.bottomRightX, myShape.centerPlane.bottomRightY); // back
  164. c.lineTo(myShape.topPlane.bottomRightX, myShape.topPlane.bottomRightY); // back
  165. c.closePath();
  166. c.strokeStyle = "black";
  167. c.lineWidth = lineWidth;
  168. c.stroke();
  169. c.fillStyle = "red";
  170. c.fill();
  171. }
  172. else {
  173. // draw left of Hexagon
  174. c.beginPath();
  175. c.moveTo(myShape.topPlane.topLeftX, myShape.topPlane.topLeftY); // front
  176. c.lineTo(myShape.centerPlane.topLeftX, myShape.centerPlane.topLeftY); // front
  177. c.lineTo(myShape.bottomPlane.topLeftX, myShape.bottomPlane.topLeftY); // front
  178. c.lineTo(myShape.bottomPlane.bottomLeftX, myShape.bottomPlane.bottomLeftY); // back
  179. c.lineTo(myShape.centerPlane.bottomLeftX, myShape.centerPlane.bottomLeftY); // back
  180. c.lineTo(myShape.topPlane.bottomLeftX, myShape.topPlane.bottomLeftY); // back
  181. c.closePath();
  182. c.strokeStyle = "black";
  183. c.lineWidth = lineWidth;
  184. c.stroke();
  185. c.fillStyle = "red";
  186. c.fill();
  187. }
  188. }
  189. function isRightSideOfShapeInFront(tankObj) {
  190. if (tankObj.topPlane.topRightY > tankObj.topPlane.topLeftY) {
  191. return true;
  192. }
  193. else {
  194. return false;
  195. }
  196. }
  197. function updateScreen() {
  198. theta += .01; // radians
  199. clearCanvas();
  200. drawScreen();
  201. setTimeout("updateScreen()", 10);
  202. }
  203. function handleMouseDown(e) {
  204. var mouseX = e.clientX;
  205. var mouseY = e.clientY;
  206. }
  207. function handleMouseMove(e) {
  208. var mouseX = e.clientX;
  209. var mouseY = e.clientY;
  210. }
  211. function handleMouseUp(e) {
  212. }
  213. </script>
  214. </head>
  215. <body onload="init()" onmousedown="return false;">
  216. <canvas id="myCanvas"></canvas>
  217. </body>
  218. </html>
Output
As the Hexagon is rotating we get:
Image 1
3D.jpg
Image 2
3d2.jpg