Как обрабатывать воспроизведение аудио в React & Redux

Я делаю аудиоплеер. Он имеет функции паузы, перемотки назад и времени. Как и кто должен обрабатывать аудио элемент?

  • Я могу отложить это в сторону магазина. Я не могу поставить его прямо на государство, так как он может быть клонирован. Тогда, когда в редукторе я могу взаимодействовать с ним. Проблема в том, что если мне нужно синхронизировать слайдер времени со звуком, мне нужно будет постоянно опробовать магазин, используя действие. Это также не имеет смысла семантически.
  • Я могу создать пользовательский компонент React, Audio, который делает все, что я сказал. проблема не решена. Как обновить слайдер? Я мог опросить, но мне действительно не нравится это решение. Кроме того, если я не создаю компонент, который содержит как аудио, так и слайдер, мне все равно придется использовать reducex для подключения обоих.

Итак, каков самый сокращенный способ обработки звука с индикацией прогресса?

Ответ 1

Redux - все о состоянии и последовательности.

Ваша цель - синхронизировать время песни и индикатор выполнения.

Я вижу два возможных аспекта:

1. Храните все в магазине.

Таким образом, вы должны сохранить текущее время песни (например, в секундах) в Магазине, потому что есть несколько зависимых компонентов и его трудно синхронизировать с ними без Store.

У вас мало событий, которые меняют текущее время:

  • Текущее время. Например, каждые 1 секунду.
  • Перемотка.
  • Поиск времени.

При изменении времени вы отправите действие и обновите Store с новым временем. Таким образом, сохранение текущего времени песни все компоненты будут синхронизированы.

Управление состоянием в однонаправленном потоке данных с помощью операций диспетчеризации, редукторов и хранилищ - это метод Redux для реализации любого компонента.

Вот псевдокод # 1 aproach:

class AudioPlayer extends React.Component {

    onPlay(second) {
        // Store song current time in the Store on each one second
        store.dispatch({ type: 'SET_CURRENT_SECOND', second });
    }

    onRewind(seconds) {
        // Rewind song current time
        store.dispatch({ type: 'REWIND_CURRENT_SECOND', seconds });
    }   

    onSeek(seconds) {
        // Seek song current time
        store.dispatch({ type: 'SEEK_CURRENT_SECOND', seconds });
    }

    render() {
        const { currentTime, songLength } = this.state;

        return <div>
            <audio onPlay={this.onPlay} onRewind={this.onRewind} onSeek={this.onSeek} />

            <AudioProgressBar currentTime songLength />
        </div>
    }
}

2. Храните как можно меньше.

Если вышеуказанный aproach не соответствует вашим потребностям, например, у вас может быть много аудиоплееров на одном экране - может быть разрыв в производительности.

В этом случае вы можете получить доступ к своим тегам и компонентам HTML5 через refs в методе жизненного цикла componentDidMount.

Аудиотекст HTML5 содержит события DOM, и вы можете синхронизировать оба компонента, не касаясь магазина. Если в магазине нужно что-то сохранить, вы можете сделать это в любое время.

Пожалуйста, посмотрите исходный код реакции-аудио-проигрывателя и проверьте, как он обрабатывает ссылки и какой API предоставляет плагин. Конечно, вы можете вдохновить оттуда. Также вы можете повторно использовать его для своего использования.

Вот некоторые из методов API, которые связаны с вашими вопросами:

  • onSeeked - вызов, когда пользователь перетаскивает индикатор времени в новое время. Передано событие.
  • onPlay - Вызывается, когда пользователь играет краны. Передано событие.
  • onPause - Вызывается, когда пользователь приостанавливает воспроизведение. Передано событие.
  • onListen - Вызывается каждый раз прослушивание миллисекунд во время воспроизведения. Передано событие.

Какой подход следует использовать?

Это зависит от особенностей вашего использования. Однако, как правило, в обоих аспектах рекомендуется создать презентационный компонент с необходимыми методами API, и вам решать, сколько данных для управления в магазине.

Итак, я создал исходный компонент, чтобы вы могли проиллюстрировать, как обрабатывать ссылки на аудио и слайдер. Включение/выключение/поиск функций. Конечно, у этого есть недостатки, но, как я уже упоминал, это хорошая отправная точка.

Вы можете превратить его в презентационный компонент с хорошими API-методами, которые соответствуют вашим потребностям.

class Audio extends React.Component {
	constructor(props) {
		super(props);
		
		this.state = {
			duration: null
		}
  	};
	
	handlePlay() {
		this.audio.play();
	}
	
	handleStop() {
		this.audio.currentTime = 0;
		this.slider.value = 0;
		this.audio.pause(); 
	}

	componentDidMount() {
		this.slider.value = 0;
		this.currentTimeInterval = null;
		
		// Get duration of the song and set it as max slider value
		this.audio.onloadedmetadata = function() {
			this.setState({duration: this.audio.duration});
		}.bind(this);
		
		// Sync slider position with song current time
		this.audio.onplay = () => {
			this.currentTimeInterval = setInterval( () => {
				this.slider.value = this.audio.currentTime;
			}, 500);
		};
		
		this.audio.onpause = () => {
			clearInterval(this.currentTimeInterval);
		};
		
		// Seek functionality
		this.slider.onchange = (e) => {
			clearInterval(this.currentTimeInterval);
			this.audio.currentTime = e.target.value;
		};
	}

	render() {
		const src = "https://files.freemusicarchive.org/music%2Fno_curator%2FThe_Womb%2FBang_-_An_Introduction_to_The_Womb%2FThe_Womb_-_02_-_Sex_Club.mp3";
		
		
		return <div>
			<audio ref={(audio) => { this.audio = audio }} src={src} />
			
			<input type="button" value="Play"
				onClick={ this.handlePlay.bind(this) } />
			
			<input type="button"
					value="Stop"
					onClick={ this.handleStop.bind(this) } />
			
			<p><input ref={(slider) => { this.slider = slider }}
					type="range"
					name="points"
					min="0" max={this.state.duration} /> </p>
		</div>
	}
}

ReactDOM.render(<Audio />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
    <!-- This element contents will be replaced with your component. -->
</div>