User:Monster Domosed/common.js: Difference between revisions

No edit summary
Undo
Tag: Undo
Line 2: Line 2:
  * Rotatable Model Viewer — with 2D / 3D toggle
  * Rotatable Model Viewer — with 2D / 3D toggle
  * All <img> elements created in JS to bypass MediaWiki sanitizer.
  * All <img> elements created in JS to bypass MediaWiki sanitizer.
*
* The rotatable sprite is always rendered at 1:1 (square) aspect,
* using min(width, height) as the side length, centred in the viewport.
  */
  */
(function () {
(function () {
Line 33: Line 30:
             startFrame  = +el.dataset.startFrame  || 0,
             startFrame  = +el.dataset.startFrame  || 0,
             sensitivity = +el.dataset.sensitivity || 5;
             sensitivity = +el.dataset.sensitivity || 5;
        /* ---- 1:1 square size & centering offsets ---- */
        var sq      = Math.min(fw, fh),
            offsetX = (fw - sq) / 2,
            offsetY = (fh - sq) / 2;


         var hint = el.querySelector('.rotatable-model-hint'),
         var hint = el.querySelector('.rotatable-model-hint'),
Line 52: Line 44:
             is2D  = false,
             is2D  = false,
             dragOK = true,
             dragOK = true,
             ready  = false;
             ready  = false;   // true once the strip image has loaded


         /* ============================================================
         /* ============================================================
           3D SPRITE STRIP —  displayed at sq × sq per frame
           3D SPRITE STRIP
           ============================================================ */
           ============================================================ */
         var img = makeImg(imageUrl, '', 'rm-strip');
         var img = makeImg(imageUrl, '', 'rm-strip');


         img.style.height = sq + 'px';
        // Set explicit display dimensions
         img.style.width  = (sq * count) + 'px';
        // width = single frame width × number of frames
        // height = frame height
         img.style.height = fh + 'px';
         img.style.width  = (fw * count) + 'px';


         img.addEventListener('load', function () {
         img.addEventListener('load', function () {
Line 73: Line 68:
         el.insertBefore(img, el.firstChild);
         el.insertBefore(img, el.firstChild);


        // Handle cached images
         if (img.complete && img.naturalWidth > 0) {
         if (img.complete && img.naturalWidth > 0) {
             ready = true;
             ready = true;
Line 80: Line 76:


         /* ============================================================
         /* ============================================================
           2D STATIC IMAGE —  also shown at sq × sq, centred
           2D STATIC IMAGE
           ============================================================ */
           ============================================================ */
         var staticImg = null;
         var staticImg = null;
         if (image2dUrl) {
         if (image2dUrl) {
             staticImg = makeImg(image2dUrl, '', 'rm-static');
             staticImg = makeImg(image2dUrl, '', 'rm-static');
            staticImg.style.width    = sq + 'px';
            staticImg.style.height    = sq + 'px';
            staticImg.style.objectFit = 'contain';
            staticImg.style.left      = offsetX + 'px';
            staticImg.style.top      = offsetY + 'px';
             el.insertBefore(staticImg, el.firstChild);
             el.insertBefore(staticImg, el.firstChild);
         }
         }
Line 136: Line 127:


         /* ============================================================
         /* ============================================================
           FRAME DISPLAY —  each frame is sq wide, centred in viewport
           FRAME DISPLAY
           ============================================================ */
           ============================================================ */
         function show(idx) {
         function show(idx) {
             cur = ((idx % count) + count) % count;
             cur = ((idx % count) + count) % count;
             img.style.left = (offsetX - cur * sq) + 'px';
            // Move the strip so that frame `cur` is visible in the viewport
             img.style.top  = offsetY + 'px';
            var offset = cur * fw;
             img.style.left = '-' + offset + 'px';
             img.style.top  = '0px';
         }
         }


Line 194: Line 187:
         /* ============================================================
         /* ============================================================
           DRAG  (mouse + touch)
           DRAG  (mouse + touch)
          One frame per `sensitivity` pixels of movement.
          Clamped to ±1 frame per event to prevent skipping.
           ============================================================ */
           ============================================================ */
         function startDrag(x0) {
         function startDrag(x0) {
Line 205: Line 200:
                 acc += dx;
                 acc += dx;


                // Convert accumulated pixels to frame steps
                 while (acc >= sensitivity) {
                 while (acc >= sensitivity) {
                     acc -= sensitivity;
                     acc -= sensitivity;
Line 247: Line 243:
         }, { passive: false });
         }, { passive: false });


        /* ---- wheel: always ±1 frame per tick ---- */
         el.addEventListener('wheel', function (e) {
         el.addEventListener('wheel', function (e) {
             if (!dragOK || !ready) return;
             if (!dragOK || !ready) return;
Line 253: Line 250:
         }, { passive: false });
         }, { passive: false });


        /* ---- keyboard ---- */
         el.setAttribute('tabindex', '0');
         el.setAttribute('tabindex', '0');
         el.addEventListener('keydown', function (e) {
         el.addEventListener('keydown', function (e) {
Line 261: Line 259:
         });
         });


        /* ---- nav buttons ---- */
         if (wrap) {
         if (wrap) {
             var bL = wrap.querySelector('.rm-btn-left'),
             var bL = wrap.querySelector('.rm-btn-left'),
Line 270: Line 269:
         }
         }


        /* ---- initial frame ---- */
         show(startFrame);
         show(startFrame);
     }
     }


    /* ---- scan & init ---- */
     function initAll(root) {
     function initAll(root) {
         var els = (root || document).querySelectorAll('.rotatable-model');
         var els = (root || document).querySelectorAll('.rotatable-model');