Search code examples
javascriptanimationcanvassetinterval

How to make an animation to draw a triangle every second on canvas in javascript?


I am learning javascript on the site mdn Drawing graphics and its first example draws a lot of triangles on canvas when open the web

here's the original code the site provided:

const canvas = document.querySelector('.myCanvas');
const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'rgb(0,0,0)';
ctx.fillRect(0,0,width,height);

ctx.translate(width/2, height/2);

function degToRad(degrees) {
  return degrees * Math.PI / 180;
};

function rand(min, max) {
  return Math.floor(Math.random() * (max-min+1)) + (min);
}

let length = 250;
let moveOffset = 20;

for (let i = 0; i < length; i++) {
  ctx.fillStyle = `rgba(${255-length},0,${255-length},0.9)`;
  ctx.beginPath();
  ctx.moveTo(moveOffset,moveOffset);
  ctx.lineTo(moveOffset+length,moveOffset);
  const triHeight = length/2 * Math.tan(degToRad(60));
  ctx.lineTo(moveOffset+(length/2),moveOffset+triHeight);
  ctx.lineTo(moveOffset,moveOffset);
  ctx.fill();

  length--;
  moveOffset+=0.7;
  ctx.rotate(degToRad(5));
}

and I want to modify the code to make it draw one triangle every second. and Here's my code:

const canvas = document.querySelector(".myCanvas");
const width = (canvas.width = window.innerWidth);
const height = (canvas.height = window.innerHeight);
const ctx = canvas.getContext("2d");

ctx.fillStyle = "rgb(0, 0, 0)";
ctx.fillRect(0, 0, width, height);

function degToRad(degrees) {
    return (degrees * Math.PI) / 180;
}
  
let length = 250;
let moveOffset = 20;


let i=0;
let ani=0;
function animation(){
    if(!ani){
        ani=setInterval(draw,1000);
    }
}
function draw(){
    ctx.fillStyle = `rgba(${255 - length},0,${255 - length},0.9)`;
    ctx.beginPath();
    ctx.moveTo(moveOffset, moveOffset);
    ctx.lineTo(moveOffset + length, moveOffset);
    const triHeight = (length / 2) * Math.tan(degToRad(60));
    ctx.lineTo(moveOffset + length / 2, moveOffset + triHeight);
    ctx.lineTo(moveOffset, moveOffset);
    ctx.fill();

    length--;
    moveOffset += 0.7;
    ctx.rotate(degToRad(5));
    console.log(i);
    i++;
}
animation();

the code can console "i(second)" every second, but there's nothing(except the black canvas) on the canvas.

But when I try the window.requestAnimationFrame(draw) method, the triangle don't appear one every second. How can I fix this code to make it work as I wanted? It's not necessary to use setInterval, I am free to use any method that works!


Solution

  • The requestAnimationFrame function is some sort of setTimeout that syncs with the browser frame rate. Here's a simple animation loop with all you need to know to make this work (see code comments). This code will move the square every 1 second (approximatively).

    const ctx = document.getElementById("canvas").getContext("2d");
    
    const canvasW = 500;
    const canvasH = 500;
    
    ctx.canvas.width = canvasW;
    ctx.canvas.height = canvasH;
    
    let timer = undefined;
    
    const rectW = 50;
    const rectH = 50;
    const rectColor = "red";
    
    let x = 0;
    let y = 0;
    
    function drawSquare(x, y) {
      ctx.fillStyle = rectColor;
      ctx.fillRect(x, y, rectW, rectH);
    }
    
    function draw() {
    
      // Initial state
      if (timer === undefined) {
        timer = Date.now();
        drawSquare(x, y);
      }
      
      // State of the animation for subsequent frames
      if ((Date.now() - timer) >= 1000) {
        // We use >= 1000 (1sec) because the precision can be an issue
        timer = Date.now();
        x += 10;
        y += 10;
        
        // important: clear your canvas before drawing next frame
        ctx.clearRect(0, 0, 500, 500);
    
        // Drawing
        drawSquare(x, y);
      }
    
      // Draw next frame if condition is truthy
      if (x < canvasW - rectW) {
        // Loop using recursion
        requestAnimationFrame(draw);
      }
      
    }
    
    // Start animation loop and draw first frame
    requestAnimationFrame(draw);
    <canvas id="canvas" style="border: solid 1px black;"></canvas>