use-sound

use-sound

React音效钩子 为网页添加简单交互音效

use-sound是一个为React设计的音效钩子库。它提供声明式API,体积仅1kb(gzip后),使用TypeScript构建,基于Howler.js音频工具。开发者可通过use-sound为网页添加点击播放、悬停音效、音高调节等交互音效,适用于多种React项目场景,有助于提升用户体验。

useSoundReact Hook声音播放Howler.jsGithub开源项目音效

useSound

一个用于音效的 React Hook

网页需要更多(恰到好处的)声音!

  • 👂 让你的网站能够通过2种人类感官而不是1种来进行交流
  • 🔥 声明式 Hooks API
  • ⚡️ 在你的打包文件中只有<1kb大小(gzip压缩后)! ~10kb异步加载。
  • ✨ 使用 Typescript 构建
  • 🗣 使用强大、经过实战检验的音频工具库: Howler.js

最小化文件大小 许可证: MIT NPM 版本 行为准则

这个库只适用于 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 的所有选项都是如此。


API 文档

useSound hook 接受两个参数:

  • 要加载的声音的 URL
  • 一个配置对象 (HookOptions)

它返回一个包含两个值的数组:

  • 一个你可以调用来触发声音的函数
  • 一个包含额外数据和控制的对象 (ExposedData)

当调用函数来播放声音时,你可以传递一组选项 (PlayOptions)。

让我们依次了解每一个。

HookOptions

调用 useSound 时,你可以传递各种选项:

名称
volumenumber
playbackRatenumber
interruptboolean
soundEnabledboolean
spriteSpriteMap
[delegated]
  • volume 是一个从 01 的数字,其中 1 是全音量,0 是完全静音。
  • playbackRate 是一个从 0.54 的数字。它可以用来减慢或加快样本。像唱片机一样,速度的变化也会影响音调。
  • interrupt 指定如果在声音结束前再次调用 play 函数,声音是否应该能够"重叠"。
  • soundEnabled 允许你传递一个值(通常来自 context 或 redux 或其他地方)来静音所有声音。注意,这可以在 PlayOptions 中被覆盖,见下文。
  • sprite 允许你为多个音效使用单个 useSound hook。参见下面的"Sprites"

[delegated] 指的是你在 HookOptions 中传递的任何额外参数都将被转发到 Howl 构造函数。有关更多信息,请参见下面的"Escape hatches"。

play 函数

当调用 hook 时,你会在元组的第一项中得到一个 play 函数:

const [play] = useSound('/meow.mp3'); // ^ 我们正在讨论的内容

当你想触发声音时,你可以不带任何参数调用这个函数。你也可以用一个 PlayOptions 对象来调用它:

名称
idstring
forceSoundEnabledboolean
playbackRatenumber
  • id 用于精灵标识。详见下面的"精灵"部分。
  • forceSoundEnabled 允许你覆盖传递给 HookOptionssoundEnabled 布尔值。通常你不会想这样做。我发现唯一的例外是:在"静音"按钮上触发声音。
  • playbackRate 是另一种设置新播放速率的方法,与 HookOptions 中的相同。一般来说,你应该优先通过 HookOptions 来设置,这只是一个后备选项。

ExposedData

该钩子产生一个包含两个选项的元组,即播放函数和一个 ExposedData 对象:

const [play, exposedData] = useSound('/meow.mp3'); // ^ 我们正在讨论的内容
名称
stop函数 ((id?: string) => void)
pause函数 ((id?: string) => void)
duration数字 (或 null)
soundHowl (或 null)
  • stop 是一个可以用来提前停止声音的函数。
  • pause 类似于 stop,但可以从同一点恢复。除非你知道要恢复,否则应该使用 stoppause 会占用资源,因为它预期在某个时候会恢复。
  • 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> ); };

编辑推荐精选

Keevx

Keevx

AI数字人视频创作平台

Keevx 一款开箱即用的AI数字人视频创作平台,广泛适用于电商广告、企业培训与社媒宣传,让全球企业与个人创作者无需拍摄剪辑,就能快速生成多语言、高质量的专业视频。

即梦AI

即梦AI

一站式AI创作平台

提供 AI 驱动的图片、视频生成及数字人等功能,助力创意创作

扣子-AI办公

扣子-AI办公

AI办公助手,复杂任务高效处理

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
蛙蛙写作

蛙蛙写作

AI小说写作助手,一站式润色、改写、扩写

蛙蛙写作—国内先进的AI写作平台,涵盖小说、学术、社交媒体等多场景。提供续写、改写、润色等功能,助力创作者高效优化写作流程。界面简洁,功能全面,适合各类写作者提升内容品质和工作效率。

AI辅助写作AI工具蛙蛙写作AI写作工具学术助手办公助手营销助手AI助手
问小白

问小白

全能AI智能助手,随时解答生活与工作的多样问题

问小白,由元石科技研发的AI智能助手,快速准确地解答各种生活和工作问题,包括但不限于搜索、规划和社交互动,帮助用户在日常生活中提高效率,轻松管理个人事务。

热门AI助手AI对话AI工具聊天机器人
Transly

Transly

实时语音翻译/同声传译工具

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

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

下拉加载更多