Search code examples
javascriptcsscanvashtml5-canvastailwind-css

Create a graph showing at least 5 random numbers using the Canvas API


I have been working on this assignment for a week now and I am struggling. I didn't want to come here and bother most of you because honestly, you have more to help than some crybaby student, but I have tried so much myself before coming here. My form validation is working, but I can't figure out how to make a query.selector calls the user input number range to the graph attempting to be drawn in the canvas. I figured out how to get the numbers displayed on top of the bars, but can't get the dang words on the bottom of them for a description, as well as my bars, won't center. Every time I try and change anything it all just disappears. JavaScript is really challenging me and it's rather intimidating for a beginner. Linked is my Codepen.io that shows what I have finished which is most of it, i am using Tailwind.

CodePen

What we're supposed to attempt - Also we were told to put more "flare" on ours than this one.

document.querySelector("#sendbtn").addEventListener("click",onClick);

function onClick(e){
  console.log("click");
  const minnum = document.querySelector("#minnum");
  const maxnum = document.querySelector("#maxnum");
  if (minnum.checkValidity() && maxnum.checkValidity()){
    e.preventDefault();
    processForm();
  }
}

function processForm(){
  console.log("Form is Valid!")
}

(()=>{
  const canvas = document.querySelector("canvas");
  const ctx = canvas.getContext("2d");
  ctx.beginPath();
  ctx.font = "14 Helvetica";
  for (let i = 0; i<5; i++) {
    console.log(i);
    let height = Math.floor() * (80 - 0) + 1;
    ctx.fillStyle = "#00FF00";
    ctx.fillRect(i * 50 + 10, 250 - height*3, 20, height*3);
    ctx.fillStyle = "#000000";
    ctx.fillText(height, i * 50 + 14, 248 - height*3)
  }
})();
#canvas {
    border: 2px solid rgb(2, 26, 247); background-color: gray;
    height: 100%;
    width: 100%;
}
<h1 class="bg-blue-600 text-center p-8 text-blue-200 font-bold text-2xl">JavaScript Code Demo - Canvas </h1>
<div class="bg-blue-600 min-h-screen flex items-center text-lg">
   <form action="/" class="p-10 md:w-2/3 lg:w-1/2 mx-auto rounded">
     <div class="shadow">
       <div class="flex items-center bg-blue-400 rounded-t-lg border-blue-500 border-b">
         <label for="minnum" class="w-20 text-right mr-8 p-4 text-blue-200">Min Number</label>
         <input type="number" name="minnum" id="minnum" tabindex="1" required autofocus min="0" placeholder="0" step="1" class="flex-1 p-4 pl-0 bg-transparent placeholder-blue-200               outline-none text-white overflow-ellipsis overflow-hidden">
       </div>
       <div class="flex items-center bg-blue-400  rounded-b-lg border-blue-500 mb-10">
         <label for="maxnum" class="w-20 text-right p-4 mr-8 text-blue-200">Max Number</label>
         <input type="number" name="maxnum" id="maxnum" placeholder="100" required tabindex="2" max="100" step="1" class="flex-1 p-4 pl-0 bg-transparent placeholder-blue-200 outline-           none text-white overflow-ellipsis overflow-hidden">
       </div>
     </div>
     <button type="submit" id="sendbtn" class="bg-green-400 block w-full rounded py-4 text-white font-bold shadow">Submit</button>
   </form>
 </div>
<canvas id="canvas" height="300" width="400"></canvas>


