网上都在说仿一个网易云什么的,能不能高级一点点(因为听不了JAY 的歌啊)!!!在本文中,我们将使用 React 和 ts-audio
仿造流行音乐流媒体服务 Spotify 构建一个类似的音乐播放器。让你构建一个音乐播放器简单又快速!
因为 Spotify 未提供公共的音乐资源 API,所以我们将会使用一组虚拟数据。 话不多说,我们开始~
什么是 ts-audio?
ts-audio 是一个能够使 AudioContext
API 更易于交互的第三方库。 它能为开发者提供播放、暂停等方法,并允许你创建播放列表。 总的来说,ts-audio 提供以下功能与特征:
- 一个简单的 API,它抽象了 AudioContext API 的复杂性
- 提供跨浏览器支持
- 轻松创建音频播放列表
- 适用于任何能够编译成 JavaScript 的语言
开始使用 ts-audio 构建
让我们首先使用以下命令创建一个新的 React 应用程序:
代码语言:javascript复制npx create-react-app ts-audio
// or
yarn create react-app ts-audio
接下来,我们安装 ts-audio
包:
yarn add ts-audio
ts-audio 有两个核心组件,Audio
和 AudioPlaylist
。
使用 Audio 组件
Audio 组件允许我们传入要播放的一首歌曲。 它还为我们提供了某些方法,例如 play()、pause()、stop()
等等。打开 App.js
填入如下内容:
// App.js
import Audio from 'ts-audio';
import GreatestWorkOfArt from './music/GreatestWorkOfArt.mp3';
export default function App() {
const audio = Audio({
file: GreatestWorkOfArt
})
const play = () => {
audio.play()
}
const pause = () => {
audio.pause()
}
const stop = () => {
audio.stop()
}
return (
<>
<button onClick={play}>Play</button>
<button onClick={pause}>Pause</button>
<button onClick={stop}>Stop</button>
</>
)
}
在上面的代码块中,我们从 ts-audio
导入了 Audio
组件和我们想要播放的歌曲——最伟大的作品(此处必须拥有名字)。 接着,我们创建了一个音频实例 audio,将其设置为导入的 Audio 组件,然后将导入的音乐传递给 Audio 元素暴露出来的 file
参数。
后面的代码中,我们利用了 ts-audio 提供给的方法,比如 play() 和 pause()
,通过按钮上绑定的点击事件函数调用它们。
使用 AudioPlaylist 组件
AudioPlaylist
组件允许我们传入多首歌曲,但它们必须在一个数组中,否则 ts-audio 不会播放它们。 AudioPlaylist 组件为我们提供了 play()、pause()、stop()、next() 和 prev()
等方法。
下面的代码块解释了如何使用 AudioPlaylist 组件:
代码语言:javascript复制// App.js
import { AudioPlaylist } from 'ts-audio';
import GreatestWorkOfArt from './music/GreatestWorkOfArt.mp3';
import Mojito from './music/Mojito.mp3';
export default function App() {
const playlist = AudioPlaylist({
files: [GreatestWorkOfArt, Mojito]
})
const play = () => {
playlist.play()
}
const pause = () => {
playlist.pause()
}
const next = () => {
playlist.next()
}
const previous = () => {
playlist.prev()
}
const stop = () => {
playlist.stop()
}
return (
<>
<button onClick={play}>Play</button>
<button onClick={pause}>Pause</button>
<button onClick={next}>Next</button>
<button onClick={prev}>Prev</button>
<button onClick={stop}>Stop</button>
</>
)
}
除了播放多首歌曲之前,我们的音乐播放器还应该具备以下功能:
- 每当我们单击下一个或上一个时,会将艺术家更改为当前歌曲的艺术家
- 将图像更改为当前歌曲的图像
- 将歌曲名称更改为当前歌曲
接下来,让我们来实现上述功能。
在 src
文件夹中,分别创建两个名为 images
和 music
的文件夹。 导航到 images
文件夹并粘贴你可能需要的任何照片。 同样,在 music
文件夹中,你可以粘贴要使用的任何音频文件。
接下来,将歌曲和图片导入到 App.js
中,如下所示:
import { AudioPlaylist } from 'ts-audio';
// Music import
import Intro from './music/Intro.mp3';
import GreatestWorkOfArt from './music/GreatestWorkOfArt.mp3';
import Mojito from './music/Mojito.mp3';
import StillWandering from './music/StillWandering.mp3';
import DontCry from './music/Say goodbye not to cry.mp3';
import PinkOcean from './music/Pink ocean.mp3';
import Reflection from './music/Reflection.mp3';
// Pictures import
import picture1 from './images/picture1.jpeg';
import picture2 from './images/picture2.jpeg';
import picture3 from './images/picture3.jpeg';
import picture4 from './images/picture4.jpeg';
import picture5 from './images/picture5.jpeg';
import picture6 from './images/picture6.jpeg';
import picture7 from './images/picture7.jpeg';
export default function App() {
const songs = [
{
title: 'Intro,
artist: 'Jay',
img_src: picture1,
src: Intro,
},
{
title: '最伟大的作品',
artist: 'Jay',
img_src: picture2,
src: GreatestWorkOfArt,
},
{
title: 'Mojito',
artist: 'Jay',
img_src: picture3,
src: Mojito,
},
{
title: '还在流浪',
artist: 'Jay',
img_src: picture4,
src: StillWandering,
},
{
title: '说好不哭',
artist: 'Jay',
img_src: picture5,
src: DontCry,
},
{
title: '粉色海洋',
artist: 'Jay',
img_src: picture6,
src: PinkOcean,
},
{
title: '倒影',
artist: 'Jay',
img_src: picture7,
src: Reflection,
},
]
const playlist = AudioPlaylist({
files: songs.map((song) => song.src),
});
const handlePlay = () => {
playlist.play();
};
const handlePause = () => {
playlist.pause();
};
const handleSkip = () => {
playlist.next();
};
const handlePrevious = () => {
playlist.prev();
};
return (
<>
<button onClick={handlePlay}>Play</button>
<button onClick={handlePause}>Pause</button>
<button onClick={handleSkip}>Next</button>
<button onClick={handlePrevious}>Prev</button>
</>
);
}
在上面的代码块中,我们导入了歌曲和图像。 接下来,我们创建了一个包含对象的歌曲数组。 每个对象都有一个标题、艺术家、导入图像的 img_src
和导入歌曲的 src
。
之后,我们通过歌曲数组映射到歌曲的 src,我们将其传递给 files
参数。 请记住,我们必须将它作为一个数组传入,然后 map()
方法通过调用一个函数来生成一个新的数组。
我们还创建了我们的方法并将它们传递给各种按钮。 我们将创建一个 Player.js
文件来处理按钮的逻辑,用于处理 App.js 中的功能:
// Player.js
export default function Player({ play, pause, next, prev }) {
return (
<div className="c-player--controls">
<button onClick={play}>Play</button>
<button onClick={pause}>Pause</button>
<button onClick={next}>Next</button>
<button onClick={prev}>Previous</button>
</div>
);
}
在上面的代码块中,我们创建了一个 Player.js 文件,然后捕获来自 App.js 的 props
,最后将它们传递给按钮。
创建功能
为了为我们的应用程序创建功能,我们导入 useState 来获取歌曲的当前索引。 然后我们将图像设置为当前照片,将艺术家设置为当前艺术家,将标题设置为当前标题:
代码语言:javascript复制// App.js
import React, { useState } from 'react';
import Player from './Player';
import { AudioPlaylist } from 'ts-audio';
// Music import
// Pictures import
export default function App() {
const [currentSong, setCurrentSong] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
// Songs Array
const playlist =AudioPlaylist({
files: songs.map((song) => song.src),
});
const handlePlay = () => {
playlist.play();
setIsPlaying(true);
};
const handlePause = () => {
playlist.pause();
setIsPlaying(false);
};
const handleSkip = () => {
playlist.next();
setIsPlaying(true);
setCurrentSong(
(currentSong) => (currentSong 1 songs.length) % songs.length
);
};
const handlePrevious = () => {
playlist.prev();
setIsPlaying(true);
setCurrentSong(
(currentSong) => (currentSong - 1 songs.length) % songs.length
);
};
return (
<>
<div className="App">
<div className="c-player">
<div className="c-player--details">
{' '}
<div className="details-img">
{' '}
<img src={songs[currentSong].img_src} alt="img" />
</div>
<h1 className="details-title">{songs[currentSong].title}</h1>
<h2 className="details-artist">{songs[currentSong].artist}</h2>
</div>
<Player
play={handlePlay}
pause={handlePause}
isPlaying={isPlaying}
setIsPlaying={setIsPlaying}
next={handleSkip}
prev={handlePrevious}
/>
</div>
</div>
</>
);
}
我们创建了一个 currentSong
状态并将其初始值设置为 0。 当我们单击下一个按钮时,我们将按照如下公式设置 currentSong
状态的值:
currentSong 1 songs.length) % songs.length
当我们单击上一个按钮时,我们将按照如下公式设置 currentSong
状态的值:
currentSong - 1 songs.length) % songs.length
我们还创建了一个 isPlaying
状态来检查歌曲是否正在播放,然后我们将它作为 props 传递给 Player 组件。
最后,我们处理了更改图像、艺术家和歌曲标题的功能。
当我们启动应用程序时,似乎一切正常; 单击“下一步”按钮时图像会发生变化。
但是问题来了,播放的歌曲与屏幕上显示的图片和艺术家姓名不匹配。 有时,同时播放两首或多首歌曲。
下面我们来解决问题。
解决问题
当我们单击下一个或上一个按钮时,我们正在重新计算值并导致重新渲染。
为了阻止这种情况,我们将歌曲数组和创建的播放列表实例包装在 useMemo
Hook 中,如下所示:
// App.js
import React, { useState, useMemo } from 'react';
import Player from './Player';
import { AudioPlaylist } from 'ts-audio';
// Music import
// Pictures import
export default function App() {
const [currentSong, setCurrentSong] = useState(0);
const songs = useMemo(
() => [
{
title: 'Intro,
artist: 'Jay',
img_src: picture1,
src: Intro,
},
{
title: '最伟大的作品',
artist: 'Jay',
img_src: picture2,
src: GreatestWorkOfArt,
},
{
title: 'Mojito',
artist: 'Jay',
img_src: picture3,
src: Mojito,
},
{
title: '还在流浪',
artist: 'Jay',
img_src: picture4,
src: StillWandering,
},
{
title: '说好不哭',
artist: 'Jay',
img_src: picture5,
src: DontCry,
},
{
title: '粉色海洋',
artist: 'Jay',
img_src: picture6,
src: PinkOcean,
},
{
title: '倒影',
artist: 'Jay',
img_src: picture7,
src: Reflection,
},
],
[]
);
const playlist = useMemo(() => {
return AudioPlaylist({
files: songs.map((song) => song.src),
});
}, [songs]);
useMemo
Hook 能有效地缓存该值,因此它不需要重新计算,因此不会导致重新渲染。
添加样式
在本文中,我们会使用 Font Awesome Icons 中提供的图标来美化我们的 UI。 你可以使用以下命令安装 Font Awesome
包:
yarn add @fortawesome/fontawesome-svg-core
yarn add @fortawesome/free-solid-svg-icons
yarn add @fortawesome/react-fontawesome
将以下代码复制并粘贴到 Player.js
文件中:
// Player.js
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlay, faPause, faForward, faBackward } from '@fortawesome/free-solid-svg-icons';
export default function Player({ play, pause, next, prev, isPlaying, setIsPlaying }) {
return (
<div className="c-player--controls">
<button className="skip-btn" onClick={prev}>
<FontAwesomeIcon icon={faBackward} />
</button>
<button
className="play-btn"
onClick={() => setIsPlaying(!isPlaying ? play : pause)}
>
<FontAwesomeIcon icon={isPlaying ? faPause : faPlay} />
</button>
<button className="skip-btn" onClick={next}>
<FontAwesomeIcon icon={faForward} />
</button>
</div>
);
}
在上面的代码块中,我们从 App.js 文件中获取 props,然后在 Player.js 文件中处理它们。 对于样式,请将以下代码复制并粘贴到你的 index.css
文件中:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Fira Sans', sans-serif;
}
body {
background-color: #ddd;
}
.App {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
max-width: 100vw;
}
.c-player {
display: block;
background-color: #0a54aa;
max-width: 400px;
display: block;
margin: 0px auto;
padding: 50px;
border-radius: 16px;
box-shadow: inset -6px -6px 12px rgba(0, 0, 0, 0.8),
inset 6px 6px 12px rgba(255, 255, 255, 0.4);
}
.c-player > h4 {
color: #fff;
font-size: 14px;
text-transform: uppercase;
font-weight: 500;
text-align: center;
}
.c-player > p {
color: #aaa;
font-size: 14px;
text-align: center;
font-weight: 600;
}
.c-player > p span {
font-weight: 400;
}
.c-player--details .details-img {
position: relative;
width: fit-content;
margin: 0 auto;
}
.c-player--details .details-img img {
display: block;
margin: 50px auto;
width: 100%;
max-width: 250px;
border-radius: 50%;
box-shadow: 6px 6px 12px rgba(0, 0, 0, 0.8),
-6px -6px 12px rgba(255, 255, 255, 0.4);
}
.c-player--details .details-img:after {
content: '';
display: block;
position: absolute;
top: -25px;
left: -25px;
right: -25px;
bottom: -25px;
border-radius: 50%;
border: 3px dashed rgb(255, 0, 0);
}
.c-player--details .details-title {
color: #eee;
font-size: 28px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8),
-2px -2px 4px rgba(255, 255, 255, 0.4);
text-align: center;
margin-bottom: 10px;
}
.c-player--details .details-artist {
color: #aaa;
font-size: 20px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8),
-2px -2px 4px rgba(255, 255, 255, 0.4);
text-align: center;
margin-bottom: 20px;
}
.c-player--controls {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30px;
}
.c-player--controls .play-btn {
display: flex;
margin: 0 30px;
padding: 20px;
border-radius: 50%;
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.8),
-4px -4px 10px rgba(255, 255, 255, 0.4),
inset -4px -4px 10px rgba(0, 0, 0, 0.4),
inset 4px 4px 10px rgba(255, 255, 255, 0.4);
border: none;
outline: none;
background-color: #ff0000;
color: #fff;
font-size: 24px;
cursor: pointer;
}
.c-player--controls .skip-btn {
background: none;
border: none;
outline: none;
cursor: pointer;
color: rgb(77, 148, 59);
font-size: 18px;
}
现在,一个音乐播放器大功告成啦~快去试着添加你喜欢的歌曲试一试吧
结尾
在本文中,我们了解了 ts-audio 的方法以及它是如何让处理音频文件变得更容易。 最后,我们学习了如何使用 ts-audio 结合 React构建一个可用的音乐播放器。