Автор: Фортуна Икечи✏️
Музыкальные плееры – это устройства или приложения, которые позволяют прослушивать аудиофайлы и записи. Существует множество музыкальных плееров, но в этой статье мы создадим клон популярного сервиса потокового воспроизведения музыки Spotify с помощью React и ts-audio.
Можно было бы ожидать, что в этом руководстве будет использоваться API Spotify, однако Spotify и другие музыкальные базы данных не предоставляют в теле ответа ссылку или URL для потокового воспроизведения. API Spotify предоставляет URL для предварительного просмотра, но продолжительность песен ограничена всего 30 секундами, а этого недостаточно для нашего примера. Поэтому мы не будем использовать API Spotify или делать какие-либо запросы к каким-либо музыкальным API или базам данных.
Вместо этого мы будем работать с фиктивными данными, состоящими из песен и изображений. Однако если вам попадется API с потоковой ссылкой, вы также можете применить методы, использованные в этой статье. Полный код этого руководства вы можете найти в репозитории GitHub. Давайте приступим!
- Что такое ts-audio?
- Создание клона Spotify с помощью ts-audio
- Использование компонента
Audio
- Использование компонента
AudioPlaylist
- Создание функциональных возможностей
- Использование компонента
- Решение проблем: Несоответствие деталей песни
- Добавление стиля
Что такое ts-audio?
ts-audio – это агностическая библиотека, которая упрощает взаимодействие с API AudioContext
. ts-audio предоставляет вам такие методы, как воспроизведение, пауза и другие, а также позволяет создавать списки воспроизведения. ts-audio предлагает следующие возможности:
- Включает простой API, абстрагирующий сложность API
AudioContext
. - Предлагает кросс-браузерную поддержку
- упрощает создание аудио плейлиста
- Работает с любым языком, который компилируется в JavaScript
Создание клона Spotify с помощью ts-audio
Давайте начнем с создания нового приложения React с помощью команды ниже:
npx create-react-app ts-audio
Если вы используете Yarn, выполните следующую команду:
yarn create react-app ts-audio
В остальной части руководства я буду использовать Yarn. Далее мы установим пакет ts-audio следующим образом:
yarn add ts-audio
По своей сути ts-audio состоит из двух компонентов, Audio
и AudioPlaylist
. Компоненты представляют собой функции, которые мы можем вызывать с определенными параметрами.
Использование компонента Audio
Компонент Audio
позволяет нам передать одну песню для воспроизведения. Он также предоставляет нам определенные методы, такие как play()
, pause()
, stop()
и другие:
// App.js
import Audio from 'ts-audio';
import Lazarus from './music/Lazarus.mp3';
export default function App() {
const audio = Audio({
file: Lazarus
})
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>
</>
)
}
В приведенном выше блоке кода мы импортировали компонент Audio
из ts-audio и песню, которую хотим воспроизвести. Мы создали экземпляр аудио, установили его на импортированный компонент Audio
, а затем передали импортированную музыку в параметр file, открываемый элементом Audio
. Мы воспользовались методами, предоставленными нам ts-audio, такими как play()
и pause()
, затем передали их через функции кнопкам.
Использование компонента AudioPlaylist
Компонент AudioPlaylist
позволяет нам передавать несколько песен, но они должны быть в массиве, иначе ts-audio не будет их воспроизводить. Компонент AudioPlaylist
предоставляет нам такие методы, как play()
, pause()
, stop()
, next()
, и prev()
.
Приведенный ниже блок кода является примером использования компонента AudioPlaylist
:
// App.js
import { AudioPlaylist } from 'ts-audio';
import Lazarus from './music/Lazarus.mp3';
import Sia from './music/Sia - Bird Set Free.mp3';
export default function App() {
const playlist = AudioPlaylist({
files: [Lazarus, Sia]
})
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>
</>
)
}
Музыкальный проигрыватель будет обладать следующими функциональными возможностями:
- Смена исполнителя на исполнителя текущей песни при нажатии на next или previous
- Изменение изображения на изображение текущей песни
- Изменение названия песни на название текущей песни.
В папке src
создайте две папки images
и music
соответственно. Перейдите в папку images
и вставьте туда все фотографии, которые вам могут понадобиться. В папку music
можно вставить любые аудиофайлы, которые вы хотите использовать.
В следующих репозиториях GitHub вы можете получить файлы изображений, используемые в этом руководстве, и получить аудиофайлы. Далее импортируйте песни и изображения в App.js
следующим образом:
import { AudioPlaylist } from 'ts-audio';
// Music import
import Eyes from './music/01. Jon Bellion - Eyes To The Sky.mp3';
import Mood from './music/24kGoldn-Mood-Official-Audio-ft.-Iann-Dior.mp3';
import Audio from './music/audio.mp3';
import Broken from './music/Cant Be Broken .mp3';
import Lazarus from './music/Lazarus.mp3';
import Sia from './music/Sia - Bird Set Free.mp3';
import Nobody from './music/T-Classic-Nobody-Fine-Pass-You.mp3';
import Yosemite from './music/Yosemite.mp3';
// Pictures import
import EyesImg from './images/Eyes to the sky.jpeg';
import MoodImg from './images/mood.jpeg';
import AudioImg from './images/lana.jpeg';
import BrokenImg from './images/lil wayne.jpeg';
import LazarusImg from './images/dave.jpeg';
import SiaImg from './images/sia.jpeg';
import NobodyImg from './images/nobody.jpeg';
import YosemiteImg from './images/travis.jpeg';
export default function App() {
const songs = [
{
title: 'Eyes to the sky',
artist: 'Jon Bellion',
img_src: EyesImg,
src: Eyes,
},
{
title: 'Lazarus',
artist: 'Dave',
img_src: LazarusImg,
src: Lazarus,
},
{
title: 'Yosemite',
artist: 'Travis scott',
img_src: YosemiteImg,
src: Yosemite,
},
{
title: 'Bird set free',
artist: 'Sia',
img_src: SiaImg,
src: Sia,
},
{
title: 'Cant be broken',
artist: 'Lil wayne',
img_src: BrokenImg,
src: Broken,
},
{
title: 'Mood',
artist: '24kGoldn',
img_src: MoodImg,
src: Mood,
},
{
title: 'Nobody fine pass you',
artist: 'T-Classic',
img_src: NobodyImg,
src: Nobody,
},
{
title: 'Dark paradise',
artist: 'Lana Del Ray',
img_src: AudioImg,
src: Audio,
},
]
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>
</>
);
}
В приведенном выше блоке кода мы импортировали песни и изображения. Затем мы создали массив песен, содержащий объекты. Каждый объект имеет title
, artist
, img_src
для импортированных изображений и src
для импортированных песен.
После этого мы отобразили массив песен, чтобы добраться до src
, который мы передали в параметре files. Помните, что мы должны передать его как массив, но затем метод map()
создает новый массив из вызова функции. Поэтому мы можем передать его в параметр files
.
Мы также создали наши методы и передали их различным кнопкам. Мы создадим файл 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
, и, наконец, передали их в кнопки.
Создание функциональных возможностей
Чтобы создать функциональные возможности для нашего приложения, мы импортируем useState
, чтобы получить текущий индекс песни. Затем мы установим изображение на текущую фотографию, исполнителя на текущего исполнителя, а название на текущее название:
// 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>
</>
);
}
Мы создали событие состояния и установили его в ноль. Когда мы нажимаем кнопку next, мы устанавливаем состояние в сумму остатка текущего состояния, единицы, и длины песни, деленной на длину песни:
currentSong + 1 + songs.length) % songs.length
Когда мы нажимаем на предыдущую кнопку, мы устанавливаем состояние как остаток текущей песни, минус один, плюс длина песни, деленная на длину песни:
currentSong - 1 + songs.length) % songs.length
Мы также создали событие состояния, которое проверяет, играет песня или нет, а затем передаем его в качестве реквизита компоненту Player
. Наконец, мы создали функциональные возможности для изменения изображения, исполнителей и названия песни.
Когда мы запускаем приложение, кажется, что все работает; изображения меняются при нажатии на следующую кнопку. Однако воспроизводимые песни не соответствуют изображениям и названиям исполнителей, отображаемым на экране. Иногда две или более песен воспроизводятся одновременно.
Решение проблемы: Несоответствие сведений о песнях
Когда мы нажимаем на кнопки next или previous, мы пересчитываем значения и фактически вызываем повторный рендеринг. Чтобы остановить это, мы обернем массив песен и созданный экземпляр списка воспроизведения в хук useMemo
, как показано ниже:
// 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: 'Eyes to the sky',
artist: 'Jon Bellion',
img_src: EyesImg,
src: Eyes,
},
{
title: 'Lazarus',
artist: 'Dave',
img_src: LazarusImg,
src: Lazarus,
},
{
title: 'Yosemite',
artist: 'Travis scott',
img_src: YosemiteImg,
src: Yosemite,
},
{
title: 'Bird set free',
artist: 'Sia',
img_src: SiaImg,
src: Sia,
},
{
title: 'Cant be broken',
artist: 'Lil wayne',
img_src: BrokenImg,
src: Broken,
},
{
title: 'Mood',
artist: '24kGoldn',
img_src: MoodImg,
src: Mood,
},
{
title: 'Nobody fine pass you',
artist: 'T-Classic',
img_src: NobodyImg,
src: Nobody,
},
{
title: 'Dark paradise',
artist: 'Lana Del Ray',
img_src: AudioImg,
src: Audio,
},
],
[]
);
const playlist = useMemo(() => {
return AudioPlaylist({
files: songs.map((song) => song.src),
});
}, [songs]);
Хук useMemo
эффективно кэширует значение, чтобы его не нужно было пересчитывать, и, следовательно, не вызывать повторный рендеринг.
Добавление стиля
В этом уроке мы будем использовать иконки из Font Awesome Icons. Вы можете установить пакет 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
, затем обрабатываем их внутри файла 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, агностической, простой в использовании библиотекой, которая работает с API AudioContext
. Мы узнали о методах ts-audio и о том, как она облегчает работу с аудиофайлами. Наконец, мы узнали, как создать работающий музыкальный плеер с использованием ts-audio.
Полная видимость производственных приложений React
Отладка приложений React может быть сложной задачей, особенно когда пользователи сталкиваются с проблемами, которые трудно воспроизвести. Если вы заинтересованы в мониторинге и отслеживании состояния Redux, автоматическом обнаружении ошибок JavaScript, отслеживании медленных сетевых запросов и времени загрузки компонентов, попробуйте LogRocket.
LogRocket – это как видеорегистратор для веб- и мобильных приложений, записывающий буквально все, что происходит в вашем React-приложении. Вместо того чтобы гадать, почему возникают проблемы, вы можете собрать данные и отчитаться о том, в каком состоянии находилось ваше приложение в момент возникновения проблемы. LogRocket также отслеживает производительность вашего приложения, предоставляя такие показатели, как загрузка процессора клиента, использование памяти клиента и многое другое.
Пакет промежуточного ПО LogRocket Redux добавляет дополнительный уровень видимости пользовательских сессий. LogRocket регистрирует все действия и состояние ваших хранилищ Redux.
Модернизируйте отладку приложений React – начните мониторинг бесплатно.