Solution

  • You're not alone. JavaScript can be tough at first, but it looks like you put in some good effort. Keep it up and you'll get the hang of it. Here is a working version of what you are trying to accomplish. I worked off of the Codepen link you provided. The code I am providing can be saved as an HTML file or you can break out the CSS,JS, and HTML into your Codepen project.

    1. Since the browser is handling the validation based on the input properties, I consolidated the event listener functionality. So the button click will call processForm() directly, instead of OnClick(). If you require an extra step there, you can always break it out again.
    2. I set the canvas CSS style to match the element width and height properties in the canvas tag, but the sizing of the chart is generated dynamically, so you should be able to tweak that without too much trouble. Just note that changing the css styles to 100% will stretch the canvas, causing the chart to look pixelated/distorted. There are ways to make it responsive and full width, just look up responsive canvas or something along those lines. I also removed the "flex" class from the blue container and put the canvas inside it, but feel free to revert those changes if you want.
    3. I added some comments to help walk through the logic, but if you have any other questions, I'll try my best to help.
    4. If you want to go above and beyond in terms of style, I would look at using rgba() values and/or maybe some gradient fills on the chart. w3schools has some pretty easy tutorials with a sandbox to play around with different ideas if you want.
    5. I think that covers most of it, but if I missed something, let me know.

    Good luck with your studies and cheers!

    <!DOCTYPE HTML>
    <html lang="en">
    <head>
    <title>Javascript Code Demo - Canvas</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css">
    <style>
    #canvas {
        border: 2px solid rgb(2, 26, 247); background-color: gray;
        height: 300px;
        width: 400px;
        margin:0 auto;
    }
    </style>
    </head>
    <body>
    <h1 class="bg-blue-600 text-center p-8 text-blue-200 font-bold text-2xl">JavaScript Code Demo - Canvas </h1>
    <div class="bg-blue-600 min-h-screen items-center text-lg">
       <form action="/" class="p-10 md:w-2/3 lg:w-1/2 mx-auto rounded">
         <div class="shadow">
           <div class="flex items-center bg-blue-400 rounded-t-lg border-blue-500 border-b">
             <label for="minnum" class="w-20 text-right mr-8 p-4 text-blue-200">Min Number</label>
             <input type="number" name="minnum" id="minnum" tabindex="1" required autofocus min="0" placeholder="0" step="1" class="flex-1 p-4 pl-0 bg-transparent placeholder-blue-200               outline-none text-white overflow-ellipsis overflow-hidden">
           </div>
           <div class="flex items-center bg-blue-400  rounded-b-lg border-blue-500 mb-10">
             <label for="maxnum" class="w-20 text-right p-4 mr-8 text-blue-200">Max Number</label>
             <input type="number" name="maxnum" id="maxnum" placeholder="100" required tabindex="2" max="100" step="1" class="flex-1 p-4 pl-0 bg-transparent placeholder-blue-200 outline-           none text-white overflow-ellipsis overflow-hidden">
           </div>
         </div>
         <button type="submit" id="sendbtn" class="bg-green-400 block w-full rounded py-4 text-white font-bold shadow">Submit</button>
       </form>
       <canvas id="canvas" height="300" width="400"></canvas>
     </div>
    <script>
    document.querySelector("#sendbtn").addEventListener("click",processForm);
    
    function processForm(e){
      e.preventDefault();
      //get min and max values
      const min = +document.querySelector("#minnum").value;
      const max = +document.querySelector("#maxnum").value;
      //get canvas
      const canvas = document.querySelector("canvas");
      const ctx = canvas.getContext("2d");
      ctx.font = "14 Helvetica";
      //reset canvas with background overlay
      ctx.fillStyle = "gray";
      ctx.fillRect(0, 0,canvas.width, canvas.height);
      //calculate bar width and position intervals based on canvas width and total bars
      const xInt = Math.floor(canvas.width/5);
      const rectWidth = xInt-20;
    
      for (let i = 0; i<5; i++) {
        //generate a random height between min & max values
        let height = Math.floor((Math.random() * (max - min + 1)) + min);
        //scale the height of the bars based on canvas height and enough margin to allow room for chart title
        let hScaled = (height*(canvas.height/125));
    
        //draw the bar
        ctx.fillStyle = "#00FF00";
        ctx.fillRect((i*xInt)+10, canvas.height-hScaled-20,rectWidth, hScaled);
        ctx.fillStyle = "#000000";
        ctx.fillText("Test Grades", (canvas.width/2)-20 , 20);
        //calculate x axis position
        let xPos = (i*xInt)+5;
        //y axis label
        ctx.fillText(height, Math.round(xPos+(rectWidth/2)) , canvas.height-hScaled-26);
        //x axis label
        ctx.fillText("Grade "+(i+1), Math.round(xPos+(rectWidth/4)) , canvas.height-10)
      }
    }
    </script>
    </body>
    </html>