【问题标题】:can not rotate a rectangular shape in canvas无法在画布中旋转矩形
【发布时间】:2021-10-07 23:28:17
【问题描述】:

我是 HTML5 画布的新手。我正在尝试使用 HTML 画布构建游戏区域,有人可以在其中驾驶汽车 画布区域,例如,如果我按左箭头键,汽车将向左转,如果按右箭头键,汽车将向右转。上下方向键负责上下移动汽车。并且汽车必须停留在画布区域内。在这里,我使用旋转方法将汽车向右或向左转动。但它不起作用,实际上汽车正在远离画布并且表现得像个废话。 任何人有任何想法来解决这个问题?我完全被困在这里。提前致谢。

最初我显示的是一个矩形而不是汽车。

我的js文件如下。

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();

【问题讨论】:

  • 你需要一个标准化的汽车方向向量。当按下左或右时,您使用 2D 旋转矩阵旋转该向量(然后再次对其进行归一化)。当按下 up 时,您将此向量添加到汽车的位置。当按下向下时,你减去它。

标签: javascript html canvas


【解决方案1】:

我会首先将所有更新信息移动到汽车类中的一个方法中,在动画循环中执行它。

这就是cossin 进来熟悉角度的地方。您还必须了解画布关系,它绘制的对象始终位于 (0, 0) 的 (x, y) 处,除非您将其转换为中心。为此,请以这种方式绘制您的对象:

    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();

save()restore() 的使用是必须的,除非您想将对象转换并旋转回其原始状态。它是相同的,但更简单。所以现在我在画布周围平移汽车,汽车本身以宽度和高度的负一半绘制,以确保画布 (0, 0) 角位于汽车的中心。这是因为画布总是从左上角旋转。

现在创建一个名为 update() 的方法并将控制逻辑放入其中:

  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;
    }
  }

请注意,我还向构造函数添加了角度和腐烂。这是做什么的,当你按下左或右时,汽车会相应地旋转。至于按下,我们将通过旋转来翻译它。由于这通常会使汽车向右行驶,因此我们还必须在当前角度上增加 -90 度以确保汽车向前行驶。只需删除 +toRadians(-90) 看看会发生什么。

将它乘以 5 是速度的任意数字。您甚至可以将其作为构造函数的一部分并将其设置在那里。即this.speed = 5

downPressed 做同样的事情,但改用+toRadians(90)

toRadians() 只是添加到代码中的一个简单函数,用于将度数转换为弧度。

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();
&lt;canvas&gt;&lt;/canvas&gt;

要清楚为什么您的代码没有按照您的预期行事。您试图在不访问对象的情况下翻译和旋转上下文。如果您将ctx.fillRect() 添加到您的动画循环中,您会认为它只会知道您想要更换汽车吗?平移和旋转也是如此。尝试将fillStylefillRect 添加到您的右侧按下

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();
  }

当您按下right 时,您会看到上下文执行您想要的操作,而不是对象的上下文。这就是为什么应该将它直接添加到类之上的原因。现在您正在专门针对您想要的对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2019-03-07
    • 1970-01-01
    • 2017-12-15
    • 2016-07-20
    • 1970-01-01
    • 2016-08-05
    相关资源
    最近更新 更多