在轨道上模拟物体的重力吸引

5
我正在尝试在以下动画中模拟万有引力/加速度。随着地球接近太阳,它的角速度应该增加,远离太阳时则减小。我认为需要一个缓动函数来修改earth.angularVelocity,但不知道如何实现。
我不知道已定义的缓动函数自定义函数是否可行。我需要的缓动函数应该像这张图表一样:pull/relase easing function graph earth的近日点位于180°,远日点位于0/360°。我该如何创建这样的函数并使其正常工作?
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>


重力只是加速度。请参考此答案,了解如何模拟恒定重力:http://stackoverflow.com/questions/38194362/jumping-code-in-javascript/38194745#38194745。对于天体物体,您只需要按照反比例平方定律(谷歌一下)通过距离来改变引力即可。 - slebetman
@slebetman 在我的情况下,没有空闲空间/动态对象。唯一动态的是earth(位置),它遵循固定的路径。我也希望它的速度是动态的。我认为我不需要复杂的物理计算,但需要某种缓动函数。我已经有一个工作的函数了,只需要一些改进。 - akinuri
1个回答

2

经过一些思考,我想出了一个似乎可行的函数,但我仍然认为效果不够自然。该函数生成左图所示的图形。理想情况下应该是右图的样子。因此,该函数仍需要进一步改进。

enter image description here

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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接