No edit summary |
No edit summary |
||
| Line 84: | Line 84: | ||
var ID = "<!--{$id|default:'ts'|escape:'js'}-->"; | var ID = "<!--{$id|default:'ts'|escape:'js'}-->"; | ||
var FPS = parseFloat("<!--{$fps|default:5|escape:'js'}-->") || 5; | var FPS = parseFloat("<!--{$fps|default:5|escape:'js'}-->") || 5; | ||
var WOBBLE = parseFloat("<!--{$wobble|default:2.5|escape:'js'}-->") || 2.5; | var WOBBLE = parseFloat("<!--{$wobble|default:2.5|escape:'js'}-->") || 2.5; | ||
var EQUATOR = parseFloat("<!--{$equator|default:8|escape:'js'}-->") || 8; | var EQUATOR = parseFloat("<!--{$equator|default:8|escape:'js'}-->") || 8; | ||
var SEP = parseFloat("<!--{$sep|default:2.5|escape:'js'}-->") || 2.5; | var SEP = parseFloat("<!--{$sep|default:2.5|escape:'js'}-->") || 2.5; | ||
var DEGPX = parseFloat("<!--{$degpx|default:10|escape:'js'}-->") || 10; | var DEGPX = parseFloat("<!--{$degpx|default:10|escape:'js'}-->") || 10; | ||
function ge(s){ return document.getElementById(ID + s); } | function ge(s){return document.getElementById(ID + s);} | ||
// | // Safe init (works even if the widget is placed inside tabs/collapsibles) | ||
function | function ready(fn){ | ||
if(document.readyState === "complete" || document.readyState === "interactive"){ | |||
setTimeout(fn,0); | |||
} else { | |||
document.addEventListener("DOMContentLoaded", fn, {once:true}); | |||
} | |||
} | } | ||
ready(function init(){ | |||
var el = { | |||
day: ge("-daylabel"), | |||
rng: ge("-x"), | |||
play: ge("-play"), | |||
date: ge("-date"), | |||
stage: ge("-stage"), | |||
sky: ge("-sky"), | |||
s: ge("-solara"), | |||
v: ge("-vaelus"), | |||
rel: ge("-relinc"), | |||
eq: ge("-eqinc") | |||
}; | |||
// If something failed to render, don't throw—just stop quietly | |||
for (var k in el){ if(!el[k]) return; } | |||
var TAU = Math.PI * 2; | |||
function cosPhase(d){ return Math.cos(TAU * ((d-1)/479)); } | |||
var | function fmtDeg(x){ var s = (x>=0?"+":""); return s + x.toFixed(2) + "°"; } | ||
function openingClosing(delta, phase){ | |||
var s = Math.sin(TAU * phase); | |||
return (-s > 0) ? "Closing" : "Opening"; | |||
} | |||
var months = ["Primaris","Vernalis","Tharion","Lunarae","Cindrel","Solvane","Auren","Veyrath","Rhendol","Tirien","Gairos","Fenmir","Wyrdrun","Cernis","Eirathen"]; | |||
function dateText(d){ | |||
if(d <= 7) return "Fortnight of Renewal (Dawning), day " + (d+7) + " (FR " + (d+7) + ")"; | |||
if(d >= 473){var k=d-472; return "Fortnight of Renewal (Fading), day " + k + " (FR " + k + ")";} | |||
return "Fortnight of Renewal (Dawning), day " + (d+7) + " (FR " + (d+7) + ")"; | var n = d - 7, m = Math.ceil(n/31), day = ((n-1)%31)+1; | ||
var n = d - 7 | |||
return day + " " + months[m-1]; | return day + " " + months[m-1]; | ||
} | } | ||
var SOL_SHARE = 1.0, VAE_SHARE = 1.5, SUM = SOL_SHARE + VAE_SHARE; | |||
function setStarSizes(){ | |||
var w = el.stage.clientWidth || el.stage.getBoundingClientRect().width || 0; | |||
if(!w){ return; } // wait for layout | |||
var base = Math.round(w * 0.05); | |||
el.s.width = el.s.height = base; | |||
el.v.width = el.v.height = Math.round(base * 0.72); | |||
} | |||
function update(){ | |||
try{ | |||
var d = parseInt(el.rng.value,10) || 1; | |||
var phase = (d-1)/479; | |||
var delta = WOBBLE * cosPhase(d); | |||
var eq = -EQUATOR * cosPhase(d); | |||
el.day.textContent = d + " / 479"; | |||
el.date.textContent = dateText(d); | |||
el.rel.textContent = fmtDeg(delta) + " (" + openingClosing(delta, phase) + ")"; | |||
el.eq.textContent = fmtDeg(eq); | |||
var w = el.stage.clientWidth, h = el.stage.clientHeight; | |||
if(!w || !h){ return; } | |||
var cx = w * 0.5, cy = h * 0.33; | |||
var dx = (SEP/2) * DEGPX; | |||
var solOff = (delta * (SOL_SHARE/SUM)) * DEGPX; | |||
var vaeOff = -(delta * (VAE_SHARE/SUM)) * DEGPX; | |||
var yBase = cy - (eq * DEGPX); | |||
el.s.style.left = (cx - dx) + "px"; | |||
el.v.style.left = (cx + dx) + "px"; | |||
el.s.style.top = (yBase - solOff) + "px"; | |||
el.v.style.top = (yBase - vaeOff) + "px"; | |||
}catch(e){ /* swallow to avoid killing other widgets */ } | |||
} | |||
// If the stage hasn't sized yet (e.g., inside tabs), retry until it has | |||
function ensureSizedThen(fn){ | |||
setStarSizes(); | |||
if(el.stage.clientWidth){ fn(); } | |||
else { setTimeout(function(){ ensureSizedThen(fn); }, 50); } | |||
} | } | ||
var timer=null, playing=false, interval = 1000/Math.max(1,FPS); | |||
function step(){ var v = parseInt(el.rng.value,10)||1; el.rng.value = (v>=479)?1:(v+1); update(); } | |||
el.rng.addEventListener("input", update); | |||
el.play.addEventListener("click", function(){ | |||
if(!playing){ playing=true; el.play.textContent="■"; timer=setInterval(step, interval); } | |||
else { playing=false; el.play.textContent="▶"; clearInterval(timer); timer=null; } | |||
}); | |||
window.addEventListener("resize", function(){ setStarSizes(); update(); }); | |||
ensureSizedThen(function(){ update(); }); | |||
}); | }); | ||
})(); | })(); | ||
</script> | </script> | ||
</includeonly> | </includeonly> | ||
Revision as of 21:02, 11 November 2025
This widget shows WyrdeHaven’s twin suns with seasonal wobble & calendar.
Usage
1 / 479
Date:
Fortnight of Renewal (Dawning), day 8 (FR 8)
Relative Solar Inclination (Δ):
+2.50° (Closing)
Seasonal Equatorial Inclination (barycenter):
-8.00°
| param | meaning | default |
|---|---|---|
| size | max stage width (px) | 768 |
| fps | play speed (frames/s) | 5 |
| wobble | solar relative wobble amplitude (Δ, degrees) | 2.5 |
| equator | seasonal/barycenter max tilt (degrees) | 8 |
| sep | visible horizontal separation between suns (degrees) | 2.5 |
| degpx | vertical degree→pixel scale (px per degree) | 10 |