Bonnes pratiques d’intégration des jeux dans une iframe
De nombreuses plateformes (Itch.io, Newgrounds, Cinevva, votre propre site) intègrent les jeux dans des iframes. Ce tutoriel explique comment garantir le bon fonctionnement de votre jeu lorsqu’il est intégré.
1) Configuration de base pour l’intégration
Votre jeu doit fonctionner sans nécessiter le contrôle total de la page :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { margin: 0; padding: 0; }
html, body { width: 100%; height: 100%; overflow: hidden; }
canvas { display: block; width: 100%; height: 100%; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<script src="game.js"></script>
</body>
</html>2) Détecter le contexte d’une iframe
const isEmbedded = window.self !== window.top
if (isEmbedded) {
// Adapter le comportement au contexte d’intégration
hideExternalLinks()
adjustUIForSmallSize()
}
function hideExternalLinks() {
document.querySelectorAll('a[target="_blank"]').forEach(link => {
link.style.display = 'none'
})
}3) Autoriser les permissions nécessaires dans l’iframe
La page parente contrôle ce que votre iframe peut faire. Voici les permissions courantes :
<iframe
src="https://game.example.com"
allow="fullscreen; autoplay; gamepad; pointer-lock"
sandbox="allow-scripts allow-same-origin allow-pointer-lock allow-popups"
></iframe>Votre jeu doit gérer correctement les permissions manquantes :
// Vérifier si le mode plein écran est disponible
const canFullscreen = document.fullscreenEnabled || document.webkitFullscreenEnabled
if (!canFullscreen) {
document.getElementById('fullscreen-btn').style.display = 'none'
}4) Mode plein écran depuis une iframe
Le mode plein écran nécessite l’attribut allow="fullscreen" :
async function requestFullscreen() {
const elem = document.documentElement
try {
if (elem.requestFullscreen) {
await elem.requestFullscreen()
} else if (elem.webkitRequestFullscreen) {
await elem.webkitRequestFullscreen()
}
} catch (err) {
// Plein écran non autorisé — afficher un message
showMessage('Le mode plein écran n’est pas disponible lorsque le jeu est intégré')
}
}5) Communiquer avec la page parente
Utilisez postMessage pour communiquer de manière sécurisée entre différentes origines :
Dans votre jeu :
// Envoyer un message à la page parente
function notifyParent(type, data) {
if (window.parent !== window) {
window.parent.postMessage({ type, data, source: 'game' }, '*')
}
}
// Recevoir les messages de la page parente
window.addEventListener('message', (event) => {
// Valider l’origine si nécessaire
// if (event.origin !== 'https://trusted-host.com') return
const { type, data } = event.data
switch (type) {
case 'pause':
pauseGame()
break
case 'resume':
resumeGame()
break
case 'setVolume':
setVolume(data.volume)
break
}
})
// Signaler que le jeu est prêt
window.addEventListener('load', () => {
notifyParent('ready', { width: 800, height: 600 })
})
// Signaler les événements du jeu
function onGameOver(score) {
notifyParent('gameover', { score })
}Dans la page parente :
const iframe = document.getElementById('game-iframe')
iframe.addEventListener('load', () => {
// Écouter les messages du jeu
window.addEventListener('message', (event) => {
if (event.source !== iframe.contentWindow) return
if (event.data.source !== 'game') return
const { type, data } = event.data
if (type === 'ready') {
console.log('Jeu prêt :', data)
}
if (type === 'gameover') {
showScoreModal(data.score)
}
})
})
// Envoyer des commandes au jeu
function pauseGame() {
iframe.contentWindow.postMessage({ type: 'pause' }, '*')
}6) Gérer le focus
Les iframes peuvent perdre le focus, ce qui interrompt les commandes au clavier :
// Donner automatiquement le focus au canvas lors d’un clic
canvas.addEventListener('click', () => {
canvas.focus()
})
// Rendre le canvas sélectionnable
canvas.tabIndex = 1
// Gérer la perte du focus
window.addEventListener('blur', () => {
// Réinitialiser les touches maintenues
input.left = input.right = input.up = input.down = false
if (isEmbedded) {
// Mettre éventuellement le jeu en pause
// pauseGame()
}
})
// Demander le focus à la page parente
function requestFocus() {
notifyParent('requestFocus', {})
}7) Dimensions adaptatives pour l’intégration
Gérez les différentes dimensions d’intégration :
function handleResize() {
const width = window.innerWidth
const height = window.innerHeight
// Adapter l’interface à la taille disponible
if (width < 400 || height < 300) {
enableCompactUI()
} else {
enableFullUI()
}
// Redimensionner correctement le jeu
resizeCanvas(width, height)
}
window.addEventListener('resize', handleResize)
handleResize()8) Indicateur de chargement pour les jeux intégrés
Affichez immédiatement un élément visuel :
// Intégrer directement dans le HTML pour un affichage instantané
const loadingHTML = `
<div id="loading" style="
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: #1a1a2e;
color: #fff;
font-family: sans-serif;
">
<div>
<div class="spinner"></div>
<p>Chargement...</p>
</div>
</div>
`
// Supprimer l’indicateur lorsque le jeu est prêt
function hideLoading() {
const loading = document.getElementById('loading')
if (loading) {
loading.style.opacity = '0'
loading.style.transition = 'opacity 0.3s'
setTimeout(() => loading.remove(), 300)
}
}9) SDK propres aux plateformes
Certaines plateformes disposent de leurs propres API :
Itch.io :
// Aucun SDK requis, mais postMessage peut servir pour les succès
window.parent.postMessage({ type: 'itch-achievement', data: { id: 'first-win' }}, '*')Newgrounds :
// Inclure le SDK Newgrounds.io
const ngio = new Newgrounds.io.core('APP_ID', 'AES_KEY')
ngio.callComponent('Medal.unlock', { id: 12345 })Cinevva :
// Signaler que le jeu est prêt
window.parent.postMessage({ type: 'cinevva:ready' }, '*')
// Signaler que le jeu est jouable
window.parent.postMessage({ type: 'cinevva:playable' }, '*')10) Considérations de sécurité
// Valider l’origine des messages pour les opérations sensibles
window.addEventListener('message', (event) => {
const trustedOrigins = [
'https://itch.io',
'https://cinevva.com',
'https://yoursite.com'
]
if (!trustedOrigins.includes(event.origin)) {
return // Ignorer les messages non fiables
}
// Traiter le message...
})
// Ne pas exposer d’opérations sensibles via postMessage
// Autoriser uniquement les commandes figurant sur la liste blanche
const allowedCommands = ['pause', 'resume', 'setVolume', 'mute']
window.addEventListener('message', (event) => {
const { type } = event.data
if (!allowedCommands.includes(type)) return
// Traiter la commande...
})Liste de vérification pour les tests
- Tests locaux : utilisez un serveur HTTP simple, et non
file:// - Origines différentes : effectuez un test avec une véritable intégration dans une iframe
- Permissions : effectuez un test avec un bac à sable restrictif
- Focus : testez le clavier après avoir cliqué en dehors de l’iframe
- Redimensionnement : testez différentes tailles d’intégration
- Mobile : testez les commandes tactiles dans un contexte d’intégration
<!-- Page de test de l’intégration -->
<!DOCTYPE html>
<html>
<body style="background: #333; padding: 20px;">
<h1 style="color: #fff;">Test d’intégration</h1>
<iframe
src="http://localhost:8000"
width="800"
height="600"
allow="fullscreen; autoplay; gamepad"
></iframe>
</body>
</html>Articles connexes
- Publier un jeu web qui se charge rapidement
- Jeux web adaptés aux appareils mobiles
- Pour les créateurs
- COOP/COEP et SharedArrayBuffer — les en-têtes interorigines qui affectent l’intégration dans une iframe
- Comment lancer votre jeu sur itch.io — itch.io intègre nativement les jeux sur navigateur dans des iframes
Ressources externes
- MDN : élément iframe — référence complète des attributs d’une iframe
- MDN : politique d’autorisations — contrôle des fonctionnalités accessibles aux iframes
- MDN : API postMessage — communication entre une iframe et sa page parente