无法在 canvas 中旋转矩形
can not rotate a rectangular shape in canvas
我是 HTML5 canvas 的新人。我正在尝试使用 HTML canvas 建立游戏区,让人们可以在里面开车
Canvas 区域,如果我按向左箭头键,汽车将向左转,如果按向右箭头键,汽车将向右侧转。向上和向下箭头键负责向上或向下移动汽车。并且车子必须在canvas范围内。在这里,我使用 rotate 方法将汽车向右或向左转。但它不起作用,实际上汽车正在远离 canvas 并且表现得像个废话。
任何人有解决问题的想法吗?我完全被困在这里。提前致谢。
最初我显示的是矩形而不是汽车。
我的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();
我会首先将所有更新信息移动到汽车中的一个方法class副在动画循环中执行它。
这是 cos
和 sin
发挥作用并熟悉角度的地方。您还必须了解 canvas 关系,它绘制的对象始终位于 (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()
,除非您想将对象平移并旋转回其原始状态。它是相同的,但更简单。所以现在我在 canvas 周围平移汽车,汽车本身绘制在宽度和高度的负一半处,以确保 canvas (0, 0) 角位于汽车的中心。这是因为 canvas 总是从左上角开始旋转。
现在创建一个名为 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();
<canvas></canvas>
要清楚为什么您的代码没有按照您的预期进行思考。您试图在不访问对象的情况下平移和旋转上下文。如果您要将 ctx.fillRect()
添加到您的动画循环中,您会认为它只会知道您想要更换汽车吗?平移和旋转也是如此。尝试在您的 rightPressed
中添加 fillStyle
和 fillRect
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
时,您会看到上下文执行您想要的操作,而不是对象的上下文。这就是为什么应该将它直接添加到 class 顶部。现在您正在专门针对您想要的对象。
我是 HTML5 canvas 的新人。我正在尝试使用 HTML canvas 建立游戏区,让人们可以在里面开车 Canvas 区域,如果我按向左箭头键,汽车将向左转,如果按向右箭头键,汽车将向右侧转。向上和向下箭头键负责向上或向下移动汽车。并且车子必须在canvas范围内。在这里,我使用 rotate 方法将汽车向右或向左转。但它不起作用,实际上汽车正在远离 canvas 并且表现得像个废话。 任何人有解决问题的想法吗?我完全被困在这里。提前致谢。
最初我显示的是矩形而不是汽车。
我的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();
我会首先将所有更新信息移动到汽车中的一个方法class副在动画循环中执行它。
这是 cos
和 sin
发挥作用并熟悉角度的地方。您还必须了解 canvas 关系,它绘制的对象始终位于 (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()
,除非您想将对象平移并旋转回其原始状态。它是相同的,但更简单。所以现在我在 canvas 周围平移汽车,汽车本身绘制在宽度和高度的负一半处,以确保 canvas (0, 0) 角位于汽车的中心。这是因为 canvas 总是从左上角开始旋转。
现在创建一个名为 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();
<canvas></canvas>
要清楚为什么您的代码没有按照您的预期进行思考。您试图在不访问对象的情况下平移和旋转上下文。如果您要将 ctx.fillRect()
添加到您的动画循环中,您会认为它只会知道您想要更换汽车吗?平移和旋转也是如此。尝试在您的 rightPressed
fillStyle
和 fillRect
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
时,您会看到上下文执行您想要的操作,而不是对象的上下文。这就是为什么应该将它直接添加到 class 顶部。现在您正在专门针对您想要的对象。