Since the sun is an approximate .5° sphere as viewed from the moon, the terminator is not a hard edge. Additionally, the moon's mountains and craters will also soften the transition from light to dark, so the code below creats a 2° gradient between the light and dark regions to provide a more realistic effect.
'use strict';
//Greg Miller (gmiller@gregmiller.net) 2023
//Released as public domain
//http://www.celestialprogramming.com/
const rad=Math.PI/180;
const canvas = document.getElementById("canvas");
const w=canvas.width;
const h=canvas.height;
const ctx = canvas.getContext("2d");
const r=w/2*.91;
const gradient=2;
function getX(phase,angle){
const f=Math.cos(phase*rad);
let x;
const cosi=Math.cos(angle*rad);
x=f*r*cosi+w/2;
if((phase<=180 && cosi<0) || (phase>180 && cosi>0)){
x=r*cosi+w/2;
}
return x;
}
function drawDarkSide(phase){
const gradientPoints=[];
let x=getX(phase-gradient,0);
let y=r*Math.sin(0)+h/2;
ctx.beginPath();
ctx.moveTo(x,y);
for(let i=0;i<=360;i+=1){
x=getX(phase+gradient,i);
let x2=getX(phase-gradient,i);
if(phase>180){
const temp=x;
x=x2;
x2=temp;
}
y=r*Math.sin(i*rad)+h/2;
gradientPoints.push([x,x2,y,phase]);
ctx.lineTo(x,y+1);
}
ctx.closePath();
ctx.fill();
return gradientPoints;
}
function fillGradient(points){
if(Math.abs(points[0][3]-180)<=gradient){
return;
}
for(let i=0;i<points.length-1;i++){
let x1=points[i][0];
let x2=points[i][1];
let y1=points[i][2];
let y2=points[i+1][2];
if(points[i][3]>180){
x1++;
}
const g=ctx.createLinearGradient(x1,y1,x2,y2);
g.addColorStop(1,"#00000000");
g.addColorStop(0,"#000000ff");
ctx.fillStyle=g;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x1,y2);
ctx.closePath();
ctx.fill();
}
}
function drawPhase(phase){
ctx.fillStyle="#000000cc";
const gradientPoints=drawDarkSide(phase);
fillGradient(gradientPoints);
}
function display(phase){
ctx.drawImage(document.getElementById("moon"), 0, 0);
drawPhase(phase);
}
function animate(){
phase=(phase+1)%360;
display(phase);
window.setTimeout(animate,10);
}
let phase=0;
animate();