网络需要更多(有品味的)声音!
该库仅适用于 React DOM,但 @remigallego 创建了 React Native 的替代方案!查看react-native-use-sound。
这个项目是“半维护”的?
我现在没有足够的资源来研究边缘问题或帮助排除故障,但我计划使其与主要 React 版本保持同步,并修复既严重又常见的问题。
如果您对功能有想法,或者遇到奇怪的怪癖,我强烈建议您分叉该项目并将其变成您自己的!它可能看起来令人生畏,但源代码并不像许多其他 NPM 包那么复杂;我把所有硬音频工作都交给了 Howler)。如果您已经使用 React 一段时间并且熟悉 hooks,那么您应该对这个包的代码感到熟悉。
可以使用yarn添加包:
yarn add use-sound
或者,使用 NPM:
npm install use-sound
UMD 构建可在 unpkg 上使用。
如果您的项目使用 TypeScript,您还应该安装@types/howler
包作为开发依赖项。
该教程包括许多演示,以及查找和准备声音效果的说明。这是一个很好的起点。
您还可以查看故事书,其中包含许多快速示例。
import useSound from 'use-sound' ;
import boopSfx from '../../sounds/boop.mp3' ;
const BoopButton = ( ) => {
const [ play ] = useSound ( boopSfx ) ;
return < button onClick = { play } > Boop! < / button > ;
} ;
该演示仅在将鼠标悬停在元素上时播放声音。当鼠标离开元素时声音暂停:
注意:许多浏览器会禁用声音,直到用户单击页面上的某个位置。如果在此示例中您没有听到任何声音,请尝试单击任意位置并重试。
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 >
) ;
} ;
使用playbackRate
选项,您可以更改样本的速度/音高。此示例播放声音并使其每次速度加快 10%:
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
需要音频文件的路径,但如何在 React 应用程序中提供该路径并不明显。
使用create-react-app
,您可以“导入”MP3 文件。它将解析为动态生成的路径:
import someAudioFile from '../sounds/sound.mp3' ;
console . log ( someAudioFile ) ; // “/build/sounds/sound-abc123.mp3”
如果您尝试在另一个 React 构建系统(例如 Next.js)中使用此技巧,您可能会收到如下错误:
您可能需要适当的加载程序来处理此文件类型,目前没有配置加载程序来处理此文件。
问题在于 Webpack(底层用于生成 JS 包的捆绑器)不知道如何处理 MP3 文件。
如果您有权访问 Webpack 配置,则可以将其更新为使用 file-loader,这将创建一个动态的、可公开访问的文件路径。
或者,大多数工具都会为您提供一个“公共”(create-react-app、Next.js)或“静态”(Gatsby)文件夹。您可以将音频文件放入其中,然后使用字符串路径。
您将与use-sound
一起使用的声音文件遵循与其他静态资源(例如图像或字体)相同的规则。请遵循您选择的元框架的指南:
️ 异步声音路径?️ 如果音频文件的 URL 是异步加载的,您可能会遇到一些问题。这可能不是适合该用例的包。
为了用户的利益,浏览器不允许网站产生声音,直到用户与它们交互(例如,通过单击某物)。在用户单击、点击或触发某些操作之前,不会产生任何声音。
useSound
利用了这一点:因为我们知道加载时不会立即需要声音,所以我们可以延迟加载第三方依赖项。
useSound
会将大约 1kb gzip 添加到您的包中,并在加载后异步获取一个额外的包,该包的大小约为 9kb gzip。
如果用户在加载和获取此依赖项之前确实点击了发出噪音的东西,则这将是无操作(一切仍然有效,但不会播放任何声音效果)。根据我的经验,这种情况非常罕见。
考虑以下代码片段:
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( '/path/to/sound' , { playbackRate } ) ;
playbackRate
不仅仅充当音效的初始值。如果playbackRate
发生变化,声音将立即开始以新的速率播放。对于传递给useSound
挂钩的所有选项都是如此。
useSound
挂钩有两个参数:
HookOptions
)它生成一个包含两个值的数组:
ExposedData
)当调用该函数播放声音时,您可以向其传递一组选项( PlayOptions
)。
让我们依次逐一讨论。
调用useSound
时,您可以向其传递多种选项:
姓名 | 价值 |
---|---|
体积 | 数字 |
播放速率 | 数字 |
打断 | 布尔值 |
声音启用 | 布尔值 |
精灵 | 雪碧图 |
[委托] | — |
volume
是从0
到1
数字,其中1
是最大音量, 0
是完全静音。playbackRate
是一个从0.5
到4
数字。它可用于减慢或加速样品。与转盘一样,速度的变化也会影响音高。interrupt
指定如果在声音结束之前再次调用play
函数,声音是否应该能够“重叠”。soundEnabled
允许您传递一个值(通常来自上下文或 redux 等)来静音所有声音。请注意,这可以在PlayOptions
中覆盖,请参见下文sprite
允许您使用单个useSound
挂钩来实现多种声音效果。请参阅下面的“精灵”。 [delegated]
指的是您在HookOptions
中传递的任何附加参数都将被转发到Howl
构造函数。有关详细信息,请参阅下面的“逃生舱口”。
play
功能调用钩子时,您将返回一个播放函数作为元组中的第一项:
const [ play ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
当您想触发声音时,可以不带任何参数调用此函数。您还可以使用PlayOptions
对象来调用它:
姓名 | 价值 |
---|---|
ID | 细绳 |
强制声音启用 | 布尔值 |
播放速率 | 数字 |
id
用于精灵识别。请参阅下面的“精灵”。forceSoundEnabled
允许您覆盖传递给HookOptions
的soundEnabled
布尔值。你通常不想这样做。我发现的唯一例外:在“静音”按钮上触发声音。playbackRate
是设置新播放速率的另一种方式,与HookOptions
中相同。一般来说,您应该更喜欢通过HookOptions
来完成此操作,这是一个逃生舱口。该钩子生成一个包含 2 个选项的元组,即 play 函数和ExposedData
对象:
const [ play , exposedData ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
姓名 | 价值 |
---|---|
停止 | 函数 ((id?: string) => void) |
暂停 | 函数 ((id?: string) => void) |
期间 | 数字(或空) |
声音 | 嚎叫(或空) |
stop
是一个可用于预先停止声音的功能。pause
类似于stop
,只不过它可以从同一点恢复。除非您知道要恢复,否则应该使用stop
; pause
占用资源,因为它预计会在某个时刻恢复。duration
是样本的长度,以毫秒为单位。在加载样本之前它将为null
。请注意,对于精灵来说,它是整个文件的长度。sound
是逃生口。它允许您访问底层的Howl
实例。请参阅 Howler 文档以了解有关如何使用它的更多信息。请注意,在组件安装后的最初几分钟内,该值将为null
。 音频精灵是包含多个样本的单个音频文件。您可以加载单个文件并将其分成多个可以独立触发的部分,而不是加载许多单独的声音。
这样做可能会带来性能优势,因为它的并行网络请求较少,但如果单个组件需要多个样本,那么这样做也是值得的。有关示例,请参阅鼓机故事。
对于精灵,我们需要定义一个SpriteMap
。它看起来像这样:
const spriteMap = {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ;
SpriteMap
是一个对象。键是各个声音的id
。该值是一个包含 2 个项目的元组(固定长度的数组):
这个可视化可能会更清楚:
我们可以将 SpriteMap 作为 HookOptions 之一传递:
const [ play ] = useSound ( '/path/to/sprite.mp3' , {
sprite : {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ,
} ) ;
要播放特定的精灵,我们将在调用play
函数时传递它的id
:
< button
onClick = { ( ) => play ( { id : 'laser' } ) }
>
Howler 是一个非常强大的库,我们只公开了它在useSound
中的一小部分功能。我们暴露了两个逃生舱口,以便您更好地控制。
首先,您传递给HookOptions
任何无法识别的选项都将被委托给Howl
。您可以在 Howler 文档中查看完整的选项列表。以下是当声音停止播放时如何使用onend
触发函数的示例:
const [ play ] = useSound ( '/thing.mp3' , {
onend : ( ) => {
console . info ( 'Sound ended!' ) ;
} ,
} ) ;
如果您需要更多控制,您应该能够直接使用sound
对象,它是 Howler 的一个实例。
例如:Howler 公开了一个fade
方法,可让您淡入或淡出声音。您可以直接在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 >
) ;
} ;