User:Monster Domosed/common.js: Difference between revisionsGive feedback
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
/** | /** | ||
* Rotatable Model Viewer — with 2D / 3D toggle | * Rotatable Model Viewer — with 2D / 3D toggle | ||
* All <img> elements are created here in JS to bypass MediaWiki's HTML sanitizer. | |||
*/ | */ | ||
(function () { | (function () { | ||
'use strict'; | 'use strict'; | ||
function makeImg(src, alt, className) { | |||
var i = new Image(); | |||
i.src = src; | |||
i.alt = alt || ''; | |||
i.draggable = false; | |||
if (className) i.className = className; | |||
return i; | |||
} | |||
function boot(el) { | function boot(el) { | ||
| Line 9: | Line 19: | ||
el.dataset.rmReady = '1'; | el.dataset.rmReady = '1'; | ||
/* ---- config | /* ---- config ---- */ | ||
var imageUrl = el.dataset.image || '', | var imageUrl = el.dataset.image || '', | ||
image2dUrl = el.dataset.image2d || '', | image2dUrl = el.dataset.image2d || '', | ||
icon2dUrl = el.dataset.icon_2d || el.dataset.icon2d || '', | |||
icon3dUrl = el.dataset.icon_3d || el.dataset.icon3d || '', | |||
iconRotUrl = el.dataset.icon_rotate || el.dataset.iconRotate || '', | |||
fw = +el.dataset.frameWidth || 300, | fw = +el.dataset.frameWidth || 300, | ||
fh = +el.dataset.frameHeight || 400, | fh = +el.dataset.frameHeight || 400, | ||
| Line 18: | Line 31: | ||
sensitivity = +el.dataset.sensitivity || 5; | sensitivity = +el.dataset.sensitivity || 5; | ||
var hint | var hint = el.querySelector('.rotatable-model-hint'), | ||
load | load = el.querySelector('.rotatable-model-loading'), | ||
wrap | wrap = el.closest('.rotatable-model-wrapper'); | ||
if (!imageUrl) { | if (!imageUrl) { | ||
| Line 27: | Line 40: | ||
} | } | ||
/* | /* ---- state ---- */ | ||
var cur = startFrame, | |||
is2D = false, | |||
var cur | dragOK = true; | ||
is2D | |||
dragOK | |||
/* ============================================================ | /* ============================================================ | ||
3D SPRITE STRIP | 3D SPRITE STRIP (<img> created in JS) | ||
============================================================ */ | ============================================================ */ | ||
var img = | var img = makeImg(imageUrl, '', 'rm-strip'); | ||
img.style.height = fh + 'px'; | img.style.height = fh + 'px'; | ||
img.style.width = (fw * count) + 'px'; | img.style.width = (fw * count) + 'px'; | ||
| Line 54: | Line 62: | ||
}); | }); | ||
el.insertBefore(img, el.firstChild); | el.insertBefore(img, el.firstChild); | ||
| Line 62: | Line 69: | ||
/* ============================================================ | /* ============================================================ | ||
2D STATIC IMAGE (created | 2D STATIC IMAGE (<img> created in JS) | ||
============================================================ */ | ============================================================ */ | ||
var staticImg = null; | var staticImg = null; | ||
if (image2dUrl) { | if (image2dUrl) { | ||
staticImg = | staticImg = makeImg(image2dUrl, '', 'rm-static'); | ||
el.insertBefore(staticImg, el.firstChild); | el.insertBefore(staticImg, el.firstChild); | ||
} | |||
/* ============================================================ | |||
MODE TOGGLE (top-right, built in JS) | |||
============================================================ */ | |||
var btn2d = null, btn3d = null; | |||
if (image2dUrl) { | |||
var toggle = document.createElement('div'); | |||
toggle.className = 'rm-mode-toggle'; | |||
btn2d = document.createElement('span'); | |||
btn2d.className = 'rm-mode-btn rm-mode-2d'; | |||
btn2d.setAttribute('role', 'button'); | |||
btn2d.setAttribute('tabindex', '0'); | |||
btn2d.title = '2D View'; | |||
if (icon2dUrl) { | |||
btn2d.appendChild(makeImg(icon2dUrl, '2D', 'rm-mode-icon')); | |||
} else { | |||
btn2d.textContent = '2D'; | |||
} | |||
btn3d = document.createElement('span'); | |||
btn3d.className = 'rm-mode-btn rm-mode-3d rm-active'; | |||
btn3d.setAttribute('role', 'button'); | |||
btn3d.setAttribute('tabindex', '0'); | |||
btn3d.title = '3D View'; | |||
if (icon3dUrl) { | |||
btn3d.appendChild(makeImg(icon3dUrl, '3D', 'rm-mode-icon')); | |||
} else { | |||
btn3d.textContent = '3D'; | |||
} | |||
toggle.appendChild(btn2d); | |||
toggle.appendChild(btn3d); | |||
el.appendChild(toggle); | |||
} | |||
/* ============================================================ | |||
ROTATION ICON (bottom-right, built in JS) | |||
============================================================ */ | |||
if (iconRotUrl) { | |||
var rotDiv = document.createElement('div'); | |||
rotDiv.className = 'rm-rotate-icon'; | |||
rotDiv.appendChild(makeImg(iconRotUrl, 'Drag to rotate', '')); | |||
el.appendChild(rotDiv); | |||
} | } | ||
| Line 86: | Line 135: | ||
/* ============================================================ | /* ============================================================ | ||
2D ↔ 3D | 2D ↔ 3D SWITCHING | ||
============================================================ */ | ============================================================ */ | ||
function setMode(mode) { | function setMode(mode) { | ||
is2D = (mode === '2d'); | is2D = (mode === '2d'); | ||
| Line 104: | Line 150: | ||
} | } | ||
if (btn2d && btn3d) { | if (btn2d && btn3d) { | ||
btn2d.classList.toggle('rm-active', is2D); | btn2d.classList.toggle('rm-active', is2D); | ||
| Line 111: | Line 156: | ||
} | } | ||
if (btn2d) | if (btn2d) btn2d.addEventListener('click', function () { setMode('2d'); }); | ||
if (btn3d) btn3d.addEventListener('click', function () { setMode('3d'); }); | |||
if (btn3d) | |||
/* ============================================================ | /* ============================================================ | ||
| Line 155: | Line 196: | ||
el.addEventListener('mousedown', function (e) { | el.addEventListener('mousedown', function (e) { | ||
if (e.target.closest('.rm-mode-toggle')) return; | if (e.target.closest('.rm-mode-toggle')) return; | ||
if (e.button === 0 && dragOK) { startDrag(e.clientX); e.preventDefault(); } | if (e.button === 0 && dragOK) { startDrag(e.clientX); e.preventDefault(); } | ||
| Line 177: | Line 217: | ||
el.addEventListener('keydown', function (e) { | el.addEventListener('keydown', function (e) { | ||
if (is2D) return; | if (is2D) return; | ||
if (e.key === 'ArrowLeft') { nudge(1); e.preventDefault(); } | if (e.key === 'ArrowLeft') { nudge(-1); e.preventDefault(); } | ||
if (e.key === 'ArrowRight') { nudge(1); e.preventDefault(); } | if (e.key === 'ArrowRight') { nudge( 1); e.preventDefault(); } | ||
if (e.key === 'Home') { reset(); e.preventDefault(); } | if (e.key === 'Home') { reset(); e.preventDefault(); } | ||
}); | }); | ||
| Line 187: | Line 227: | ||
bR = wrap.querySelector('.rm-btn-right'), | bR = wrap.querySelector('.rm-btn-right'), | ||
bO = wrap.querySelector('.rm-btn-reset'); | bO = wrap.querySelector('.rm-btn-reset'); | ||
if (bL) bL.addEventListener('click', function () { nudge( | if (bL) bL.addEventListener('click', function () { nudge(1); }); | ||
if (bR) bR.addEventListener('click', function () { nudge( 1); }); | if (bR) bR.addEventListener('click', function () { nudge(-1); }); | ||
if (bO) bO.addEventListener('click', reset); | if (bO) bO.addEventListener('click', reset); | ||
} | } | ||
| Line 196: | Line 236: | ||
} | } | ||
/* ---- scan & | /* ---- scan & init ---- */ | ||
function initAll(root) { | function initAll(root) { | ||
var els = (root || document).querySelectorAll('.rotatable-model'); | var els = (root || document).querySelectorAll('.rotatable-model'); | ||