Browser-Canvas-Spiele mobilfähig machen
Browser-Canvas-Spiele mobilfähig machen
Wer Browser-Spiele mit dem HTML5-Canvas-Element baut, denkt meist zuerst an Desktop und Tastatur. Wenn dieselben Spiele auch auf Tablets und Smartphones funktionieren sollen, braucht es ein paar gezielte Anpassungen. Dieser Post fasst zusammen, was dabei wirklich zählt.
1. Canvas CSS-Skalierung — ohne die Spiellogik anzufassen
Das Canvas-Element hat zwei unabhängige Größen: die logische Auflösung (die Attribute width und height im HTML) und die CSS-Anzeigegröße. Standardmäßig sind beide identisch, was auf kleinen Bildschirmen zu horizontalem Scrollen führt.
Die einfachste Lösung: Canvas per CSS skalieren, ohne die interne Auflösung zu ändern. Das lässt alle Spiellogik unberührt.
/* Canvas skaliert sich auf die Container-Breite,
behält aber das Seitenverhältnis */
#game-container {
max-width: 100%;
}
#game-container canvas {
max-width: 100%;
height: auto !important; /* !important überschreibt inline-Styles */
display: block;
}
max-width: 100% schrumpft den Canvas auf die verfügbare Breite. height: auto erhält das Seitenverhältnis. Der Browser skaliert die Pixelausgabe — der Spielcode läuft weiterhin in der originalen Auflösung.
2. Der Koordinaten-Versatz-Bug bei Click-to-Place-Spielen
Spiele, bei denen man per Klick oder Tap etwas auf dem Canvas platziert, haben nach der CSS-Skalierung ein subtiles Problem: Die Touch-/Klick-Koordinaten des Events sind in CSS-Pixeln, aber der Spielcode erwartet Canvas-Logik-Pixel. Bei einem 800px breiten Canvas der auf 400px skaliert angezeigt wird, liegt jede Platzierung um den Faktor 2 versetzt.
Symptom: Man tippt auf die rechte Bildschirmhälfte, das Objekt erscheint in der Mitte — oder weiter links.
Fix: Den Scale-Faktor in die Koordinatenberechnung einbeziehen:
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
// Scale-Faktor: wie viel Canvas-Pixel pro CSS-Pixel?
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// Korrekte Canvas-Koordinaten
const x = (e.clientX - rect.left) * scaleX;
const y = (e.clientY - rect.top) * scaleY;
// Ab hier wie gehabt...
placeItemAt(x, y);
});
touchstart) identisch — touch.clientX statt e.clientX, sonst gleiche Rechnung.
3. Touch-Controls für Tastatur-Spiele
Spiele die auf Pfeiltasten oder WASD angewiesen sind funktionieren auf Touchscreens ohne zusätzliche Steuerelemente gar nicht. Die Lösung: On-Screen-Buttons, die nur auf Touch-Geräten eingeblendet werden.
Die sauberste CSS-Methode nutzt pointer: coarse — das trifft Touch-Bildschirme ohne Hacks wie User-Agent-Erkennung:
/* Steuerung nur auf Touch-Geräten zeigen */
.touch-controls {
display: none;
}
@media (pointer: coarse) {
.touch-controls {
display: grid; /* oder flex */
}
}
Für Spiele mit kontinuierlichem Input (Schiff bewegt sich solange Taste gedrückt) müssen die Buttons den Tastaturzustand direkt simulieren:
/* Das Spiel liest: if (keys['ArrowLeft']) { ... } */
const keys = {};
document.querySelectorAll('[data-key]').forEach(btn => {
const k = btn.dataset.key;
btn.addEventListener('touchstart', e => {
e.preventDefault(); // verhindert Scroll-Interferenz
keys[k] = true;
btn.classList.add('pressed');
}, { passive: false });
btn.addEventListener('touchend', () => {
keys[k] = false;
btn.classList.remove('pressed');
});
btn.addEventListener('touchcancel', () => {
keys[k] = false;
btn.classList.remove('pressed');
});
});
Für Spiele mit diskretem Input (ein Schritt pro Tastendruck, wie Frogger) reicht ein einfacher Aufruf der bestehenden Input-Funktion auf touchstart:
btn.addEventListener('touchstart', e => {
e.preventDefault();
handleInput('ArrowUp'); // bestehende Spielfunktion
}, { passive: false });
4. Viewport-Höhe ohne Scrollen
Auf Mobilgeräten ist die sichtbare Fläche deutlich kleiner als die Canvas-Originalgröße. Damit kein vertikales Scrollen nötig ist, hilft eine kombinierte Strategie:
- Margins und Padding des Game-Wrappers auf Mobile stark reduzieren
- Canvas mit
max-heightnach unten deckeln 100dvhstatt100vhverwenden —dvhberücksichtigt die Browser-Statusleiste korrekt
/* Canvas überschreitet nie die Viewport-Höhe */
#game-container canvas {
max-height: calc(100dvh - 280px);
/* 280px ≈ Header + Touch-Controls + Abstände */
}
@media (max-width: 768px) {
#game-wrapper {
padding: 0.5rem !important;
margin: 0.25rem auto !important;
}
}
5. Mobile-UX: Was weglassen, was ergänzen
Einige Desktop-Elemente sind auf Mobile überflüssig oder störend — andere fehlen:
- Sidebar mit Stats ausblenden — Platz ist knapp, die Stats können bei Bedarf in einem kompakten Strip unter dem Canvas erscheinen
- Start-Button ersetzen — wenn die Sidebar ausgeblendet wird, fehlt auch der Start-Button. Ein dedizierter mobiler Start-Button löst das:
.mobile-start-btn { display: none; } @media (max-width: 768px) { .mobile-start-btn { display: flex; } } - Keyboard-Hinweise ausblenden — „Pfeiltasten zum Steuern" ist auf Touch sinnlos:
@media (pointer: coarse) { .keyboard-hint { display: none; } } - Modales Overlay korrekt positionieren — ein Hilfe-Overlay mit
position: fixed; top: 0überdeckt den fixierten Header. Besser:top: 56px(Höhe des Headers), damit der Header weiterhin bedienbar bleibt
6. UX-Entscheidung: Keine doppelte Navigation
Ein „Zurück"-Button im Spiel-Header klingt intuitiv, ist aber überflüssig: Jeder Browser und jedes Betriebssystem bietet native Zurück-Navigation (Swipe, Hardware-Taste, Alt+←). Ein eigener Button belegt wertvollen Header-Platz und führt auf schmalen Displays zu Überlappungen.
Zusammenfassung
Canvas-Spiele mobilfähig zu machen ist weniger aufwendig als es klingt, wenn man die richtigen Stellen kennt:
- CSS-Skalierung:
max-width: 100%; height: autoauf dem Canvas — kein JS nötig, keine Spiellogik anfassen - Koordinaten-Fix:
scaleX = canvas.width / rect.widthüberall wo Klick- oder Touch-Positionen ins Canvas-Koordinatensystem umgerechnet werden - Touch-Controls:
@media (pointer: coarse)für On-Screen-Buttons,touchstart/endmite.preventDefault()undpassive: false - Viewport-Fit: Margins reduzieren,
max-height: calc(100dvh - Xpx)setzen - UX aufräumen: Redundante Elemente entfernen, mobile Start-Button hinzufügen