JSFiddle - React, Tailwind, and code Playground

by Ernest P. Guagliata

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<div id="main">
  <div id="equation">
    <h2>Spirograph</h2>
    <p>\(x = (R - r)cos&hairsp;t + &rho;&hairsp;cos&hairsp;\frac{R-r}{r}t\)</p>
    <p>\(y = (R - r)sin&hairsp;t - &rho;&hairsp;sin&hairsp;\frac{R-r}{r}t\)</p>
    <p>\(R = 250\)</p>
    
    <p class="tooltip">\(r = \)&thinsp;
      <input id="r" type="text" size="5" value="90" />
      <span class="tooltiptext">required:&numsp;\(r&numsp;&lt;&numsp;R\)</span>
    </p>
      
    <p class="tooltip">\(&rho; =&numsp;\)&thinsp;
      <input id="p" type="text" size="5" value="40" />
      <span class="tooltiptext">required:&numsp;\(&rho;&numsp;&lt;&numsp;r\)</span>
    </p>
    
    <p class="tooltip">\(Color =&numsp;\)&thinsp;
      <input id="color" type="text" value="" size="5" />
      <span class="tooltiptext">leave blank for random color</span>
    </p>
    
  </div>
  <img src="http://portfolio.ernieblues.com/images/spirograph.png" /><br />
  <canvas id="myCanvas" width="500" height="500" style="border:1px solid #d3d3d3;">Your browser does not support the HTML5 canvas tag.</canvas><br />
  <button onclick="startDrawing();">Start Drawing</button>
  <button onclick="stopDrawing();">Stop Drawing</button>
  <button onclick="clearCanvas();">Clear Canvas</button>
</div>

CSS

#main {
  width: 600px;
  margin: auto;
  white-space: nowrap;
}

#equation {
  float: left;
  margin-right: 15px;
}

#fraction {
  font-size:  22px;
}

img {
  width: 320px;
  margin: 12px;
}

/*************** Tooltip ***************/
.tooltip {
    position: relative;
    display: block;
}

.tooltip .tooltiptext {
    visibility: hidden;
    width: auto;
    background-color: #66ccff;
    color: #fff;
    text-align: center;
    border-radius: 6px;
    padding: 1px 7px 2px 7px;
    position: absolute;
    z-index: 1;
    bottom: 125%;
    left: 0%;
    margin-left: 0%;
    opacity: 0;
    transition: opacity 0.3s;
}

.tooltip .tooltiptext::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 50%;
    margin-left: -5px;
    border-width: 5px;
    border-style: solid;
    border-color: #66ccff transparent transparent transparent;
}

.tooltip:hover .tooltiptext {
    visibility: visible;
    opacity: 1;
}

JavaScript

// Global Variables
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var x = 0;
var y = 0;
var R = c.width/2;
var r = 0;
var t = 0;
var p = 0;


// Start Drawing
function startDrawing() {
  t = 0;
  r = $("#r").val();
	p = $("#p").val();
  
  validate();
  
  // Create a random color
  var timesRun = 0;
  var color = '#' + $("#color").val();

  if (color == "#") {
    color = '#'+Math.floor(Math.random()*16777215).toString(16);
  }

  // Initial x and y
  x = R + (R-r)*Math.cos(t) + p*Math.cos(((R-r)/r)*t); // add R to set 0,0 in the middle of the canvas
  y = R + (R-r)*Math.sin(t) - p*Math.sin(((R-r)/r)*t); // add R to set 0,0 in the middle of the canvas

  // Start the Drawing
  ctx.beginPath();
  ctx.strokeStyle = color;
  ctx.moveTo(x,y);

  var xStart = x;
  var yStart = y;

  // Use the timer to create drawing
  interval = setInterval(function() {
    timesRun += 1;
    if(timesRun > 1 && x.toFixed(6) == xStart && y.toFixed(6) == yStart) {
      clearInterval(interval);
    }

    drawSpirograph();
  }, 20); 
    
}

// Draw Spirograph
function drawSpirograph() {
  t += Math.PI/50;
  x = R + (R-r)*Math.cos(t) + p*Math.cos(((R-r)/r)*t); // add R to set 0,0 in the middle of the canvas
  y = R + (R-r)*Math.sin(t) - p*Math.sin(((R-r)/r)*t); // add R to set 0,0 in the middle of the canvas

  ctx.lineTo(x,y);  
  ctx.stroke();
}

// Validate Input Fields
function validate() {
	if (+r >= +R) {
  	alert("r must be less than R");
    exit;
  }

  if (+p >= +r) {
  	alert("p must be less than r");
    exit;
  }
  
  var regex = /^[0-9a-fA-F]{6}$/;
  if (regex.test($(color).val()) == false && $(color).val() != "") {
  alert("color must be 6 digit hex number");
  exit;
  }
}

// Stop Drawing
function stopDrawing() {
	clearInterval(interval);
}

// Clear Canvas
function clearCanvas() {
	clearInterval(interval);
  ctx.clear();
}

CanvasRenderingContext2D.prototype.clear = 
  CanvasRenderingContext2D.prototype.clear || function...