3

I have a canvas demo that spawns some circles. I can't seem to figure out how to have it be responsive in setting either canvas.width or canvas.style.width (what's the difference here?) on a windows resize using window.innerWidth.

Code works fine but it rerenders strangely on smaller viewports. I tried adding this snippet of code at the bottom of my javascript file, but it broke animation.

  // ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
  if (window.innerWidth < 1000) {
    canvas.width = window.innerWidth;
   } else {
     canvas.width = 1000;
   }

enter image description here

var canvas = document.querySelector("canvas");

canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");

// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this.radius = radius;
  this.counter = counter;

  this.draw = function() {
    c.beginPath();
    c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
    c.strokeStyle = "white";
    c.stroke();
    c.fillStyle = "white";
    c.fill();
  };

  this.update = function() {
    if (this.y + this.radius > canvas.height) {
      this.y = 0;
    }
    this.x += this.dx;
    this.y += this.dy;

    this.draw();
  };
}

// Initialize array to store snow objects
var circleArray = [];

// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
  var radius = 1 + Math.random() * 5;
  var x = Math.random() * canvas.width;
  var y = 0 - Math.random() * 50; // start at top, render some circles off screen
  var dx = (Math.random() - 0.5) * 2;
  var dy = 0.5 + Math.random() * 0.5; // use gravity
  circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}

function animate() {
  requestAnimationFrame(animate); // recurisvely run
  c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content

  for (var i = 0; i < circleArray.length; i++) {
    circleArray[i].update();
  }
  // ADDING THESE LINES PREVENTS ANIMATION FROM RUNNING
  //if (window.innerWidth < 1000) {
  //  canvas.width = window.innerWidth;
  // } else {
    // canvas.width = 1000;
  // }
}
animate();
body {
  background-color: grey;
  display: flex;
  justify-content: center;
}
.wrapper {
  position: relative;
  width: 1000px;
  height: 110px;
  min-height: 110px;
  margin-top: 50vh;
}
.wrapper > * {
  width: 1000px;
  position: absolute;
}
canvas {
  position: absolute;
}
img {
  width: 100%;
  height: 110px;
}
<div class="wrapper">
  <img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
                          " alt="" />
  <img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
                          " alt="" /> 
  <img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
  <canvas></canvas>
</div>
Vincent Tang
  • 3,758
  • 6
  • 45
  • 63
  • related : https://stackoverflow.com/questions/53575931/why-box-sizing-is-not-working-with-width-height-attribute-on-canvas-element – Temani Afif Dec 25 '18 at 08:42

1 Answers1

3

The difference between canvas.width and canvas.style.width is that canvas.width specifies the actual size of the canvas in pixels, while canvas.style.width stretches or compresses the canvas to the width you specify. This will set the canvas width to 300px:

canvas.width = 300;

This will also set the canvas width to 300px, but will stretch it until it reaches 600px:

canvas.width = 300;
canvas.style.width = "600px";

Using canvas.width is better practice.


For the animation to run you must first fix the canvas width. Putting the code that resizes the canvas at the beginning of the animate() function solves the problem:

var canvas = document.querySelector("canvas");

canvas.width = 1000;
canvas.height = 100;
var c = canvas.getContext("2d");

// Constructor Function (object blueprint)
function Circle(x, y, dx, dy, radius, counter) {
  this.x = x;
  this.y = y;
  this.dx = dx;
  this.dy = dy;
  this.radius = radius;
  this.counter = counter;

  this.draw = function() {
    c.beginPath();
    c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
    c.strokeStyle = "white";
    c.stroke();
    c.fillStyle = "white";
    c.fill();
  };

  this.update = function() {
    if (this.y + this.radius > canvas.height) {
      this.y = 0;
    }
    this.x += this.dx;
    this.y += this.dy;

    this.draw();
  };
}

// Initialize array to store snow objects
var circleArray = [];

// Initialize objects with constructor
for (var i = 0; i < 50; i++) {
  var radius = 1 + Math.random() * 5;
  var x = Math.random() * canvas.width;
  var y = 0 - Math.random() * 50; // start at top, render some circles off screen
  var dx = (Math.random() - 0.5) * 2;
  var dy = 0.5 + Math.random() * 0.5; // use gravity
  circleArray.push(new Circle(x, y, dx, dy, radius, 0));
}

function animate() {
  if (window.innerWidth < 1000) {
    canvas.width = window.innerWidth;
  } else {
    canvas.width = 1000;
  }
  
  requestAnimationFrame(animate); // recurisvely run
  c.clearRect(0, 0, innerWidth, innerHeight); // erases previously drawn content

  for (var i = 0; i < circleArray.length; i++) {
    circleArray[i].update();
  }
}
animate();
body {
  background-color: grey;
  display: flex;
  justify-content: center;
}

.wrapper {
  position: relative;
  width: 1000px;
  height: 110px;
  min-height: 110px;
  margin-top: 50vh;
}

.wrapper>* {
  width: 1000px;
  position: absolute;
}

canvas {
  position: absolute;
}

img {
  width: 100%;
  height: 110px;
}
<div class="wrapper">
  <img class="stars" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/stars.png
                          " alt="" />
  <img class="tress" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/trees.png
                          " alt="" />
  <img class="clouds" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/867725/clouds.png" alt="" />
  <canvas></canvas>
</div>
Wais Kamal
  • 5,858
  • 2
  • 17
  • 36