如何在 React & 中处理音频播放终极版 [英] How to handle Audio playing in React & Redux

查看:45
本文介绍了如何在 React & 中处理音频播放终极版的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个音频播放器.它具有暂停、倒带和时间搜索功能.如何以及由谁来处理音频元素?

  • 我可以把它放在商店里.我不能把它直接放在状态上,因为它可能会被克隆.然后当在减速器中时,我可以与它进行交互.问题是,如果我需要将时间滑块与音频同步,我将需要使用操作不断轮询商店.从语义上讲,它也没有真正的意义.
  • 我可以创建一个自定义的 React 组件 Audio,它可以完成我所说的一切.问题没有解决.如何刷新滑块?我可以投票,但我真的不喜欢这个解决方案.此外,除非我创建一个包含音频和滑块的组件,否则我仍然需要使用 redux 来连接两者.

那么处理带有进度显示的音频的最redux 方式是什么?

解决方案

Redux - 一切都与状态和一致性有关.

你的目标是让歌曲时间和进度条保持同步.

我看到两种可能的方法:

1.保留商店中的所有内容.

因此您必须将歌曲的当前时间(例如以秒为单位)保存在 Store 中,因为有一些依赖组件并且没有 Store 很难同步它们.

您几乎没有更改当前时间的事件:

  • 当前时间本身.例如每 1 秒一次.
  • 倒带.
  • 时间寻找.

在时间更改时,您将分派一个操作并使用新时间更新 Store.从而保持当前歌曲的时间所有组件将同步.

通过动作分派、reducer 和 store 管理单向数据流中的状态是实现任何组件的 Redux 方式.

这是#1 方法的伪代码:

class AudioPlayer 扩展 React.Component {播放(第二){//每隔一秒将歌曲当前时间存储在 Store 中store.dispatch({ type: 'SET_CURRENT_SECOND', second });}onRewind(秒){//倒回歌曲当前时间store.dispatch({ type: 'REWIND_CURRENT_SECOND', seconds });}onSeek(秒){//查找歌曲当前时间store.dispatch({ type: 'SEEK_CURRENT_SECOND', seconds });}使成为() {const { currentTime, songLength } = this.state;返回 

<audio onPlay={this.onPlay} onRewind={this.onRewind} onSeek={this.onSeek}/><AudioProgressBar currentTime songLength/>

}}

<小时>

2.尽可能少地在商店中存放.

如果上述方法不符合您的需求,例如您可能在同一个屏幕上有很多音频播放器 - 可能存在性能差距.

在这种情况下,您可以通过 refs 在 componentDidMount 生命周期方法中.

HTML5 音频标签具有 DOM 事件,您可以在不接触 Store 的情况下使两个组件保持同步.如果需要在 Store 中保存某些内容 - 您可以随时进行.

请看一下react-audio-player源代码并检查它如何处理引用以及插件公开的API.当然,您可以从那里获得灵感.您也可以在您的用例中重复使用它.

以下是一些与您的问题相关的 API 方法:

  • onSeeked - 当用户将时间指示器拖动到新时间时调用.活动通过.
  • onPlay - 当用户点击播放时调用.活动通过.
  • onPause - 当用户暂停播放时调用.活动通过.
  • onListen - 在播放过程中每隔 listenInterval 毫秒调用一次.活动通过.
<小时>

我应该使用什么方法?

这取决于您的用例细节.但是一般来说,在这两种方法中,实现 具有必要 API 方法的展示组件,由您决定要在 Store 中管理多少数据.

所以我为您创建了一个起始组件来说明如何处理对音频和滑块的引用.包括开始/停止/寻找功能.它肯定有缺点,但正如我已经提到的,这是一个很好的起点.

您可以将其发展为具有良好 API 方法的展示组件,以满足您的需求.

