Can't rotate rectangle in canvas


Rajib

I am new to HTML5 canvas. I'm trying to build a game area using an HTML canvas, where someone can drive around a car inside the Canvas area, like if I press the left arrow key, the car will turn to the left, and if the right arrow key is pressed, the car will turn to the right. The up and down arrow keys are responsible for moving the car up or down. And the car must stay within the canvas area. Here I use the rotation method to turn the car right or left. But it doesn't work, actually the car is moving away from the canvas and behaving like crap. Anyone have an idea to solve the problem? I am totally stuck here. Thanks in advance.

Initially I was showing a rectangle instead of a car.

My js file is as follows.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

canvas.width = innerWidth - 100;
canvas.height = innerHeight - 100;
const canvasW = canvas.width;
const canvasH = canvas.height;

let upPressed = false,
  downPressed = false,
  rightPressed = false,
  leftPressed = false;

class PlayerCar {
  constructor(carX, carY, carWidth, carHeight) {
    this.carX = carX;
    this.carY = carY;
    this.carWidth = carWidth;
    this.carHeight = carHeight;
  }
  draw() {
    ctx.fillStyle = "blue";
    ctx.beginPath();
    ctx.rect(this.carX, this.carY, this.carWidth, this.carHeight);
    ctx.fill();
    ctx.closePath();
  }
}

const playerCar = new PlayerCar(100, 100, 40, 60);
playerCar.draw();

function navigation() {
  const handleKeyDown = (e) => {
    if (e.key === "ArrowUp") {
      upPressed = true;
    }
    if (e.key === "ArrowDown") {
      downPressed = true;
    }
    if (e.key === "ArrowRight") {
      rightPressed = true;
    }
    if (e.key === "ArrowLeft") {
      leftPressed = true;
    }
  };

  const handleKeyUp = (e) => {
    if (e.key === "ArrowUp") {
      upPressed = false;
    }
    if (e.key === "ArrowDown") {
      downPressed = false;
    }
    if (e.key === "ArrowRight") {
      rightPressed = false;
    }
    if (e.key === "ArrowLeft") {
      leftPressed = false;
    }
  };

  document.addEventListener("keydown", handleKeyDown);
  document.addEventListener("keyup", handleKeyUp);
}

function animate() {
  requestAnimationFrame(animate);
  ctx.clearRect(0, 0, canvasW, canvasH);
  if (upPressed) {
    playerCar.carY -= 5;
  }
  if (downPressed) {
    playerCar.carY += 5;
  }
  if (leftPressed) {
    ctx.save();
    ctx.translate(
      playerCar.carX + playerCar.width / 2,
      playerCar.carY + playerCar.height / 2
    );
    ctx.rotate((Math.PI / 180) * 0.2);
    playerCar.carX -= 5;
    ctx.restore();
  }
  if (rightPressed) {
    ctx.save();
    ctx.translate(
      playerCar.carX + playerCar.width / 2,
      playerCar.carY + playerCar.height / 2
    );
    ctx.rotate((Math.PI / 180) * -0.2);
    playerCar.carX += 5;
    ctx.restore();
  }

  if (playerCar.carX < 0) playerCar.carX = 0;
  if (playerCar.carX > canvasW - playerCar.carWidth)
    playerCar.carX = canvasW - playerCar.carWidth;
  if (playerCar.carY < 0) playerCar.carY = 0;
  if (playerCar.carY > canvasH - playerCar.carHeight)
    playerCar.carY = canvasH - playerCar.carHeight;

  playerCar.draw();
}

function startGame() {
  animate();
}

startGame();
navigation();
Justin

I first moved all the update information to a method in the car class, executing it in the animation loop.

This is costhe sinangle that comes in and becomes familiar. You also have to understand the canvas relationship, the object it draws is always at (x, y) at (0, 0) unless you pan it to the center. To do this, draw the object this way:

    ctx.fillStyle = "blue";
    ctx.save();
    ctx.beginPath();
    ctx.translate(this.carX, this.carY)
    ctx.rotate(this.angle)
    ctx.rect(-this.carWidth/2, -this.carHeight/2, this.carWidth, this.carHeight);
    ctx.fill();
    ctx.closePath();
    ctx.restore();

