<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Universal Board Viewer</title>
<style>
body {
margin: 0;
background: #111;
width: 100vw;
height: 100vh;
overflow: hidden;
font-family: Arial, sans-serif;
}
/* Gesamter Viewer-Bereich */
#stage {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
/* Hintergrundbild browserfensterbreit */
#background {
position: absolute;
left: 50%;
top: 50%;
width: 100vw;
height: auto;
transform: translate(-50%, -50%);
z-index: 1;
pointer-events: none;
user-select: none;
}
/* Drehbares Board bleibt separat und mittig */
#viewer {
position: absolute;
left: 50%;
top: 50%;
width: 800px;
height: 800px;
transform: translate(-50%, -50%);
background-position: center;
background-repeat: no-repeat;
background-size: contain;
cursor: grab;
user-select: none;
touch-action: none;
z-index: 2;
}
#viewer.dragging {
cursor: grabbing;
}
.info {
position: fixed;
left: 50%;
bottom: 20px;
transform: translateX(-50%);
color: rgba(255,255,255,0.7);
font-size: 14px;
z-index: 10;
pointer-events: none;
}
/* Mobile etwas kleiner, damit das Board nicht aus dem Screen läuft */
@media (max-width: 900px) {
#viewer {
width: 90vw;
height: 90vw;
}
}
</style>
Klicken + Ziehen zum Drehen
<script>
// -----------------------------------
// EINSTELLUNGEN
// -----------------------------------
const imageFolder = 'images/';
const imagePrefix = 'Board_';
const imageExtension = '.webp';
// Prüft Board_01.webp bis Board_999.webp
const maxFramesToCheck = 999;
// Drag-Empfindlichkeit
// kleiner = feinfühliger / langsamer
// größer = schneller
const dragStep = 8;
// -----------------------------------
// VIEWER
// -----------------------------------
const viewer = document.getElementById('viewer');
let images = [];
let currentFrame = 0;
let dragging = false;
let lastX = 0;
// -----------------------------------
// HILFSFUNKTIONEN
// -----------------------------------
function padNumber(num, size) {
let s = String(num);
while (s.length < size) {
s = '0' + s;
}
return s;
}
function imageExists(src) {
return new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(src);
img.onerror = () => resolve(null);
img.src = src;
});
}
// -----------------------------------
// BILDER AUTOMATISCH LADEN
// -----------------------------------
async function loadAvailableImages() {
const checks = [];
for (let i = 1; i <= maxFramesToCheck; i++) {
const fileName =
imageFolder +
imagePrefix +
padNumber(i, 2) +
imageExtension;
checks.push(imageExists(fileName));
}
const results = await Promise.all(checks);
images = results.filter(src => src !== null);
if (images.length === 0) {
alert('Keine Board-Bilder gefunden. Prüfe: images/Board_01.webp, Board_02.webp usw.');
return;
}
render();
}
// -----------------------------------
// RENDER
// -----------------------------------
function render() {
if (images.length === 0) return;
viewer.style.backgroundImage =
url('${images[currentFrame]}') ;
}
// -----------------------------------
// FRAME UPDATE
// -----------------------------------
function updateFrame(direction) {
if (images.length === 0) return;
currentFrame += direction;
if (currentFrame < 0) {
currentFrame = images.length - 1;
}
if (currentFrame >= images.length) {
currentFrame = 0;
}
render();
}
// -----------------------------------
// MOUSE
// -----------------------------------
viewer.addEventListener('mousedown', (e) => {
dragging = true;
lastX = e.clientX;
viewer.classList.add('dragging');
});
window.addEventListener('mouseup', () => {
dragging = false;
viewer.classList.remove('dragging');
});
window.addEventListener('mousemove', (e) => {
if (!dragging) return;
const delta = e.clientX - lastX;
if (Math.abs(delta) > dragStep) {
if (delta > 0) {
updateFrame(-1);
} else {
updateFrame(1);
}
lastX = e.clientX;
}
});
// -----------------------------------
// TOUCH SUPPORT
// -----------------------------------
viewer.addEventListener('touchstart', (e) => {
dragging = true;
lastX = e.touches[0].clientX;
});
viewer.addEventListener('touchend', () => {
dragging = false;
});
viewer.addEventListener('touchmove', (e) => {
if (!dragging) return;
const currentX = e.touches[0].clientX;
const delta = currentX - lastX;
if (Math.abs(delta) > dragStep) {
if (delta > 0) {
updateFrame(-1);
} else {
updateFrame(1);
}
lastX = currentX;
}
});
// -----------------------------------
// START
// -----------------------------------
loadAvailableImages();
</script>