class Audio 扩展 React.Component {构造函数(道具){超级(道具);this.state = {持续时间:空}};手柄播放(){this.audio.play();}句柄停止(){this.audio.currentTime = 0;this.slider.value = 0;this.audio.pause();}componentDidMount() {this.slider.value = 0;this.currentTimeInterval = null;//获取歌曲的持续时间并将其设置为最大滑块值this.audio.onloadedmetadata = function() {this.setState({duration: this.audio.duration});}.bind(this);//将滑块位置与歌曲当前时间同步this.audio.onplay = () =>{this.currentTimeInterval = setInterval(() => {this.slider.value = this.audio.currentTime;}, 500);};this.audio.onpause = () =>{clearInterval(this.currentTimeInterval);};//寻找功能this.slider.onchange = (e) =>{clearInterval(this.currentTimeInterval);this.audio.currentTime = e.target.value;};}使成为() {const src = "https://mp3.gisher.org/download/1000/preview/true";返回 

<音频参考={(音频)=>{ this.audio = 音频 }} src={src}/><input type="button" value="播放"onClick={ this.handlePlay.bind(this) }/><输入类型=按钮"值=停止"onClick={ this.handleStop.bind(this) }/><p><input ref={(slider) =>{ this.slider = 滑块 }}类型=范围"名称=点"min="0" max={this.state.duration}/></p>

}}ReactDOM.render(

<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="容器"><!-- 此元素的内容将替换为您的组件.-->

<小时>

如果您有任何问题,请随时在下面发表评论!:)

I am making an audio player. It has pause, rewind and time seek features. How and who should handle the audio element?

  • I can put it aside the store. I cant put it directly on the state, since it might get cloned. Then when in the reducer I can interact with it. The problem is that if I need to sync the time slider with the audio I will need to constantly poll the store using an action. It also doesn't really makes sense semantically speaking.
  • I can create a custom React component, Audio, which does everything I said. the problem isn't solved. How can I refresh the slider? I could poll but I really don't like this solution. Besides, unless I create a component that contains both audio and slider, I would still need to use redux to connect both.

So what is the most redux way to handle audio with progress display?

解决方案

Redux - it's all about the state and consistency.

Your goal is to keep in sync the song time and the progress bar.

I see two possible aproaches:

1. Keep everyting in the Store.

So you have to keep the song's current time (in seconds for instance) in the Store, because of there are a few dependend components and its hard to sync them without the Store.

You have few events those change the current time:

  • The current time itself. For example on each 1 second.
  • Rewind.
  • Time seek.

On a time change you will dispatch an action and will update the Store with the new time. Thereby keeping current song's time all components will be in sync.

Managing the state in unidirectional data flow with actions dispatching, reducers and stores is the Redux way of implementing any component.

Here is a pseudo code of the #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. Keep as less as possible in the Store.

If the above aproach doesn't fit your needs, for example you may have a lot of Audio players on a same screen - there may be a performance gap.

In that case you can access your HTML5 audio tag and components via refs in the componentDidMount lifecycle method.

The HTML5 audio tag has DOM events and you can keep the both components in sync without touching the Store. If there is a need to save something in the Store - you can do it anytime.

Please take a look at react-audio-player source code and check how it handles the refs and what API the plugin exposes. For sure you can take inspiration from there. Also you can reuse it for your use case.

Here are some of the API methods those are related to your questions:

  • onSeeked - Called when the user drags the time indicator to a new time. Passed the event.
  • onPlay - Called when the user taps play. Passed the event.
  • onPause - Called when the user pauses playback. Passed the event.
  • onListen - Called every listenInterval milliseconds during playback. Passed the event.

What approach should I use?

It depends to your use case specifics. However generally speaking in the both aproaches it's a good idea to implement a presentational component with the necessary API methods and it's up to you to decide how much data to manage in the Store.

So I created a starting component for you to illustrate how to handle the refs to the audio and slider. Start / Stop / Seeking features included. For sure it has drawbacks, but as I already mentioned it's a good starting point.

You can evolve it to a presentational component with good API methods, that suits your needs.

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://mp3.gisher.org/download/1000/preview/true";
		
		
		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's contents will be replaced with your component. -->
</div>


If you have any questions feel free to comment below! :)

这篇关于如何在 React &amp; 中处理音频播放终极版的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