Rendering robuster + sichtbare Fehlermeldung + no-cache
- crossOrigin von Asset-Images entfernt (haeufige Ursache fuer "tainted canvas", wodurch der Szenen-Export fehlschlug -> kaputtes Bild) - toBlob mit Fallback auf toDataURL - Ladefehler werden jetzt sichtbar im UI angezeigt (statt nur Konsole) - Frontend mit Cache-Control: no-cache -> kein veraltetes JS/CSS nach git pull Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -269,6 +269,24 @@ html, body {
|
|||||||
#next-scene:hover { background: var(--pink-soft); }
|
#next-scene:hover { background: var(--pink-soft); }
|
||||||
@keyframes pop { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
@keyframes pop { from { transform: scale(0.85); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
||||||
|
|
||||||
|
/* ---- Fehleranzeige ---- */
|
||||||
|
#error-box {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
max-width: 82%;
|
||||||
|
background: #2a1020;
|
||||||
|
color: #ffd0e2;
|
||||||
|
border: 1px solid var(--pink);
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden { display: none !important; }
|
.hidden { display: none !important; }
|
||||||
|
|
||||||
/* Mobile */
|
/* Mobile */
|
||||||
|
|||||||
@@ -31,12 +31,30 @@ function showToast(message) {
|
|||||||
showToast._t = setTimeout(() => toast.classList.remove('show'), 900);
|
showToast._t = setTimeout(() => toast.classList.remove('show'), 900);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showError(err) {
|
||||||
|
let box = document.getElementById('error-box');
|
||||||
|
if (!box) {
|
||||||
|
box = document.createElement('div');
|
||||||
|
box.id = 'error-box';
|
||||||
|
stage.appendChild(box);
|
||||||
|
}
|
||||||
|
const msg = err && err.message ? err.message : String(err);
|
||||||
|
box.textContent = 'Fehler beim Laden der Szene: ' + msg;
|
||||||
|
box.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearError() {
|
||||||
|
const box = document.getElementById('error-box');
|
||||||
|
if (box) box.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
async function loadScene() {
|
async function loadScene() {
|
||||||
showLoading(true);
|
showLoading(true);
|
||||||
|
clearError();
|
||||||
winOverlay.classList.add('hidden');
|
winOverlay.classList.add('hidden');
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/scene');
|
const res = await fetch('/api/scene');
|
||||||
if (!res.ok) throw new Error('Szene konnte nicht geladen werden.');
|
if (!res.ok) throw new Error('Server-Antwort ' + res.status);
|
||||||
current = await res.json();
|
current = await res.json();
|
||||||
await scene.render(current);
|
await scene.render(current);
|
||||||
scene.resetView();
|
scene.resetView();
|
||||||
@@ -44,7 +62,7 @@ async function loadScene() {
|
|||||||
document.getElementById('category-name').textContent = current.category || '';
|
document.getElementById('category-name').textContent = current.category || '';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
showToast('Ups — Szene konnte nicht geladen werden.');
|
showError(err);
|
||||||
} finally {
|
} finally {
|
||||||
showLoading(false);
|
showLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ let emojiAssetsAvailable = null; // pro Session: liegt ein Emoji-Bildset vor?
|
|||||||
function loadImage(url) {
|
function loadImage(url) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.crossOrigin = 'anonymous';
|
// Kein crossOrigin: Assets sind same-origin. crossOrigin kann je nach Browser/Proxy
|
||||||
|
// dazu fuehren, dass das Canvas als "tainted" gilt und der Export fehlschlaegt.
|
||||||
img.onload = () => resolve({ url, img });
|
img.onload = () => resolve({ url, img });
|
||||||
img.onerror = () => resolve({ url, img: null });
|
img.onerror = () => resolve({ url, img: null });
|
||||||
img.src = url;
|
img.src = url;
|
||||||
@@ -155,9 +156,20 @@ export class Scene {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await new Promise((resolve) => {
|
// Export als Blob-URL; mit Fallback auf dataURL, falls toBlob nicht greift.
|
||||||
canvas.toBlob((blob) => resolve(URL.createObjectURL(blob)), 'image/png');
|
try {
|
||||||
});
|
const blob = await new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob lieferte null'))), 'image/png');
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('[scene] toBlob fehlgeschlagen, versuche toDataURL:', err);
|
||||||
|
return canvas.toDataURL('image/png'); // wirft nur, wenn Canvas wirklich "tainted" ist
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawItem(ctx, item) {
|
_drawItem(ctx, item) {
|
||||||
|
|||||||
@@ -57,8 +57,14 @@ if (scenePool.length === 0) {
|
|||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// Statisches Frontend + Assets
|
// Statisches Frontend + Assets.
|
||||||
app.use(express.static(PUBLIC_DIR));
|
// no-cache fuers Frontend (HTML/JS/CSS): Browser/Proxy muss revalidieren -> nie altes
|
||||||
|
// Skript nach einem Update (haeufige Ursache fuer "geht gar nix" nach git pull).
|
||||||
|
app.use(
|
||||||
|
express.static(PUBLIC_DIR, {
|
||||||
|
setHeaders: (res) => res.setHeader('Cache-Control', 'no-cache'),
|
||||||
|
})
|
||||||
|
);
|
||||||
app.use('/scenes', express.static(SCENES_DIR));
|
app.use('/scenes', express.static(SCENES_DIR));
|
||||||
app.use('/library', express.static(LIBRARY_DIR));
|
app.use('/library', express.static(LIBRARY_DIR));
|
||||||
if (existsSync(PANZOOM_DIST)) {
|
if (existsSync(PANZOOM_DIST)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user