L'intégration de fonctionnalités de messagerie vocale dans les applications web nécesite une gestion fine de l'élément HTML5 <audio> combinée à des animations CSS. L'objectfi est de reproduire l'interface familière de WeChat : un clic lance la lecture avec une animation d'ondes sonores, et le clic sur un autre message interrompt le précédent.
Structure HTML de base
Pour maintenir une interface propre, l'élément audio est masqué techniquement tout en restant accessible via le DOM. On utilise l'attribut preload="auto" pour optimiser la réactivité au clic.
<!-- Structure d'un message vocal -->
<div class="voice-wrapper" onclick="handleVoicePlayback(this)" data-duration="5">
<span class="voice-icon"></span>
<span class="voice-duration">5"</span>
<audio class="audio-element" preload="auto" style="display:none;">
<source src="path/to/audio.mp3" type="audio/mpeg">
</audio>
</div>
Animation des ondes en CSS3
L'effet visuel de lecture est obtenu grâce aux animations de type "steps" (par étapes) sur une image sprite ou par la manipulation de barres CSS. Lorsqu'une classe spécifique (par exemple .is-playing) est ajoutée au conteneur, l'animation se déclenche.
.voice-icon {
width: 20px;
height: 20px;
background: url('icon-voice-static.png') no-repeat;
background-size: cover;
}
.is-playing .voice-icon {
animation: voicePlaySteps 1s steps(3) infinite;
background-image: url('icon-voice-animated.png');
}
@keyframes voicePlaySteps {
from { background-position: 0; }
to { background-position: -60px; }
}
Gestion des événements et API Audio
L'API HTML5 Audio fournit les méthodes essentielles play(), pause() et load(). Pour une expérience utilisateur fluide, il est crucial de détecter l'état du média via des événements comme canplaythrough ou ended.
Voici les méthodes et événements clés :
- play() : Démarre la lecture.
- pause() : Suspend la lecture.
- duration : Propriété indiquant la durée totale du fichier.
- canplay : Événement déclenché lorsque le naivgateur peut commencer la lecture.
Logique de contrôle en JavaScript
Pour assurer qu'une seule piste joue à la fois et que l'animation s'arrête précisément à la fin du son, on utilise un gestionnaire d'état global pour le minuteur.
let voiceTimer = null;
function handleVoicePlayback(container) {
const audio = container.querySelector('.audio-element');
const seconds = parseInt(container.getAttribute('data-duration'));
// 1. Réinitialisation des états précédents
resetAllPlayers();
// 2. Logique de lecture
if (audio.paused) {
audio.play();
container.classList.add('is-playing');
// 3. Synchronisation de l'animation avec la durée
voiceTimer = setTimeout(() => {
container.classList.remove('is-playing');
}, seconds * 1000);
} else {
audio.pause();
audio.currentTime = 0;
}
}
function resetAllPlayers() {
// Nettoyage du timer global
if (voiceTimer) {
clearTimeout(voiceTimer);
}
// Arrêt de tous les audios et retrait des classes CSS
const players = document.querySelectorAll('.voice-wrapper');
players.forEach(p => {
const a = p.querySelector('audio');
a.pause();
a.currentTime = 0;
p.classList.remove('is-playing');
});
}
Optimisation avec les métadonnées
Au lieu de se baser uniquement sur un attribut data-duration statique, on peut utiliser l'événement onloadedmetadata pour récupérer la durée réelle du fichier audio et ajuster l'interface dynamiquement.
audio.onloadedmetadata = function() {
console.log("Durée réelle : " + audio.duration + " secondes");
// Mise à jour de l'affichage si nécessaire
};
L'utilisation de clearTimeout est ici indispensable. Sans cela, si un utilisateur clique rapidement sur plusieurs messages vocaux, les fonctions de rappel s'empileront, provoquant l'arrêt prématuré ou désordonné des animations visuelles.