网页需要更多(恰到好处的)声音!
这个库只适用于 React DOM,但 @remigallego 为 React Native 创建了一个替代方案!查看 react-native-use-sound。
这个项目处于"半维护"状态 😅
我目前没有精力去研究边缘情况的问题或帮助排查问题,但我计划随着 React 的主要版本更新来保持项目的更新,并修复那些既严重又常见的问题。
如果你有功能想法,或遇到奇怪的问题,我强烈建议你 fork 这个项目并使其成为你自己的!这可能看起来有点吓人,但源代码并不像其他许多 NPM 包那样复杂;我将所有困难的音频工作都交给了 Howler。如果你已经使用 React 一段时间并且熟悉 hooks,你应该会对这个包的代码感到很熟悉。
可以使用 yarn 添加包:
yarn add use-sound
或者使用 NPM:
npm install use-sound
UMD 构建可在 unpkg 上获得。
如果你的项目使用 TypeScript,你还应该将 @types/howler 包作为开发依赖项安装。
教程 包含了许多演示,以及寻找和准备音效的说明。这是一个很好的起点。
你还可以 查看 storybook,其中包含了许多快速示例。
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` 确保如果声音在结束前再次开始, // 它将被截断。否则,声音可能会重叠。 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,这将创建一个动态的、可公开访问的文件路径。
另外,大多数工具都会给你一个"public"(create-react-app, Next.js)或"static"(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 hook 的所有选项都是如此。
useSound hook 接受两个参数:
HookOptions)它返回一个包含两个值的数组:
ExposedData)当调用函数来播放声音时,你可以传递一组选项 (PlayOptions)。
让我们依次了解每一个。
调用 useSound 时,你可以传递各种选项:
| 名称 | 值 |
|---|---|
| volume | number |
| playbackRate | number |
| interrupt | boolean |
| soundEnabled | boolean |
| sprite | SpriteMap |
| [delegated] | — |
volume 是一个从 0 到 1 的数字,其中 1 是全音量,0 是完全静音。playbackRate 是一个从 0.5 到 4 的数字。它可以用来减慢或加快样本。像唱片机一样,速度的变化也会影响音调。interrupt 指定如果在声音结束前再次调用 play 函数,声音是否应该能够"重叠"。soundEnabled 允许你传递一个值(通常来自 context 或 redux 或其他地方)来静音所有声音。注意,这可以在 PlayOptions 中被覆盖,见下文。sprite 允许你为多个音效使用单个 useSound hook。参见下面的"Sprites"。[delegated] 指的是你在 HookOptions 中传递的任何额外参数都将被转发到 Howl 构造函数。有关更多信息,请参见下面的"Escape hatches"。
play 函数当调用 hook 时,你会在元组的第一项中得到一个 play 函数:
const [play] = useSound('/meow.mp3'); // ^ 我们正在讨论的内容
当你想触发声音时,你可以不带任何参数调用这个函数。你也可以用一个 PlayOptions 对象来调用它:
| 名称 | 值 |
|---|---|
| id | string |
| forceSoundEnabled | boolean |
| playbackRate | number |
id 用于精灵标识。详见下面的"精灵"部分。forceSoundEnabled 允许你覆盖传递给 HookOptions 的 soundEnabled 布尔值。通常你不会想这样做。我发现唯一的例外是:在"静音"按钮上触发声音。playbackRate 是另一种设置新播放速率的方法,与 HookOptions 中的相同。一般来说,你应该优先通过 HookOptions 来设置,这只是一个后备选项。该钩子产生一个包含两个选项的元组,即播放函数和一个 ExposedData 对象:
const [play, exposedData] = useSound('/meow.mp3'); // ^ 我们正在讨论的内容
| 名称 | 值 |
|---|---|
| stop | 函数 ((id?: string) => void) |
| pause | 函数 ((id?: string) => void) |
| duration | 数字 (或 null) |
| sound | Howl (或 null) |
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 对象,它是 Howler 的一个实例。
例如:Howler 暴露了一个 fade 方法,让你可以淡入或淡出声音。你可以直接在 sound 对象上调用这个方法:
const Arcade = () => { const [play, { sound }] = useSound('/win-theme.mp3'); return ( <button onClick={() => { // 你赢了!淡入胜利 主题 sound.fade(0, 1, 1000); }} > 点击获胜 </button> ); };


稳定高效的流量提升解决方案,助力品牌曝光
稳定高效的流量提升解决方案,助力品牌曝光


最新版Sora2模型免费使用,一键生成无水印视频
最新版Sora2模型免费使用,一键生成无水印视频


实时语音翻译/同声传译工具
Transly是一个多场景的AI大语言模型驱动的同声传译、专业翻译助手,它拥有超精准的音频识别翻译能力,几乎零延迟的使用体验和支持多国语言可以让你带它走遍全球,无论你是留学生、商务人士、韩剧美剧爱好者,还是出国游玩、多国会议、跨国追星等等,都可以满足你所有需要同传的场景需求,线上线下通用,扫除语言障碍,让全世界的语言交流不再有国界。


选题、配图、成文,一站式创作,让内容运营更高效
讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。