Javascript

时间:2015-06-01 15:32:05

标签: javascript math canvas

所以我试图在弧形路径中画一个太阳(Pref a circle path)来模拟日出/日落,我遇到了问题。

画布宽度= 800; 画布高度= 100;

if(getTime() - sunTime > 100){
    angle++;
    sunTime = getTime();
}

sun.x = 400 * Math.sin(angle * (Math.PI/180));//Convert to degrees
sun.y = 100 * Math.sin(angle * (Math.PI/180));

这是我试图使其正确旋转的代码,它似乎给了我错误的弧形类型。

关于HTML画布的奇怪之处在于(0,0)位于屏幕的左上角,所以事情有点奇怪。我试图找到一个体面的网站,解释如何找到它,但我找到的一切似乎并没有按照我想要的方式工作。

我根据我对数学的了解进行了这次转换....如果这种方法有所了解,请告诉我!

1 个答案:

答案 0 :(得分:4)

错误是由于x和y都使用了正弦。由于画布朝向0°方向,因此它应该是:

sun.x = 400 * Math.cos(angle * (Math.PI/180));  // cos for x
sun.y = 100 * Math.sin(angle * (Math.PI/180));

请注意,此处有400,100是半径,因此要定义一个中心,您还需要一个中心点:

sun.x = centerX + radiusX * Math.cos(angle * (Math.PI/180));
sun.y = centerY + radiusY * Math.sin(angle * (Math.PI/180));

只需要考虑几点 -

什么被解释为逼真的日出/日落将在很大程度上取决于你在世界上的位置(与纬度或φ相关)。

例如,如果您居住在厄瓜多尔,或者φ= 0,太阳基本上会直线上升并且直线下降。如果你在北极圈(或南极圈)之上,你会有一个相当陡峭的角度。此外,太阳永远不会在夏季时间(midnight sun)设置,并且在冬季(polar night)永远不会在这些纬度上升。

如果您刚刚接近近似值,则可以在角度0处定义基线(日出),在角度180处定义日落,从左到右(或从东到西,如果您在南部)以弧形定义半球你可能更喜欢相反的方式。)

Canvas'坐标系将指向右0°,90°将指向下方,因此我们将知道180°360°将是一个具有天顶(最高点)的圆弧,为270°。

这将使用虚拟太阳和任意步骤绘制这样的弧:

snap

var ctx = document.querySelector("canvas").getContext("2d");

var angle = Math.PI,                    // we'll use radians here, 0 to Math.PI (=180)
    centerX = ctx.canvas.width * 0.5,   // center of arc
    bottomY = ctx.canvas.height,
    radiusX = ctx.canvas.width  * 0.8,  // radius, 80% of width in this example
    radiusY = ctx.canvas.height * 0.9;  // radius, 90% of height in this example

// goes 180 to 360°, in radians PI to 2xPI
for(; angle < Math.PI*2; angle += 0.1) {
  var x = centerX + radiusX * Math.cos(angle);
  var y = bottomY + radiusY * Math.sin(angle);
  ctx.fillRect(x - 2, y - 2, 4, 4);     // dummy sun for now
}
<canvas></canvas>

下一步是规范化输入和输出值,这样我们就可以将标准化值从时间插入太阳渲染器。

要标准化时间,您可以定义日出和日落时间。然后在该范围内划分当前时间。如果值在[0,1]范围内,我们有太阳并且可以将该值插入“渲染器”。

通用公式(不考虑极端情况如f.ex.(ant)北极圈):

var normTime = (currentTime - sunriseTime) / (sunsetTime - sunrisetime);

渲染器将采用标准化值并使用简单插值将其应用于[180,360]范围:

var currentAngle = Math.PI + (Math.PI * 2 - Math.PI) * normTime;

或简化为:

var currentAngle = Math.PI + Math.PI * normTime;

您甚至可以将标准化时间值插入渐变值,也可以绘制代表天空颜色的背景。

为简单起见,我们可以模拟一个运行速度非常快的24小时时钟:

var ctx = document.querySelector("canvas").getContext("2d"),
    gr = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height),
    sky = new Image();

sky.onload = go;
sky.src = "http://i.stack.imgur.com/qhQhQ.jpg";

function go() {
  
// some style setup
ctx.font = "bold 16px sans-serif";
gr.addColorStop(0, "#ffc");
gr.addColorStop(0.75, "gold");
gr.addColorStop(1, "orange");

ctx.shadowColor = "#ffa";

var centerX = ctx.canvas.width * 0.5,   // center of arc
    bottomY = ctx.canvas.height + 16,   // let center be a bit below horizon
    radiusX = ctx.canvas.width  * 0.52, // radius, 80% of width in this example
    radiusY = ctx.canvas.height * 0.8;  // radius, 90% of height in this example

// define sunrise and sunset times (in 24-hour clock, can be fractional)
var time = 7, sunrise = 7, sunset = 19;

(function loop() {
  var normTime = getTime();                                  // get normalized time
  var angle = getAngle(normTime);                            // get angle in radians
  var x = centerX + radiusX * Math.cos(angle);               // calcuate point
  var y = bottomY + radiusY * Math.sin(angle);
  drawSky(normTime);                                         // draw sky gradient
  drawSun(x, y);                                             // draw sun at point
  drawTime();                                                // render time
  requestAnimationFrame(loop)                                // loop
})();

function getTime() {
  // produces a normalized pseduo-time
  time += 0.033;
  if (time > 23) time = 0;
  return (time - sunrise) / (sunset - sunrise);
}

function getAngle(normTime) {
  return Math.PI + Math.PI * normTime
}

function drawSun(x, y) {
  ctx.beginPath();
  ctx.moveTo(x + 16, y);
  ctx.arc(x, y, 16, 0, 6.28);
  ctx.fillStyle = gr;
  ctx.shadowBlur = 20;
  ctx.fill();
}

function drawTime() {
  ctx.fillStyle = "#fff";
  ctx.shadowBlur = 0;
  ctx.fillText("Time: " + time.toFixed(1) + "h", 10, 20);
}
  
  function drawSky(t) {
    t = Math.max(0, Math.min(1, t));
      var iw = sky.width,
          w =  ctx.canvas.width,
          x = 60 + (iw - 120) * t;
    ctx.drawImage(sky, x, 0, 1, sky.height, 0, 0, w, ctx.canvas.height);
  }
}
canvas {background:#acf}
<canvas width=400 height=180></canvas>

现在剩下的就是根据纬度找到宽度和高度的水平线和半径。天顶/方位角也是需要考虑的因素,太阳路径的中心可能不会在地平线上,所以必须根据一年中的位置和时间减少或增加标准化的角度范围。

另请参阅 Yahoo's Weather channel API ,其中提供了您所在位置的时间/日落时间。还可以查看 NOAA's page ,了解正确计算与观点等相关的角度的提示和公式。(NASA也有一些很好的资源)

当它在[0,1]范围之外时,您也可以限制太阳绘图,具体取决于最终渲染的方式(在上面的演示中,画布将为我们剪辑)。