Project Icon

use-sound

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

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

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>
  );
};
项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

吐司

探索Tensor.Art平台的独特AI模型,免费访问各种图像生成与AI训练工具,从Stable Diffusion等基础模型开始,轻松实现创新图像生成。体验前沿的AI技术,推动个人和企业的创新发展。

Project Cover

SubCat字幕猫

SubCat字幕猫APP是一款创新的视频播放器,它将改变您观看视频的方式!SubCat结合了先进的人工智能技术,为您提供即时视频字幕翻译,无论是本地视频还是网络流媒体,让您轻松享受各种语言的内容。

Project Cover

美间AI

美间AI创意设计平台,利用前沿AI技术,为设计师和营销人员提供一站式设计解决方案。从智能海报到3D效果图,再到文案生成,美间让创意设计更简单、更高效。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号