I'm making a simple program using canvas where I have a sun, and there are three planets rotating around it. I managed to make the Sun and the planets. However, when I try to revolve the planets around the Sun, for some reason the planets do not revolve in a perfect circle.
Here's my code:
const canv = document.getElementById('canvas');
const ctx = canv.getContext('2d');
canv.width = window.innerWidth;
canv.height = window.innerHeight;
// Get the DPR and size of the canvas
var dpr = window.devicePixelRatio;
var rect = canvas.getBoundingClientRect();
// Set the "actual" size of the canvas
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
// Scale the context to ensure correct drawing operations
ctx.scale(dpr, dpr);
// Set the "drawn" size of the canvas
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
var w = canvas.width;
var h = canvas.height;
var circle = function(color, r) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(0, 0, r, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
}
const earth = new Image();
earth.src = 'earth.png';
mwidth= canvas.width/2;
mheight= canvas.height/2;
var i = 0;
function draw() {
ctx.save();
// paint bg
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
// set origin to center
ctx.translate(w / 2, h / 2);
// draw sun
circle('yellow', 20);
// rotate move along x
ctx.rotate(i / 100);
ctx.translate(100, 0);
// draw planet
circle('green', 10);
// rotate move along x
ctx.rotate(i / 100);
ctx.translate(150, 0);
// draw planet
circle('blue', 15);
// rotate move along x
ctx.rotate(i / 100);
ctx.translate(180, 0);
// draw planet
circle('red', 10);
ctx.restore();
i ;
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
//setInterval(draw, 1);
body {margin: 0; padding: 0}
<html>
<head>
<title>Our Universe</title>
</head>
<body>
<canvas id='canvas'>Sorry, your browser does not support this feature.</canvas>
</body>
</html>
You can see if you run the above snippet that the planets are revolving in a strange manner (they are not revolving in perfect circles.). Can someone explain why and help me correct this?
CodePudding user response:
If you want them to be perfect circles, there's nothing like trigonometry. :-)
To find the point on a circle, you use:
x = radius * cosine(angle);
y = radius * sine(angle);
...which gives you a value that is -radius
to radius
for each. You offset those by the center of the circle.
So for your example, you could use your game counter (i
) multiplied by some speed variable and calculate the center point of each of the three orbiting planets, like this:
const canv = document.getElementById("canvas");
const ctx = canv.getContext("2d");
canv.width = window.innerWidth;
canv.height = window.innerHeight;
// Get the DPR and size of the canvas
let dpr = window.devicePixelRatio;
let rect = canvas.getBoundingClientRect();
// Set the "actual" size of the canvas
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
// Scale the context to ensure correct drawing operations
ctx.scale(dpr, dpr);
// Set the "drawn" size of the canvas
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
let w = canvas.width;
let h = canvas.height;
let mwidth = w / 2;
let mheight = h / 2;
let circle = function (x, y, color, r) {
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
};
/* Not currently used
const earth = new Image();
earth.src = "earth.png";
*/
const circles = [
{color: "green", radius: w / 8, size: 10, speed: 0.02, },
{color: "red", radius: w / 6, size: 15, speed: 0.012, },
{color: "blue", radius: w / 4, size: 10, speed: 0.01, },
];
let i = 0;
function draw() {
ctx.save();
// paint bg
ctx.fillStyle = "black";
ctx.fillRect(0, 0, w, h);
// Draw sun
circle(mwidth, mheight, "yellow", 20);
// Draw circles
for (const { speed, radius, color, size } of circles) {
const angle = (i * speed) % 360;
const x = radius * Math.cos(angle);
const y = radius * Math.sin(angle);
circle(mwidth x, mheight y, color, size);
}
ctx.restore();
i ;
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
body {margin: 0; padding: 0}
<html>
<head>
<title>Our Universe</title>
</head>
<body>
<canvas id='canvas'>Sorry, your browser does not support this feature.</canvas>
</body>
</html>
I haven't used transform
in that, there didn't seem to be any need, but I'm relatively new to canvas programming so you might want to adjust the above to use that instead of explicit x
/y
coordinates.
Side note: I'm not sure the scaling code in there is correct, the canvas looks slightly distorted to me. But that wasn't the question. :-)
CodePudding user response:
From my perspective, the planets are rotating with respect to the last one. It's because you rotate and translate the whole ctx
after each planet is added. (See how the last example works here) It would work perfectly if you want to show how moons will orbit for instance.
A better implementation is by setting the trigonometric position of each planet.
const canv = document.getElementById('canvas');
const ctx = canv.getContext('2d');
canv.width = window.innerWidth;
canv.height = window.innerHeight;
// Get the DPR and size of the canvas
var dpr = window.devicePixelRatio;
var rect = canvas.getBoundingClientRect();
// Set the "actual" size of the canvas
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
// Scale the context to ensure correct drawing operations
ctx.scale(dpr, dpr);
// Set the "drawn" size of the canvas
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;
var w = canvas.width;
var h = canvas.height;
var circle = function(color, r, orbit, velocity, time) {
ctx.fillStyle = color;
ctx.beginPath();
x = Math.sin(-time * velocity / 100) * orbit
y = Math.cos(-time * velocity / 100) * orbit
ctx.arc(x, y, r, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
}
const earth = new Image();
earth.src = 'earth.png';
mwidth= canvas.width/2;
mheight= canvas.height/2;
var i = 0;
function draw() {
i ;
ctx.save();
// paint bg
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, w, h);
// set origin to center
ctx.translate(w / 2, h / 2);
// draw planets
circle('yellow', 20, 0, 0, i);
circle('green', 10, 50, 3, i);
circle('blue', 15, 120, 2, i);
circle('red', 10, 170, 4, i);
ctx.restore();
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
//setInterval(draw, 1);
body {margin: 0; padding: 0}
<html>
<head>
<title>Our Universe</title>
</head>
<body>
<canvas id='canvas'>Sorry, your browser does not support this feature.</canvas>
</body>
</html>