使用动态坐标定位在 canvas
Positioning on canvas with dynamic coordinates
我正在研究交互式太阳系 canvas 和 HTML5 canvas。我希望能够在页面加载后将行星定位在 canvas 上的特定坐标处。现在我把它们都排在一条水平线上,所以我知道它们彼此之间的距离有多远。正如您在小图中看到的那样。
但这就是我的问题,因为 x 和 y 坐标会根据使它们旋转的函数不断变化,我不知道如何将这些行星准确定位在 canvas 上,以便它们从彼此并没有像他们在图像上的样子,同时仍然让他们定位,以便他们在旋转时不会直接接触彼此。我认为这里涉及到一些数学。
另外:我意识到不同的动画间隔可能是解决这个问题的方法,但我遇到了麻烦。当我为每个星球创建新的动画功能时,星球绘图并不像一个动画带来的那样流畅。我意识到这本身就是一个独立的问题!
这里是大部分相关代码:
function initCanvas(){
var ctx = document.getElementById('my_canvas').getContext('2d');
var dynamicSunW = 25;
var dynamicSunH = 0;
var dynamicSunX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5); //dynamicSun is always at dead center of canvas
var dynamicSunY = (ctx.canvas.height * .5) - (dynamicSunH * .5);
var angleOfSun = 0;
var posMercuryX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posMercuryY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 20;
var gravityMercury = {x: posMercuryX, y: posMercuryY };
var posVenusX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posVenusY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 46;
var gravityVenus = {x: posVenusX, y: posVenusY };
var posEarthX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posEarthY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 80;
var gravityEarth = {x: posEarthX, y: posEarthY};
var posMarsX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posMarsY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 116;
var gravityMars = {x: posMarsX, y: posMarsY };
var posJupiterX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posJupiterY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 157;
var gravityJupiter = {x: posJupiterX, y: posJupiterY };
var posSaturnX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posSaturnY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 208;
var gravitySaturn = {x: posSaturnX, y: posSaturnY };
var posUranusX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posUranusY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 250;
var gravityUranus = {x: posUranusX, y: posUranusY };
var posNeptuneX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posNeptuneY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 283.9;
var gravityNeptune = {x: posNeptuneX, y: posNeptuneY };
function rotate_point(pointX, pointY, originX, originY, ang) {
ang = Math.PI / 180.0;
return {
x: Math.cos(ang) * (pointX-originX) - Math.sin(ang) * (pointY-originY) + originX,
y: Math.sin(ang) * (pointX-originX) + Math.cos(ang) * (pointY-originY) + originY
};
}
var Solarsystem = {
Neptune: {
render: function(){
ctx.beginPath();
gravityNeptune = rotate_point(gravityNeptune.x, gravityNeptune.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityNeptune.x,gravityNeptune.y ,10, 0, 2*Math.PI, true);
ctx.fillStyle = "darkblue";
ctx.closePath();
ctx.fill();
}
}
, Uranus: {
render: function(){
ctx.beginPath();
gravityUranus = rotate_point(gravityUranus.x, gravityUranus.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityUranus.x,gravityUranus.y ,6, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(64,224,208)";
ctx.closePath();
ctx.fill();
}
}
, Saturn: {
render: function(){
ctx.beginPath();
gravitySaturn = rotate_point(gravitySaturn.x, gravitySaturn.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravitySaturn.x,gravitySaturn.y ,15, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(186,85,211)";
ctx.closePath();
ctx.fill();
}
}
, Jupiter: {
render: function(){
ctx.beginPath();
gravityJupiter = rotate_point(gravityJupiter.x, gravityJupiter.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityJupiter.x,gravityJupiter.y ,18, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(255,255,153)";
ctx.closePath();
ctx.fill();
}
}
, Mars: {
render: function(){
ctx.beginPath();
gravityMars = rotate_point(gravityMars.x, gravityMars.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityMars.x,gravityMars.y ,7, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(255,99,71)";
ctx.closePath();
ctx.fill();
}
}
, Earth: {
render: function(){
ctx.beginPath();
gravityEarth = rotate_point(gravityEarth.x, gravityEarth.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityEarth.x,gravityEarth.y ,8, 0, 2*Math.PI);
ctx.fillStyle = "rgba(30,144,255,1)";
ctx.closePath();
ctx.fill();
}
}
, Venus: {
render: function(){
ctx.beginPath();
gravityVenus = rotate_point(gravityVenus.x, gravityVenus.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityVenus.x,gravityVenus.y ,7, 0, 2*Math.PI);
ctx.fillStyle = "rgba(255,165,0,1)";
ctx.closePath();
ctx.fill();
}
}
, Mercury: {
render: function(){
ctx.beginPath();
gravityMercury = rotate_point(gravityMercury.x, gravityMercury.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityMercury.x,gravityMercury.y ,5, 0, 2*Math.PI);
ctx.fillStyle = "rgba(119,136,153,1)";
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
, Sun: {
render: function(){
ctx.fillStyle = "rgba(255,255,51,1)";
ctx.save(); //store ctx so it can be later reused
ctx.shadowColor = 'yellow';
ctx.shadowBlur = 70;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.arc(dynamicSunX, dynamicSunY, dynamicSunW, dynamicSunH, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.restore(); //ctx at time of save
}
}
}
var bg = new Image();
bg.src = "spacedef.png";
function Background(){
this.x = 0, this.y = 0, this.w = bg.width, this.h = bg.height;
this.render = function(){
ctx.drawImage(bg, this.x--, 0);
if(this.x <= -499){
this.x = 0;
}
}
}
var background = new Background();
function animate(){
background.render();
Solarsystem.Neptune.render();
Solarsystem.Uranus.render();
Solarsystem.Saturn.render();
Solarsystem.Jupiter.render();
Solarsystem.Mars.render();
Solarsystem.Earth.render();
Solarsystem.Venus.render();
Solarsystem.Mercury.render();
Solarsystem.Sun.render();
}
var animateInterval = setInterval(animate, 1000/60);
}
与其使用彼此相对的旋转点,不如让所有行星围绕太阳旋转。每个行星都会有自己的太阳半径,因此您的数学计算可以简化为(对于圆形轨道):
var neptune.x=sun.x+neptune.radius*Math.cos(radianAngle);
var neptune.y=sun.y+neptune.radius*Math.sin(radianAngle);
当然,行星轨道实际上是椭圆形多于圆形。您可以像这样计算椭圆上的点:
// Calc points on Ellipse
function getPointsOnEllipse(cx,cy,a,b){
var startAngle=-PI/2;
var lastX=cx-(a*Math.cos(startAngle));
var lastY=cy+(b*Math.sin(startAngle));
var points=[];
// change 1000 to your desired count of waypoints along the ellipse
for(var i=0;i<1000;i++){
var angle=startAngle+PI2/1000*i;
var x=cx-(a*Math.cos(angle));
var y=cy+(b*Math.sin(angle));
var dx=x-lastX;
var dy=y-lastY;
var length=parseInt(Math.sqrt(dx*dx+dy*dy));
var eAngle=(Math.atan2(dy,dx)+PI2)%PI2;
if(length>0){
points.push({x:x,y:y,angle:eAngle});
lastX=x;
lastY=y;
}
}
return(points);
}
我正在研究交互式太阳系 canvas 和 HTML5 canvas。我希望能够在页面加载后将行星定位在 canvas 上的特定坐标处。现在我把它们都排在一条水平线上,所以我知道它们彼此之间的距离有多远。正如您在小图中看到的那样。
但这就是我的问题,因为 x 和 y 坐标会根据使它们旋转的函数不断变化,我不知道如何将这些行星准确定位在 canvas 上,以便它们从彼此并没有像他们在图像上的样子,同时仍然让他们定位,以便他们在旋转时不会直接接触彼此。我认为这里涉及到一些数学。
另外:我意识到不同的动画间隔可能是解决这个问题的方法,但我遇到了麻烦。当我为每个星球创建新的动画功能时,星球绘图并不像一个动画带来的那样流畅。我意识到这本身就是一个独立的问题!
这里是大部分相关代码:
function initCanvas(){
var ctx = document.getElementById('my_canvas').getContext('2d');
var dynamicSunW = 25;
var dynamicSunH = 0;
var dynamicSunX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5); //dynamicSun is always at dead center of canvas
var dynamicSunY = (ctx.canvas.height * .5) - (dynamicSunH * .5);
var angleOfSun = 0;
var posMercuryX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posMercuryY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 20;
var gravityMercury = {x: posMercuryX, y: posMercuryY };
var posVenusX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posVenusY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 46;
var gravityVenus = {x: posVenusX, y: posVenusY };
var posEarthX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posEarthY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 80;
var gravityEarth = {x: posEarthX, y: posEarthY};
var posMarsX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posMarsY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 116;
var gravityMars = {x: posMarsX, y: posMarsY };
var posJupiterX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posJupiterY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 157;
var gravityJupiter = {x: posJupiterX, y: posJupiterY };
var posSaturnX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posSaturnY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 208;
var gravitySaturn = {x: posSaturnX, y: posSaturnY };
var posUranusX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posUranusY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 250;
var gravityUranus = {x: posUranusX, y: posUranusY };
var posNeptuneX = (ctx.canvas.width * .5) - (Math.PI* 1 * .5) - 50;
var posNeptuneY = (ctx.canvas.height * .5) - (dynamicSunH * .5) + 283.9;
var gravityNeptune = {x: posNeptuneX, y: posNeptuneY };
function rotate_point(pointX, pointY, originX, originY, ang) {
ang = Math.PI / 180.0;
return {
x: Math.cos(ang) * (pointX-originX) - Math.sin(ang) * (pointY-originY) + originX,
y: Math.sin(ang) * (pointX-originX) + Math.cos(ang) * (pointY-originY) + originY
};
}
var Solarsystem = {
Neptune: {
render: function(){
ctx.beginPath();
gravityNeptune = rotate_point(gravityNeptune.x, gravityNeptune.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityNeptune.x,gravityNeptune.y ,10, 0, 2*Math.PI, true);
ctx.fillStyle = "darkblue";
ctx.closePath();
ctx.fill();
}
}
, Uranus: {
render: function(){
ctx.beginPath();
gravityUranus = rotate_point(gravityUranus.x, gravityUranus.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityUranus.x,gravityUranus.y ,6, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(64,224,208)";
ctx.closePath();
ctx.fill();
}
}
, Saturn: {
render: function(){
ctx.beginPath();
gravitySaturn = rotate_point(gravitySaturn.x, gravitySaturn.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravitySaturn.x,gravitySaturn.y ,15, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(186,85,211)";
ctx.closePath();
ctx.fill();
}
}
, Jupiter: {
render: function(){
ctx.beginPath();
gravityJupiter = rotate_point(gravityJupiter.x, gravityJupiter.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityJupiter.x,gravityJupiter.y ,18, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(255,255,153)";
ctx.closePath();
ctx.fill();
}
}
, Mars: {
render: function(){
ctx.beginPath();
gravityMars = rotate_point(gravityMars.x, gravityMars.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityMars.x,gravityMars.y ,7, 0, 2*Math.PI, true);
ctx.fillStyle = "rgb(255,99,71)";
ctx.closePath();
ctx.fill();
}
}
, Earth: {
render: function(){
ctx.beginPath();
gravityEarth = rotate_point(gravityEarth.x, gravityEarth.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityEarth.x,gravityEarth.y ,8, 0, 2*Math.PI);
ctx.fillStyle = "rgba(30,144,255,1)";
ctx.closePath();
ctx.fill();
}
}
, Venus: {
render: function(){
ctx.beginPath();
gravityVenus = rotate_point(gravityVenus.x, gravityVenus.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityVenus.x,gravityVenus.y ,7, 0, 2*Math.PI);
ctx.fillStyle = "rgba(255,165,0,1)";
ctx.closePath();
ctx.fill();
}
}
, Mercury: {
render: function(){
ctx.beginPath();
gravityMercury = rotate_point(gravityMercury.x, gravityMercury.y, dynamicSunX, dynamicSunY, angleOfSun)
ctx.arc(gravityMercury.x,gravityMercury.y ,5, 0, 2*Math.PI);
ctx.fillStyle = "rgba(119,136,153,1)";
ctx.closePath();
ctx.fill();
ctx.stroke();
}
}
, Sun: {
render: function(){
ctx.fillStyle = "rgba(255,255,51,1)";
ctx.save(); //store ctx so it can be later reused
ctx.shadowColor = 'yellow';
ctx.shadowBlur = 70;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.beginPath();
ctx.arc(dynamicSunX, dynamicSunY, dynamicSunW, dynamicSunH, Math.PI*2, true);
ctx.closePath();
ctx.fill();
ctx.restore(); //ctx at time of save
}
}
}
var bg = new Image();
bg.src = "spacedef.png";
function Background(){
this.x = 0, this.y = 0, this.w = bg.width, this.h = bg.height;
this.render = function(){
ctx.drawImage(bg, this.x--, 0);
if(this.x <= -499){
this.x = 0;
}
}
}
var background = new Background();
function animate(){
background.render();
Solarsystem.Neptune.render();
Solarsystem.Uranus.render();
Solarsystem.Saturn.render();
Solarsystem.Jupiter.render();
Solarsystem.Mars.render();
Solarsystem.Earth.render();
Solarsystem.Venus.render();
Solarsystem.Mercury.render();
Solarsystem.Sun.render();
}
var animateInterval = setInterval(animate, 1000/60);
}
与其使用彼此相对的旋转点,不如让所有行星围绕太阳旋转。每个行星都会有自己的太阳半径,因此您的数学计算可以简化为(对于圆形轨道):
var neptune.x=sun.x+neptune.radius*Math.cos(radianAngle);
var neptune.y=sun.y+neptune.radius*Math.sin(radianAngle);
当然,行星轨道实际上是椭圆形多于圆形。您可以像这样计算椭圆上的点:
// Calc points on Ellipse
function getPointsOnEllipse(cx,cy,a,b){
var startAngle=-PI/2;
var lastX=cx-(a*Math.cos(startAngle));
var lastY=cy+(b*Math.sin(startAngle));
var points=[];
// change 1000 to your desired count of waypoints along the ellipse
for(var i=0;i<1000;i++){
var angle=startAngle+PI2/1000*i;
var x=cx-(a*Math.cos(angle));
var y=cy+(b*Math.sin(angle));
var dx=x-lastX;
var dy=y-lastY;
var length=parseInt(Math.sqrt(dx*dx+dy*dy));
var eAngle=(Math.atan2(dy,dx)+PI2)%PI2;
if(length>0){
points.push({x:x,y:y,angle:eAngle});
lastX=x;
lastY=y;
}
}
return(points);
}