You must use save()and unless you want to translate and rotate the object back to its original state restore(). It's the same, but simpler. So now I pan the car around the canvas and the car itself is drawn at the negative half of the width and height to make sure the canvas (0, 0) corner is in the center of the car. This is because the canvas is always rotated from the top left corner.

Now create a method called update()and put your control logic there:

  update() {
    if (rightPressed) {
      this.angle += this.rot
    } else if (leftPressed) {
      this.angle -= this.rot
    }
    if (upPressed) {
      this.carX += Math.cos(this.angle+toRadians(-90)) * 5;
      this.carY += Math.sin(this.angle+toRadians(-90)) * 5;
    }
  }

Note that I also added angular and rot to the constructor. What this does is that when you press left or right, the car rotates accordingly. As for pressing, we'll translate it by rotating. Since this normally drives the car to the right, we also have to add -90 degrees to the current angle to make sure the car is going forward. Just delete +toRadians(-90)and see what happens.

Multiplying it by 5 is an arbitrary number for speed. You can even make it part of the constructor and set it there. IEthis.speed = 5

Do the same thing, downPressedbut instead use+toRadians(90)

toRadians()Just a simple function to add to the code to convert degrees to radians.

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

canvas.width = 600;
canvas.height = 600;
const canvasW = canvas.width;
const canvasH = canvas.height;

let upPressed = false,
  downPressed = false,
  rightPressed = false,
  leftPressed = false;

class PlayerCar {
  constructor(carX, carY, carWidth, carHeight) {
    this.carX = carX;
    this.carY = carY;
    this.carWidth = carWidth;
    this.carHeight = carHeight;
    this.angle = 0;
    this.rot = 0.1; //control how fast it turns
  }
  draw() {
    ctx.fillStyle = "blue";
    ctx.save();
    ctx.beginPath();
    ctx.translate(this.carX, this.carY)
    ctx.rotate(this.angle)
    ctx.rect(-this.carWidth/2, -this.carHeight/2, this.carWidth, this.carHeight);
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  }
  update() {
    if (rightPressed) {
      this.angle += this.rot
    } else if (leftPressed) {
      this.angle -= this.rot
    }
    if (upPressed) {
      this.carX += Math.cos(this.angle+toRadians(-90)) * 5;
      this.carY += Math.sin(this.angle+toRadians(-90)) * 5;
    }
    if (downPressed) {
      this.carX += Math.cos(this.angle+toRadians(90)) * 5;
      this.carY += Math.sin(this.angle+toRadians(90)) * 5;
    }
  }
}

function toRadians(deg) {
    return (deg * Math.PI) / 180;
}

const playerCar = new PlayerCar(100, 100, 40, 60);
playerCar.draw();

function navigation() {
  const handleKeyDown = (e) => {
    if (e.key === "ArrowUp") {
      upPressed = true;
    }
    if (e.key === "ArrowDown") {
      downPressed = true;
    }
    if (e.key === "ArrowRight") {
      rightPressed = true;
    }
    if (e.key === "ArrowLeft") {
      leftPressed = true;
    }
  };

  const handleKeyUp = (e) => {
    if (e.key === "ArrowUp") {
      upPressed = false;
    }
    if (e.key === "ArrowDown") {
      downPressed = false;
    }
    if (e.key === "ArrowRight") {
      rightPressed = false;
    }
    if (e.key === "ArrowLeft") {
      leftPressed = false;
    }
  };

  document.addEventListener("keydown", handleKeyDown);
  document.addEventListener("keyup", handleKeyUp);
}

function animate() {
  requestAnimationFrame(animate);
  ctx.clearRect(0, 0, canvasW, canvasH);
 
  if (playerCar.carX < 0) playerCar.carX = 0;
  if (playerCar.carX > canvasW - playerCar.carWidth)
    playerCar.carX = canvasW - playerCar.carWidth;
  if (playerCar.carY < 0) playerCar.carY = 0;
  if (playerCar.carY > canvasH - playerCar.carHeight)
    playerCar.carY = canvasH - playerCar.carHeight;

  playerCar.draw();
  playerCar.update();
}

function startGame() {
  animate();
}

startGame();
navigation();
<canvas></canvas>

To figure out why your code isn't doing what you expect, consider this. You are trying to transform and rotate the context without accessing the object. If you were to add ctx.fillRect()to your animation loop, would you think it would only know that you want to change the car? The same goes for translation and rotation. try adding an and in your rightPressedfillStylefillRect

