我正在制作音频播放器。它具有暂停,倒带和时间搜索功能。如何以及谁应该处理音频元素?
那么使用进度显示处理音频的最简单方法是什么?
答案 0 :(得分:18)
Redux - 关于状态和一致性。
您的目标是保持歌曲时间和进度条的同步。
我看到两种可能的方法:
所以你必须在商店中保留歌曲的当前时间(例如以秒为单位),因为有一些dependend组件并且很难在没有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>
}
}
如果上述方法不符合您的需求,例如您可能在同一屏幕上有很多音频播放器 - 可能存在性能差距。
在这种情况下,您可以通过componentDidMount生命周期方法中的refs访问HTML5音频标记和组件。
HTML5音频标记包含DOM事件,您可以在不触及商店的情况下保持两个组件同步。如果需要在商店中保存某些东西 - 您可以随时进行。
请查看react-audio-player source code并查看它如何处理引用以及插件公开的API。当然,你可以从那里获取灵感。您也可以将它重用于您的用例。
以下是与您的问题相关的一些API方法:
这取决于您的用例细节。但是一般来说,在这两种方法中,使用必要的API方法实现presentational component是一个好主意,由您来决定在商店中管理多少数据。
所以我为你创建了一个起始组件来说明如何处理音频和滑块的引用。启动/停止/寻找功能包括在内。当然它有缺点,但正如我已经提到的那样,这是一个很好的起点。
您可以使用适合您需求的良好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://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>
如果您有任何问题,请随时在下面发表评论! :)