No edit summary |
No edit summary |
||
| (5 intermediate revisions by the same user not shown) | |||
| Line 12: | Line 12: | ||
|- | |- | ||
| ''id'' || || unique id prefix if you embed multiple widgets on one page || || wh | | ''id'' || || unique id prefix if you embed multiple widgets on one page || || wh | ||
|- | |||
| ''fps'' || || Desired fps for the animation || || 5 | |||
|} | |} | ||
</noinclude><includeonly> | </noinclude><includeonly> | ||
<div class="wh-moonstack" style="max-width:<!--{$size|default:512|escape:'html'}-->px;margin:1rem 0;"> | <div class="wh-moonstack" style="max-width:<!--{$size|default:512|escape:'html'}-->px;margin:1rem 0;"> | ||
<label for="<!--{$id|default:'wh'|escape:'html'}-->-x"><strong>Days since alignment:</strong> | |||
<span id="<!--{$id|default:'wh'|escape:'html'}-->-xval">1</span> / 6433 | |||
</label> | |||
< | <div style="display:flex;align-items:center;gap:0.5rem;"> | ||
< | <input id="<!--{$id|default:'wh'|escape:'html'}-->-x" type="range" min="1" max="6433" value="1" style="flex-grow:1;"> | ||
<button id="<!--{$id|default:'wh'|escape:'html'}-->-play" style="font-size:1rem;padding:0.2rem 0.5rem;cursor:pointer;">▶</button> | |||
</div> | |||
<div style="margin:.5rem 0;"> | <div style="margin:.5rem 0;"> | ||
| Line 31: | Line 35: | ||
style="position:relative;width:<!--{$size|default:512|escape:'html'}-->px;height:<!--{$size|default:512|escape:'html'}-->px;isolation:isolate;background:#000;border:1px solid #333;"> | style="position:relative;width:<!--{$size|default:512|escape:'html'}-->px;height:<!--{$size|default:512|escape:'html'}-->px;isolation:isolate;background:#000;border:1px solid #333;"> | ||
<img id="<!--{$id|default:'wh'|escape:'html'}-->-base" | <img id="<!--{$id|default:'wh'|escape:'html'}-->-base" | ||
src="<!--{$basePath|default:'/images/moons/'|escape:'html'}-->WyrdeHaven_center.png" width="{size}" height="{size}" | src="<!--{$basePath|default:'/images/moons/'|escape:'html'}-->WyrdeHaven_center.png?v=2" width="{size}" height="{size}" | ||
alt="WyrdeHaven base" | alt="WyrdeHaven base" | ||
style="position:absolute;top:0;left:0;width:100%;height:100%;z-index: | decoding="async" fetchpriority="high" draggable="false" | ||
style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;"/> | |||
<img id="<!--{$id|default:'wh'|escape:'html'}-->-tiriel" src="" alt="Tiriel" width="{size}" height="{size}" | <img id="<!--{$id|default:'wh'|escape:'html'}-->-tiriel" src="" alt="Tiriel" width="{size}" height="{size}" | ||
style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:2;"/> | |||
<img id="<!--{$id|default:'wh'|escape:'html'}-->-rhaen" src="" alt="Rhaen" width="{size}" height="{size}" | |||
style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;"/> | style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:3;"/> | ||
<img id="<!--{$id|default:'wh'|escape:'html'}-->-veyra" src="" alt="Veyra" width="{size}" height="{size}" | <img id="<!--{$id|default:'wh'|escape:'html'}-->-veyra" src="" alt="Veyra" width="{size}" height="{size}" | ||
style="position:absolute;top:0;left:0;width:100%;height:100%;z-index: | style="position:absolute;top:0;left:0;width:100%;height:100%;z-index:4;"/> | ||
</div> | </div> | ||
</div> | </div> | ||
<script type="text/javascript"> | <script type="text/javascript"> | ||
(function() { | (function() { | ||
| Line 57: | Line 61: | ||
v: ge("-veyra") | v: ge("-veyra") | ||
}; | }; | ||
function pad2(n){return ("0"+n).slice(-2);} | function pad2(n){return ("0"+n).slice(-2);} | ||
// --- NEW: simple preloader & caches --- | |||
var cache = { T: [], R: [], V: [] }; | |||
function preloadSet(prefix, count, bucket){ | |||
for(var i=0;i<count;i++){ | |||
var img = new Image(); | |||
img.decoding = "async"; | |||
img.referrerPolicy = "no-referrer"; | |||
img.draggable = false; | |||
img.src = BASE + prefix + "_" + pad2(i) + ".png"; | |||
bucket[i] = img; | |||
} | |||
} | |||
// Kick off preloads (non-blocking) | |||
preloadSet("Tiriel", 12, cache.T); | |||
preloadSet("Rhaen", 32, cache.R); | |||
preloadSet("Veyra", 67, cache.V); | |||
// Track last indices to avoid redundant src swaps while scrubbing | |||
var last = { t:-1, r:-1, v:-1 }; | |||
function update(){ | function update(){ | ||
var X = parseInt(el.x.value,10); | var X = parseInt(el.x.value,10); | ||
var Y = Math.floor(X/479); | var Y = Math.floor(X/479); | ||
var Z = X % 479; | var Z = X % 479; | ||
var iT = (X-1) % 12; | var iT = (X-1) % 12; | ||
var iR = (X-1) % 32; | var iR = (X-1) % 32; | ||
var iV = (X-1) % 67; | var iV = (X-1) % 67; | ||
el.xval.textContent = X; | el.xval.textContent = X; | ||
el.cyc.textContent = Y; | el.cyc.textContent = Y; | ||
el.day.textContent = Z; | el.day.textContent = Z; | ||
el.t.src = BASE + "Tiriel_" + pad2(iT) + ".png"; | |||
el.r.src = BASE + "Rhaen_" + pad2(iR) + ".png"; | if(iT !== last.t){ last.t = iT; el.t.src = (cache.T[iT] && cache.T[iT].src) || (BASE + "Tiriel_" + pad2(iT) + ".png"); } | ||
el.v.src = BASE + "Veyra_" + pad2(iV) + ".png"; | if(iR !== last.r){ last.r = iR; el.r.src = (cache.R[iR] && cache.R[iR].src) || (BASE + "Rhaen_" + pad2(iR) + ".png"); } | ||
if(iV !== last.v){ last.v = iV; el.v.src = (cache.V[iV] && cache.V[iV].src) || (BASE + "Veyra_" + pad2(iV) + ".png"); } | |||
} | } | ||
el.x.addEventListener("input", update); | |||
// Smooth scrubbing without spamming layout | |||
var raf = null; | |||
el.x.addEventListener("input", function(){ | |||
if(raf) return; | |||
raf = requestAnimationFrame(function(){ raf = null; update(); }); | |||
}); | |||
update(); | |||
var playBtn = document.getElementById(ID + "-play"); | |||
var timer = null; | |||
var playing = false; | |||
var interval = 1000/<!--{$fps|default:5|escape:'html'}-->; | |||
function stepForward(){ | |||
var v = parseInt(el.x.value, 10); | |||
if(v >= 6433) v = 1; | |||
else v++; | |||
el.x.value = v; | |||
update(); | update(); | ||
} | |||
playBtn.addEventListener("click", function(){ | |||
if(!playing){ | |||
playing = true; | |||
playBtn.textContent = "■"; // stop icon | |||
timer = setInterval(stepForward, interval); | |||
} else { | |||
playing = false; | |||
playBtn.textContent = "▶"; // play icon | |||
clearInterval(timer); | |||
timer = null; | |||
} | |||
}); | |||
})(); | })(); | ||
</script></includeonly> | </script></includeonly> | ||
Latest revision as of 16:50, 10 November 2025
This widget allows you to embed the lunar orbits of WyrdeHaven in your mediaWiki page
Usage
{{#widget:MoonStack | basePath=/images/moons/ | size=512 | id=ms1}}
| basePath | Location of image files for the orbits | /images/moons/ | ||
| size | maximum image size in pixels | 512 | ||
| id | unique id prefix if you embed multiple widgets on one page | wh | ||
| fps | Desired fps for the animation | 5 |