if (rightPressed) {
    ctx.save();
    playerCar.carX += 5;
    ctx.translate(
      playerCar.carX + playerCar.width / 2,
      playerCar.carY + playerCar.height / 2
    );
    ctx.rotate(45);
    ctx.fillStyle = 'grey'
    ctx.fillRect(0, 0, canvas.width, canvas.height)
    ctx.restore();
  }

When you press rightcontext , you see what you want to do, not the object's context. That's why it should be added directly at the top of the class. Now you are specifically targeting the object you want.

Related


Can't rotate image inside canvas

username if (player1Ready) { ctx.save(); ctx.rotate(player1.rotation); ctx.drawImage(player1Image, player1.x, player1.y,80,80); ctx.restore(); } I'm trying to rotate a single image inside a canvas based on the angle the player is pressing

Can't rotate image inside canvas

username if (player1Ready) { ctx.save(); ctx.rotate(player1.rotation); ctx.drawImage(player1Image, player1.x, player1.y,80,80); ctx.restore(); } I'm trying to rotate a single image inside a canvas based on the angle the player is pressing

Can't rotate image inside canvas

username if (player1Ready) { ctx.save(); ctx.rotate(player1.rotation); ctx.drawImage(player1Image, player1.x, player1.y,80,80); ctx.restore(); } I'm trying to rotate a single image inside a canvas based on the angle the player is pressing

Can't rotate image inside canvas

username if (player1Ready) { ctx.save(); ctx.rotate(player1.rotation); ctx.drawImage(player1Image, player1.x, player1.y,80,80); ctx.restore(); } I'm trying to rotate a single image inside a canvas based on the angle the player is pressing

Can't rotate image inside canvas

username if (player1Ready) { ctx.save(); ctx.rotate(player1.rotation); ctx.drawImage(player1Image, player1.x, player1.y,80,80); ctx.restore(); } I'm trying to rotate a single image inside a canvas based on the angle the player is pressing

Rotate rectangle around circumference of circle on canvas

Mussmanson I'm trying to create a circular "equalizer" effect using JavaScript and HTML canvas for a small project I'm working on, and it works great, except for one small thing. It's just a series of rectangular bars moving to the mp3 over time, nothing fancy

Rotate rectangle around circumference of circle on canvas

Mussmanson I'm trying to create a circular "equalizer" effect using JavaScript and HTML canvas for a small project I'm working on, and it works great, except for one small thing. It's just a series of rectangular bars moving to the mp3 over time, nothing fancy

Rotate rectangle around circumference of circle on canvas

Mussmanson I'm trying to create a circular "equalizer" effect using JavaScript and HTML canvas for a small project I'm working on, and it works great, except for one small thing. It's just a series of rectangular bars moving to the mp3 over time, nothing fancy

Rotate rectangle around circumference of circle on canvas

Mussmanson I'm trying to create a circular "equalizer" effect using JavaScript and HTML canvas for a small project I'm working on, and it works great, except for one small thing. It's just a series of rectangular bars moving to the mp3 over time, nothing fancy

Why doesn't this rectangle rotate?

graphics That red rectangle should animate, but it's not. I found a typo, but still don't know what's wrong. http://jsfiddle.net/gcgtcwqv/1/ jQuery: jQuery('<div/>', { class: 'logo', text: 'Cake Baby' }).appendTo($("#cakeHook")); CSS: .logo {

Why doesn't this rectangle rotate?

graphics That red rectangle should animate, but it's not. I found a typo, but still don't know what's wrong. http://jsfiddle.net/gcgtcwqv/1/ jQuery: jQuery('<div/>', { class: 'logo', text: 'Cake Baby' }).appendTo($("#cakeHook")); CSS: .logo {

I can't draw rectangle using canvas as child component

Riddle I need to draw a rectangle using canvas. Canvas must be in child component. Just like the updateCanvas function. Is it possible to do so? I am trying to draw a rectangle in the parent component and everything works fine, but in this case I need to put t

I can't draw rectangle using canvas as child component

Riddle I need to draw a rectangle using canvas. Canvas must be in child component. Just like the updateCanvas function. Is it possible to do so? I am trying to draw a rectangle in the parent component and everything works fine, but in this case I need to put t