Uberche
Uberche

Reputation: 37

clearTimeout not stopping canvas animation

So I have a simple canvas animation I want to start and stop using a toggle switch. The animation works fine and starts up, but the clearTimeout does not seem to work.

I made sure the variable "timeOut" is defined globally so it should have access to it, that seemed to be the usual advice in past questions similar to this one. Am I make a mistake somewhere else? Tried it on Firefox, Chrome and Chromium.

let timeOut;

function circles() {
  let canvas = document.querySelector('canvas');
  canvas.width = document.querySelector('canvas').offsetWidth;
  canvas.height = document.querySelector('canvas').offsetHeight;
  let c = canvas.getContext('2d');
  let topRadius = 30;
  let colorArray = [
    '#ff0000',
    '#00ff00',
    '#0000ff',
  ]

  let mouse = {
    x: undefined,
    y: undefined
  }

  window.addEventListener('mousemove', function(e) {
    mouse.x = e.x;
    mouse.y = e.y;
  })

  window.addEventListener('resize', function() {
    canvas.width = document.querySelector('canvas').offsetWidth;
    canvas.height = document.querySelector('canvas').offsetHeight;
    init();
  });

  function Circle(x, y, dx, dy, radius) {
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dx;
    this.radius = radius;
    this.minRadius = radius;
    this.color = colorArray[Math.floor(Math.random() * colorArray.length)];

    this.draw = function() {
      if (this.radius < 0) {
        this.radius = this.minRadius;
      }
      c.beginPath();
      c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
      c.fillStyle = this.color;
      c.fill();
    }

    this.update = function() {
      if (this.y + this.radius > innerHeight || this.y - this.radius <= 0) {
        this.dy = -this.dy;
      }
      if (this.x + this.radius > canvas.width|| this.x - this.radius <= 0) {
        this.dx = -this.dx;
      }
      this.x+=this.dx;
      this.y+=this.dy;

      if (mouse.x - this.x < 50 && mouse.x - this.x > -50 && mouse.y - this.y < 50 && mouse.y - this.y > -50) {
        if (this.radius < topRadius) {
          this.radius += 1; 
        }
      } else if (this.radius > this.minRadius){
        this.radius -= 1;
      }

      this.draw();
    }
  }

  let circleArray = [];
  function init() {
    circleArray = [];
    for (let i =0; i<50; i++) {
      let radius = Math.random() * 10;
      let x = Math.random() * (canvas.width - radius*2) + radius;
      let y = Math.random() * (canvas.height - radius * 2) + radius;
      let dx = (Math.random() - 0.5) * 4;
      let dy = (Math.random() - 0.5 )* 4;
      circleArray.push(new Circle(x, y, dx, dy, radius));
    }
  }

  function animate() {
    requestAnimationFrame(animate);
    c.clearRect(0,0, canvas.width, innerHeight);

    for(let i=0; i < circleArray.length; i++) {
      circleArray[i].update();
    }
  }
  init();
  animate();
}

$('#startstop').click(function() {
  if(!timeOut) {
    timeOut = setTimeout(circles, 1000);
  } else {
    clearTimeout(timeOut);
    timeOut = null;
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="display: flex; justify-content: space-around;">
  <canvas style="width: 100px; height: 800px; background: red;"></canvas>

<div id="startstop">toggle</div>
</div>

Upvotes: 0

Views: 270

Answers (2)

Eugene Chernenko
Eugene Chernenko

Reputation: 74

You were super close to the solution ;)

in the function animate add line #3 (before for loop), that is having this value:

if (!timeOut) return;

Good luck!

Upvotes: 1

amprew
amprew

Reputation: 261

This animation is not stopping because you call clearTimeout on your setTimeout but that only runs once. Your issue is further into the animate function. This relatively loops the requestAnimationFrame. You can store this as a request and then cancelAnimationFrame

Upvotes: 1

Related Questions