Le Web a besoin de plus de sons (de bon goût) !
Cette bibliothèque ne fonctionne qu'avec React DOM, mais @remigallego a créé une alternative pour React Native ! Découvrez réagir-native-use-sound.
Ce projet est « semi-maintenu » ?
Je n'ai pas la bande passante pour le moment pour examiner les problèmes extrêmes ou aider au dépannage, mais je prévois de le maintenir à jour avec les versions majeures de React et de résoudre les problèmes à la fois graves et courants.
Si vous avez des idées de fonctionnalités ou si vous rencontrez des bizarreries étranges, je vous recommande vivement de bifurquer le projet et de vous l'approprier ! Cela peut sembler intimidant, mais la source n'est pas aussi complexe que celle de nombreux autres packages NPM ; Je confie tout le gros travail audio à Howler). Si vous utilisez React depuis un certain temps et que vous êtes à l'aise avec les hooks, vous devriez vous sentir comme chez vous avec le code de ce package.
Le paquet peut être ajouté en utilisant du fil :
yarn add use-sound
Ou utilisez NPM :
npm install use-sound
Version UMD disponible sur unpkg.
Si votre projet utilise TypeScript, vous devez également installer le package @types/howler
en tant que dépendance de développement.
Le didacticiel comprend de nombreuses démos, ainsi que des instructions pour rechercher et préparer des effets sonores. C'est un excellent point de départ.
Vous pouvez également consulter le livre d'histoires , qui comprend de nombreux exemples rapides.
import useSound from 'use-sound' ;
import boopSfx from '../../sounds/boop.mp3' ;
const BoopButton = ( ) => {
const [ play ] = useSound ( boopSfx ) ;
return < button onClick = { play } > Boop! < / button > ;
} ;
Cette démo ne joue le son qu'en survolant un élément. Le son s'arrête lorsque la souris quitte l'élément :
REMARQUE : De nombreux navigateurs désactivent les sons jusqu'à ce que l'utilisateur ait cliqué quelque part sur la page. Si vous n'entendez rien avec cet exemple, essayez de cliquer n'importe où et réessayez.
import useSound from 'use-sound' ;
import fanfareSfx from '../../sounds/fanfare.mp3' ;
const FanfareButton = ( ) => {
const [ play , { stop } ] = useSound ( fanfareSfx ) ;
return (
< button onMouseEnter = { ( ) => play ( ) } onMouseLeave = { ( ) => stop ( ) } >
< span role = "img" aria-label = "trumpet" >
?
< / span >
< / button >
) ;
} ;
Avec l’option playbackRate
, vous pouvez modifier la vitesse/hauteur de l’échantillon. Cet exemple joue un son et le rend 10 % plus rapide à chaque fois :
import useSound from 'use-sound' ;
import glugSfx from '../../sounds/glug.mp3' ;
export const RisingPitch = ( ) => {
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( glugSfx , {
playbackRate ,
// `interrupt` ensures that if the sound starts again before it's
// ended, it will truncate it. Otherwise, the sound can overlap.
interrupt : true ,
} ) ;
const handleClick = ( ) => {
setPlaybackRate ( playbackRate + 0.1 ) ;
play ( ) ;
} ;
return (
< Button onClick = { handleClick } >
< span role = "img" aria-label = "Person with lines near mouth" >
?
< / span >
< / Button >
) ;
} ;
useSound
nécessite un chemin d'accès à un fichier audio, et il n'est pas évident de savoir comment en fournir un dans une application React.
En utilisant create-react-app
, vous pouvez "importer" un fichier MP3. Il se résoudra en un chemin généré dynamiquement :
import someAudioFile from '../sounds/sound.mp3' ;
console . log ( someAudioFile ) ; // “/build/sounds/sound-abc123.mp3”
Si vous essayez d'appliquer cette astuce dans un autre système de build React comme Next.js, vous pouvez obtenir une erreur comme celle-ci :
Vous aurez peut-être besoin d'un chargeur approprié pour gérer ce type de fichier. Actuellement, aucun chargeur n'est configuré pour traiter ce fichier.
Le problème est que Webpack (le bundler utilisé sous le capot pour générer des bundles JS) ne sait pas comment traiter un fichier MP3.
Si vous avez accès à la configuration Webpack, vous pouvez la mettre à jour pour utiliser le chargeur de fichiers, qui créera un chemin dynamique et accessible au public vers le fichier.
Alternativement, la plupart des outils vous donneront un dossier « public » (create-react-app, Next.js) ou « statique » (Gatsby). Vous pouvez y déposer vos fichiers audio, puis utiliser un chemin de chaîne.
Les fichiers audio que vous utiliserez avec use-sound
suivent les mêmes règles que les autres éléments statiques comme les images ou les polices. Suivez les guides pour le méta-framework de votre choix :
️ Des chemins sonores asynchrones ?️ Si l'URL de votre fichier audio est chargée de manière asynchrone, vous pourriez rencontrer des problèmes. Ce n'est probablement pas le bon package pour ce cas d'utilisation.
Dans l'intérêt de l'utilisateur, les navigateurs n'autorisent pas les sites Web à produire du son tant que l'utilisateur n'a pas interagi avec eux (par exemple en cliquant sur quelque chose). Aucun son ne sera produit jusqu'à ce que l'utilisateur clique, appuie ou déclenche quelque chose.
useSound
en profite : comme nous savons que les sons ne seront pas nécessaires immédiatement au chargement, nous pouvons charger paresseusement une dépendance tierce.
useSound
ajoutera environ 1 Ko de gzip à votre bundle et récupérera de manière asynchrone un package supplémentaire après le chargement, qui enregistre environ 9 Ko de gzip.
Si l'utilisateur clique sur quelque chose qui fait du bruit avant que cette dépendance n'ait été chargée et récupérée, ce ne sera pas une opération (tout fonctionnera toujours, mais aucun effet sonore ne sera joué). D'après mon expérience, c'est extrêmement rare.
Considérez l'extrait de code suivant :
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( '/path/to/sound' , { playbackRate } ) ;
playbackRate
ne sert pas seulement de valeur initiale pour l'effet sonore. Si playbackRate
change, le son commencera immédiatement à jouer à un nouveau rythme. Cela est vrai pour toutes les options transmises au hook useSound
.
Le hook useSound
prend deux arguments :
HookOptions
)Il produit un tableau avec deux valeurs :
ExposedData
) Lorsque vous appelez la fonction pour lire le son, vous pouvez lui transmettre un ensemble d'options ( PlayOptions
).
Passons en revue chacun d'eux tour à tour.
Lorsque vous appelez useSound
, vous pouvez lui transmettre diverses options :
Nom | Valeur |
---|---|
volume | nombre |
taux de lecture | nombre |
interrompre | booléen |
sonEnabled | booléen |
lutin | SpriteMap |
[délégué] | — |
volume
est un nombre compris entre 0
et 1
, où 1
correspond au volume maximum et 0
au volume complètement coupé.playbackRate
est un nombre compris entre 0.5
et 4
. Il peut être utilisé pour ralentir ou accélérer l’échantillon. Comme pour une platine vinyle, les changements de vitesse affectent également la hauteur.interrupt
spécifie si le son doit pouvoir se "chevaucher" ou non si la fonction play
est à nouveau appelée avant la fin du son.soundEnabled
vous permet de transmettre une valeur (généralement issue du contexte ou d'un redux ou quelque chose du genre) pour couper tous les sons. Notez que cela peut être remplacé dans PlayOptions
, voir ci-dessoussprite
vous permet d'utiliser un hook useSound
unique pour plusieurs effets sonores. Voir « Sprites » ci-dessous. [delegated]
fait référence au fait que tout argument supplémentaire que vous transmettez dans HookOptions
sera transmis au constructeur Howl
. Voir « Trappes d'évacuation » ci-dessous pour plus d'informations.
play
Lorsque vous appelez le hook, vous récupérez une fonction play comme premier élément du tuple :
const [ play ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
Vous pouvez appeler cette fonction sans aucun argument lorsque vous souhaitez déclencher le son. Vous pouvez également l'appeler avec un objet PlayOptions
:
Nom | Valeur |
---|---|
identifiant | chaîne |
forceSoundEnabled | booléen |
taux de lecture | nombre |
id
est utilisé pour l’identification des sprites. Voir « Sprites » ci-dessous.forceSoundEnabled
vous permet de remplacer le booléen soundEnabled
transmis à HookOptions
. En général, vous ne voulez jamais faire ça. Seule exception que j'ai trouvée : le déclenchement d'un son sur le bouton "Mute".playbackRate
est une autre façon de définir un nouveau taux de lecture, comme dans HookOptions
. En général, vous devriez préférer le faire via HookOptions
, c'est une trappe de secours. Le hook produit un tuple avec 2 options, la fonction play et un objet ExposedData
:
const [ play , exposedData ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
Nom | Valeur |
---|---|
arrêt | fonction ((id?: chaîne) => void) |
pause | fonction ((id?: chaîne) => void) |
durée | nombre (ou nul) |
son | Hurlement (ou nul) |
stop
est une fonction que vous pouvez utiliser pour arrêter le son de manière préventive.pause
est comme stop
, sauf qu'elle peut être reprise au même point. À moins que vous sachiez que vous souhaiterez reprendre, vous devez utiliser stop
; pause
monopolise des ressources, puisqu’elle s’attend à reprendre à un moment donné.duration
est la durée de l'échantillon, en millisecondes. Il sera null
jusqu'à ce que l'échantillon soit chargé. Notez que pour les sprites, il s'agit de la longueur du fichier entier.sound
est une trappe de secours. Il vous donne accès à l'instance Howl
sous-jacente. Consultez la documentation Howler pour en savoir plus sur son utilisation. Notez que ce sera null
pendant les premiers instants après le montage du composant. Un sprite audio est un fichier audio unique contenant plusieurs échantillons. Au lieu de charger plusieurs sons individuels, vous pouvez charger un seul fichier et le découper en plusieurs sections qui peuvent être déclenchées indépendamment.
Cela peut présenter un avantage en termes de performances, car il s'agit de moins de requêtes réseau parallèles, mais cela peut également valoir la peine si un seul composant nécessite plusieurs échantillons. Voir l'histoire de Drum Machine pour un exemple.
Pour les sprites, nous devrons définir un SpriteMap
. Cela ressemble à ceci :
const spriteMap = {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ;
SpriteMap
est un objet. Les touches sont les id
des sons individuels. La valeur est un tuple (tableau de longueur fixe) avec 2 éléments :
Cette visualisation pourrait rendre les choses plus claires :
Nous pouvons transmettre notre SpriteMap comme l'une de nos HookOptions :
const [ play ] = useSound ( '/path/to/sprite.mp3' , {
sprite : {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ,
} ) ;
Pour jouer un sprite spécifique, nous transmettrons son id
lors de l'appel de la fonction play
:
< button
onClick = { ( ) => play ( { id : 'laser' } ) }
>
Howler est une bibliothèque très puissante, et nous n'avons exposé qu'une infime partie de ce qu'elle peut faire dans useSound
. Nous exposons deux trappes de secours pour vous donner plus de contrôle.
Tout d'abord, toute option non reconnue que vous transmettez à HookOptions
sera déléguée à Howl
. Vous pouvez voir la liste complète des options dans la documentation Howler. Voici un exemple de la façon dont nous pouvons utiliser onend
pour déclencher une fonction lorsque notre son arrête de jouer :
const [ play ] = useSound ( '/thing.mp3' , {
onend : ( ) => {
console . info ( 'Sound ended!' ) ;
} ,
} ) ;
Si vous avez besoin de plus de contrôle, vous devriez pouvoir utiliser directement l'objet sound
, qui est une instance de Howler.
Par exemple : Howler expose une méthode fade
, qui vous permet de faire fondre un son en entrée ou en sortie. Vous pouvez appeler cette méthode directement sur l'objet sound
:
const Arcade = ( ) => {
const [ play , { sound } ] = useSound ( '/win-theme.mp3' ) ;
return (
< button
onClick = { ( ) => {
// You win! Fade in the victory theme
sound . fade ( 0 , 1 , 1000 ) ;
} }
>
Click to win
< / button >
) ;
} ;