Skip to content

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 :

html
<!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

js
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 :

html
<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 :

js
// 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" :

js
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 :

js
// 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 :

js
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 :

js
// 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 :

js
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 :

js
// 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 :

js
// Aucun SDK requis, mais postMessage peut servir pour les succès
window.parent.postMessage({ type: 'itch-achievement', data: { id: 'first-win' }}, '*')

Newgrounds :

js
// Inclure le SDK Newgrounds.io
const ngio = new Newgrounds.io.core('APP_ID', 'AES_KEY')
ngio.callComponent('Medal.unlock', { id: 12345 })

Cinevva :

js
// 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é

js
// 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
html
<!-- 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

Ressources externes