Widget:MoonStack: Difference between revisions

From WyrdeWiki
(Moonphase stack widget)
 
No edit summary
 
(18 intermediate revisions by the same user not shown)
Line 1: Line 1:
<widget xmlns="http://www.w3.org/1999/xhtml">
<noinclude>__NOTOC__
This widget allows you to embed the lunar orbits of WyrdeHaven in your mediaWiki page


  <parameters>
=Usage=
    <!-- Base path where the PNGs are stored -->
&lcub;&lcub;#widget:MoonStack &#124; basePath=/images/moons/ &#124; size=512 &#124; id=ms1&rcub;&rcub;
    <parameter name="basePath" default="/images/moons/"/>
    <!-- Optional width for the visualization -->
    <parameter name="size" default="512"/>
  </parameters>


  <content><![CDATA[
{|
    <div class="wh-moonstack" style="max-width:{size}px;margin:1rem 0;">
|-
     
| ''basePath'' || &nbsp; || Location of image files for the orbits || &nbsp; || /images/moons/
      <label for="wh-x">
|-
        <strong>Alignment step:</strong>  
| ''size'' || &nbsp; || maximum image size in pixels || &nbsp; || 512
        <span id="wh-xval">1</span> / 6433
|-
      </label>
| ''id'' || &nbsp; || unique id prefix if you embed multiple widgets on one page || &nbsp; || wh
      <input id="wh-x" type="range" min="1" max="6433" value="1"
|-
            style="width:100%;">
| ''fps'' || &nbsp; || Desired fps for the animation || &nbsp; || 5
|}
</noinclude><includeonly>
<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="margin:.5rem 0;">
  <div style="display:flex;align-items:center;gap:0.5rem;">
        <strong>When:</strong>
    <input id="<!--{$id|default:'wh'|escape:'html'}-->-x" type="range" min="1" max="6433" value="1" style="flex-grow:1;">
        <span id="wh-cycles">0</span> Cycles,
    <button id="<!--{$id|default:'wh'|escape:'html'}-->-play" style="font-size:1rem;padding:0.2rem 0.5rem;cursor:pointer;">▶</button>
        <span id="wh-days">1</span> days
  </div>
      </div>


      <div id="wh-stage"
  <div style="margin:.5rem 0;">
        style="
    <strong>When:</strong>
          position:relative;
    <span id="<!--{$id|default:'wh'|escape:'html'}-->-cycles">0</span> Cycles,
          width:{size}px;
    <span id="<!--{$id|default:'wh'|escape:'html'}-->-days">1</span> days
          height:{size}px;
  </div>
          isolation:isolate;
          border:1px solid #333;
          background:#000;
        ">
       
        <img id="wh-base"
          src="{basePath}WyrdeHaven_center.png"
          alt="WyrdeHaven base"
          style="position:absolute;inset:0;object-fit:contain;z-index:1;"/>


        <img id="wh-tiriel"
  <div id="<!--{$id|default:'wh'|escape:'html'}-->-stage"
          src=""
      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;">
          alt="Tiriel"
    <img id="<!--{$id|default:'wh'|escape:'html'}-->-base"
          style="position:absolute;inset:0;object-fit:contain;z-index:2;"/>
        src="<!--{$basePath|default:'/images/moons/'|escape:'html'}-->WyrdeHaven_center.png?v=2" width="{size}" height="{size}"
        alt="WyrdeHaven base"
        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}"
        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;"/>
    <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:4;"/>
  </div>
</div>
<script type="text/javascript">
(function() {
  var ID  = "<!--{$id|default:'wh'|escape:'js'}-->";
  var BASE = "<!--{$basePath|default:'/images/moons/'|escape:'js'}-->";
  function ge(s){return document.getElementById(ID + s);}
  var el = {
    x:    ge("-x"),
    xval: ge("-xval"),
    cyc:  ge("-cycles"),
    day:  ge("-days"),
    t:    ge("-tiriel"),
    r:    ge("-rhaen"),
    v:    ge("-veyra")
  };


        <img id="wh-rhaen"
  function pad2(n){return ("0"+n).slice(-2);}
          src=""
          alt="Rhaen"
          style="position:absolute;inset:0;object-fit:contain;z-index:3;"/>


        <img id="wh-veyra"
  // --- NEW: simple preloader & caches ---
          src=""
  var cache = { T: [], R: [], V: [] };
          alt="Veyra"
          style="position:absolute;inset:0;object-fit:contain;z-index:4;"/>
      </div>


     </div>
  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);


    <script type="text/javascript">
  // Track last indices to avoid redundant src swaps while scrubbing
      (function() {
  var last = { t:-1, r:-1, v:-1 };
        const BASE = "{basePath}";
        const SIZE = {size};


        const el = {
   function update(){
          x:      document.getElementById("wh-x"),
    var X = parseInt(el.x.value,10);
          xval:   document.getElementById("wh-xval"),
    var Y = Math.floor(X/479);
          cyc:    document.getElementById("wh-cycles"),
    var Z = X % 479;
          days:  document.getElementById("wh-days"),
          tiriel: document.getElementById("wh-tiriel"),
          rhaen:  document.getElementById("wh-rhaen"),
          veyra:  document.getElementById("wh-veyra")
        };


        function pad2(n) {
    var iT = (X-1) % 12;
          return ("0" + n).slice(-2);
    var iR = (X-1) % 32;
        }
    var iV = (X-1) % 67;


        function update() {
    el.xval.textContent = X;
          const X = parseInt(el.x.value, 10);
    el.cyc.textContent  = Y;
    el.day.textContent  = Z;


          const Y = Math.floor(X / 479);
    if(iT !== last.t){ last.t = iT; el.t.src = (cache.T[iT] && cache.T[iT].src) || (BASE + "Tiriel_" + pad2(iT) + ".png"); }
          const Z = X % 479;
    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"); }
  }


          const iT = (X - 1) % 12;
  // Smooth scrubbing without spamming layout
          const iR = (X - 1) % 32;
  var raf = null;
          const iV = (X - 1) % 67;
  el.x.addEventListener("input", function(){
    if(raf) return;
    raf = requestAnimationFrame(function(){ raf = null; update(); });
  });


          el.xval.textContent = X;
  update();
          el.cyc.textContent  = Y;
var playBtn = document.getElementById(ID + "-play");
          el.days.textContent = Z;
var timer = null;
var playing = false;
var interval = 1000/<!--{$fps|default:5|escape:'html'}-->;


          el.tiriel.src = BASE + "Tiriel_" + pad2(iT) + ".png";
function stepForward(){
          el.rhaen.src  = BASE + "Rhaen_"  + pad2(iR) + ".png";
  var v = parseInt(el.x.value, 10);
          el.veyra.src  = BASE + "Veyra_"  + pad2(iV) + ".png";
  if(v >= 6433) v = 1;
        }
  else v++;
  el.x.value = v;
  update();
}


        el.x.addEventListener("input", update);
playBtn.addEventListener("click", function(){
        update();
  if(!playing){
      })();
    playing = true;
     </script>
    playBtn.textContent = "■"; // stop icon
 
    timer = setInterval(stepForward, interval);
  ]]></content>
  } else {
 
    playing = false;
</widget>
    playBtn.textContent = "▶";  // play icon
    clearInterval(timer);
     timer = null;
  }
});
})();
</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