I'm trying to simulate gravitational pull/acceleration in the following animation. earth
's angular velocity should increase as it gets closer to sol
and decrease as it gets far. I think I'll need an easing function to modify the earth.angularVelocity
but no idea how.
I don't know if already defined easing functions or a custom one would work. The easing function I need should work like in this graph:
earth
's perihelion is at 180°, and aphelion is at 0/360°. How can I create such a function and make this work?
function pullRelease(angularPosition, begin, change, maxVelocity) {
// ?
}
earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 3);
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var sol = {
x: 125,
y: 150,
r: 30,
fill: "gold",
};
var orbit = {
x: 200,
y: 150,
semiMajor: 150,
semiMinor: 75,
};
var earth = {
r: 15,
fill: "dodgerblue",
angularPosition: 0,
angularVelocity: 1,
};
// draw sun
context.beginPath();
context.arc(sol.x, sol.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = sol.fill;
context.fill();
// draw fake sun to mark the orbit center
context.beginPath();
context.arc(orbit.x, orbit.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = "rgba(255,215,0,.1)";
context.fill();
// draw earth's orbit path
context.beginPath();
context.ellipse(orbit.x, orbit.y, orbit.semiMajor, orbit.semiMinor, 0, 0, 2 * Math.PI);
context.stroke();
// these are fixed, so save them as background-image
canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";
function draw() {
context.resetTransform();
context.clearRect(0, 0, canvas.width, canvas.height);
var newPosition = rotate(-earth.angularPosition, orbit.semiMajor, orbit.semiMinor, orbit.x, orbit.y);
earth.x = newPosition.x;
earth.y = newPosition.y;
// earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 3);
earth.angularPosition += earth.angularVelocity;
if (earth.angularPosition >= 360) {
earth.angularPosition = 0;
}
position.innerHTML = earth.angularPosition + "°";
context.translate(earth.x, earth.y);
context.beginPath();
context.arc(0, 0, earth.r, 0, 360 * Math.PI / 180);
context.closePath();
context.fillStyle = earth.fill;
context.fill();
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
function rotate(angle, distanceX, distanceY, originX, originY) {
return {
x: originX + Math.cos(angle * Math.PI / 180) * distanceX,
y: originY + Math.sin(angle * Math.PI / 180) * distanceY,
}
}
body {
background: gainsboro;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .1);
}
#position {
display: inline-block;
width: 35px;
text-align: right;
}
<canvas id="canvas" class="box" width="400" height="300"></canvas>
<p>Position: <span id="position">0</span></p>
After some thinking, I've came up with a function which seems to work, but still I don't think the effect feels natural. Function produces the graph at left. An ideal one would be the one at right. So the function still needs some work.
function pullRelease(angularPosition, begin, change, minVelocity, maxVelocity) {
var midWay = Math.floor(change/2);
if (angularPosition >= begin && angularPosition < midWay) {
var percent = angularPosition / midWay;
return minVelocity + (maxVelocity-minVelocity) * percent;
}
else if (angularPosition == midWay) {
return maxVelocity;
}
else if (angularPosition > midWay && angularPosition <= change) {
var midWayOffset = angularPosition - midWay;
var remaining = midWay - midWayOffset;
var percent = remaining / midWay;
return minVelocity + (maxVelocity-minVelocity) * percent;
}
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var sol = {
x: 125,
y: 150,
r: 30,
fill: "gold",
};
var orbit = {
x: 200,
y: 150,
semiMajor: 150,
semiMinor: 75,
};
var earth = {
x: 0,
y: 0,
r: 15,
fill: "dodgerblue",
angularPosition: 0,
angularVelocity: 0,
};
// draw sun
context.beginPath();
context.arc(sol.x, sol.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = sol.fill;
context.fill();
// draw fake sun to mark the orbit center
context.beginPath();
context.arc(orbit.x, orbit.y, sol.r, 0, 360 * Math.PI / 180);
context.fillStyle = "rgba(255,215,0,.1)";
context.fill();
// draw earth's orbit path
context.beginPath();
context.ellipse(orbit.x, orbit.y, orbit.semiMajor, orbit.semiMinor, 0, 0, 2 * Math.PI);
context.stroke();
// these are fixed, so save them as background-image
canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";
function draw() {
context.resetTransform();
context.clearRect(0, 0, canvas.width, canvas.height);
var newPosition = rotate(-earth.angularPosition, orbit.semiMajor, orbit.semiMinor, orbit.x, orbit.y);
earth.x = newPosition.x;
earth.y = newPosition.y;
earth.angularVelocity = pullRelease(earth.angularPosition, 0, 360, 0.5, 5);
earth.angularPosition += earth.angularVelocity;
if (earth.angularPosition >= 360) {
earth.angularPosition = 0;
}
position.innerHTML = Math.floor(earth.angularPosition) + "°";
velocity.innerHTML = (earth.angularVelocity).toFixed(2) + "°";
context.translate(earth.x, earth.y);
context.beginPath();
context.arc(0, 0, earth.r, 0, 360 * Math.PI / 180);
context.closePath();
context.fillStyle = earth.fill;
context.fill();
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
function rotate(angle, distanceX, distanceY, originX, originY) {
return {
x: originX + Math.cos(angle * Math.PI / 180) * distanceX,
y: originY + Math.sin(angle * Math.PI / 180) * distanceY,
}
}
function pullRelease(angularPosition, begin, change, minVelocity, maxVelocity) {
var midWay = Math.floor(change / 2);
if (angularPosition >= begin && angularPosition < midWay) {
var percent = angularPosition / midWay;
return minVelocity + (maxVelocity - minVelocity) * percent;
} else if (angularPosition == midWay) {
return maxVelocity;
} else if (angularPosition > midWay && angularPosition <= change) {
var midWayOffset = angularPosition - midWay;
var remaining = midWay - midWayOffset;
var percent = remaining / midWay;
return minVelocity + (maxVelocity - minVelocity) * percent;
}
}
body {
background: gainsboro;
}
canvas {
background: white;
box-shadow: 1px 1px 1px rgba(0, 0, 0, .1);
}
#position {
display: inline-block;
width: 35px;
text-align: right;
}
#velocity {
display: inline-block;
width: 35px;
text-align: right;
}
<canvas id="canvas" class="box" width="400" height="300"></canvas>
<p>Position: <span id="position">0</span></p>
<p>Velocity: <span id="velocity">0</span></p>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With