admin管理员组

文章数量:1306628

So I am trying to draw a sun in a arc path (Pref a circle path) to simulate a sunrise / sunset and I am running into issues.

Canvas Width = 800; Canvas Height = 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));

That is the code that I am trying to make it arc correctly and it seems to give me the wrong type of arc.

The weird thing about the HTML canvas is that (0,0) is at the top left of the screen so things are a little weird. I tried finding a decent website that explained how to find it but everything I find does not seem to work the way I want it to.

I did this conversion based on what I know with math.... Soooo if This approach is way off please let me know!

So I am trying to draw a sun in a arc path (Pref a circle path) to simulate a sunrise / sunset and I am running into issues.

Canvas Width = 800; Canvas Height = 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));

That is the code that I am trying to make it arc correctly and it seems to give me the wrong type of arc.

The weird thing about the HTML canvas is that (0,0) is at the top left of the screen so things are a little weird. I tried finding a decent website that explained how to find it but everything I find does not seem to work the way I want it to.

I did this conversion based on what I know with math.... Soooo if This approach is way off please let me know!

Share Improve this question edited Jun 2, 2015 at 3:50 user1693593 asked Jun 1, 2015 at 15:32 JustinJustin 8861 gold badge13 silver badges29 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 13

The error is due to sinus being used for both x and y. As canvas is oriented with 0° heading right it should be:

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

Note that 400,100 are radius here so to define a center you would also need a center point:

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

Just a few points to consider -

What is interpreted as a realistic sunrise/sunset will depend largely on where you are in the world (in relation to latitude, or φ).

For example, if you live in Ecuador, or φ = 0, the sun will basically go straight up and straight down. If you are above the arctic circle (or antarctic circle for that matter) you will have a pretty steep angle. Additionally the sun will never set in summer time (midnight sun), and never rise in the winter (polar night) at these latitudes.

If you're just after an approximation however, you can define baseline (sunrise) at angle 0, and sunset at angle 180, going in an arc from left to right (or east to west, if you are in the southern hemisphere you'd probably prefer the opposite).

Canvas' coordinate system will have 0° pointing to right, 90° will point down, so we will know that 180 - 360° will be an arc with zenith (highest point) at 270°.

This would draw an arc like this using a dummy sun and arbitrary steps:

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>

The next step is to normalize the input and output values so we can just plug a normalized value from time into the sun-renderer.

To normalize time you can define sunrise and sunset times. Then divide current time on that range. If the value is within [0, 1] range we have sun and can plug that value into the "renderer".

A generic formula (not considering extreme cases as f.ex. (ant)arctic circles):

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

The renderer will take a normalized value and apply it to the [180, 360] range by using simple interpolation:

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

or simplified to:

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

You can even plug the normalized time value into a gradient value as well do draw a background representing the sky color.

For simplicity, lets simulate a 24 hour clock that runs very fast:

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

sky.onload = go;
sky.src = "https://i.sstatic/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>

What's left now is to find horizon line and radius for width and height depending on latitude. Zenith/azimuth are also factors to consider as well as that the sun-path's center will probably not lay on horizon so the angle-range to normalize must be reduced or increased depending on location and time of year.

See also Yahoo's Weather channel API which provides surise/sunset times for where you are. Also check out NOAA's page for hints and formulas to correctly calculate angles in relation to view point etc. (NASA has some good resources as well)

You could also limit the sun-drawing when it's outside [0, 1] range depending on how your final render will go (in the demo above canvas will clip it for us).

本文标签: mathSun Arc Path in JavascriptStack Overflow