实战React音乐播放器
Posted 王乐平
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实战React音乐播放器相关的知识,希望对你有一定的参考价值。
上篇文章《一步一步实战HTML音乐播放器》中,我用html+JS + CSS的方式一步步实现了一个音乐播放器,因为最近接触了一下React,感觉挺不错的,在这里我用React的方式实现一个同样的音乐播放器。
播放器功能
- 自动显示 专辑图片、歌手名、歌曲名、专辑名
- 显示播放器进度条
- 音乐播放暂停、上一曲、下一曲
- 实时显示播放时间、播放总长度
- 歌曲播放完后,自动切换下一曲
播放器效果
React 环境准备
在这个小项目中,不再使用传统的构建React的方式来搭建环境了,这里用一种很方便的小工具来实现环境的搭建。
在Node.js环境下执行如下命令,安装一下create-react-app
,并创建musicPlayer
项目:
npm install -g create-react-app
create-react-app musicPlayer
cd musicPlayer
npm start
执行完后后,工具会自动打开浏览器来显示这个项目的内容,效果如下:
这里我用到的是src
目录,首先把src
目录的内容全部删除,我们一点一点的来编写项目代码。
引入必要文件
因为系统默认将index.js
作为入口文件,所以我们要先在src
下创建index.js
文件,所有代码也是在这个文件中编写。
先在index.js
中引入一些必要的文件:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.min.css';
index.min.css
是播放器的样式文件,这里主要说React,这个样式文件的详细说明可以参考 《一步一步实战HTML音乐播放器》
创建播放器容器组件
var Player = React.createClass({
render: function() {
return (
<div className="player">
{/* 各类子组建…… */}
</div>
);
}
});
子组建我们下面会一一创建,这里先用做一下占位说明。
页面渲染
ReactDOM.render(
<Player />,
document.getElementById('root')
);
容器内的各类组件
根据播放器结构,创建如下组件:
var Player = React.createClass({
render: function() {
return (
<div className="player">
{/* 播放器名称 */}
<div className="header">音乐播放器.React版</div>
{/* 音乐信息 */}
<TrackInfo />
{/* 播放进度条 */}
<Progress />
{/* 播放控制 */}
<Controls />
{/* 播放时间 */}
<Time />
{/* 音频控件 */}
<audio id="audio"></audio>
</div>
);
}
});
初始化STATE,PROPS
根据需求,进行状态和属性的创建。
这里歌单就手动制作一个了,把这个歌单写的props
中,用于系统的调用:
getDefaultProps: function() {
//歌单列表
return{
"tracks": [
{
"name": "元日",
"artists": [
{
"name": "于文华",
}
],
"album": {
"name": "国学唱歌集",
"picUrl": "http://p3.music.126.net/SR9eFEjRB0NsscxN7-fHMw==/3344714372906000.jpg",
},
"duration": 136829,
"mp3Url": "http://m2.music.126.net/rUcfqqZbq7TIfJeAHfTrkw==/3376600210116829.mp3"
},
{
"name": "元日 ",
"artists": [
{
"name": "清弄",
}
],
"album": {
"name": "热门华语261",
"picUrl": "http://p4.music.126.net/ly2FJHh5-lYMdC3NZxvavQ==/7714173580661848.jpg",
},
"duration": 109000,
"mp3Url": "http://m2.music.126.net/jwwZVlWJ78HEarft42uKUQ==/7906588115920636.mp3"
},
{
"name": "青龙·花木苍苍",
"artists": [
{
"name": "五色石南叶",
}
],
"album": {
"name": "热门华语234",
"picUrl": "http://p4.music.126.net/tHAfnugCElS93EDp5cHLIw==/8909342719897560.jpg",
},
"duration": 295575,
"mp3Url": "http://m2.music.126.net/rnq_W32zFX_utQbBhE0xkg==/8934631487358481.mp3"
}]
}
},
接着初始化一下播放器的状态:
//初始化状态
getInitialState: function() {
return{
currentTrackLen: this.props.tracks.length, //歌单歌曲数
currentTrackIndex: 0, //当前播放的歌曲索引,默认加载第一首歌
currentTime: 0, //当前歌曲播放的时间
currentTotalTime: 0, //当前歌曲的总时间
playStatus: true, //true为播放状态,false为暂停状态
}
},
创建子组件
TrackInfo组件
var TrackInfo = React.createClass({
render: function() {
return(
<div>
<div className="albumPic" style={{'backgroundImage':'url('+ this.props.track.album.picUrl +')'}}></div>
<div className='trackInfo'>
<div className="name">{this.props.track.name}</div>
<div className="artist">{this.props.track.artists[0].name}</div>
<div className="album">{this.props.track.album.name}</div>
</div>
</div>
);
}
});
在Player
容器中的标签修改为:
{/* 音乐专辑 */}
<TrackInfo track={this.props.tracks[this.state.currentTrackIndex]} />
Progress组件
var Progress = React.createClass({
render: function(){
return (
<div className="progress" style={{'width':this.props.progress}}></div>
)
}
});
在Player
容器中的标签修改为:
{/* 播放进度条 */}
<Progress progress={this.state.currentTime / this.state.currentTotalTime * 100 + '%'} />
通过当前时间和总时间来计算播放百分百。
Controls组件
var Controls = React.createClass({
render: function(){
let className;
if(this.props.isPlay == true){
className = 'icon-pause';
}else{
className = 'icon-play';
}
return (
<div className="controls">
<div className="play" onClick={this.props.onPlay}>
<i className={className}></i>
</div>
<div className="previous" onClick={this.props.onPrevious}>
<i className="icon-previous"></i>
</div>
<div className="next" onClick={this.props.onNext}>
<i className="icon-next"></i>
</div>
</div>
)
}
});
通过isPlay
来控制播放按钮图标的显示。
在Player
容器中的标签修改为:
{/* 播放控制 */}
<Controls isPlay={this.state.playStatus} onPlay={this.play} onPrevious={this.previous} onNext={this.next} />
Time组件
var Time = React.createClass({
timeConvert: function(timestamp){
var minutes = Math.floor(timestamp / 60);
var seconds = Math.floor(timestamp - (minutes * 60));
if(seconds < 10) {
seconds = '0' + seconds;
}
timestamp = minutes + ':' + seconds;
return timestamp;
},
render:function() {
return(
<div className="time">
<div className="current">{this.timeConvert(this.props.currentTime)}</div>
<div className="total">{this.timeConvert(this.props.currentTotalTime)}</div>
</div>
);
}
});
timeConvert
做为一个时间转换显示来用。
在Player
容器中的标签修改为:
{/* 播放时间 */}
<Time currentTime={this.state.currentTime} currentTotalTime={this.state.currentTotalTime} />
audio标签
audio
这里不需要在创建组件了,修改一下在Player
中的标记就行:
{/* 音频控件 */}
<audio id="audio" src={this.props.tracks[this.state.currentTrackIndex].mp3Url}></audio>
事件处理方法
创建updatePlayStatus
方法用于更新播放器的状态:
//更新播放状态
updatePlayStatus: function(){
let audio = document.getElementById('audio');
if(this.state.playStatus){
audio.play();
}else{
audio.pause();
}
//更新当前歌曲总时间
this.setState({currentTotalTime: this.props.tracks[this.state.currentTrackIndex].duration / 1000});
},
创建三个播放控制按钮的事件方法:
//播放事件处理
play:function(){
//这里有setState是异步的,需要在回调中执行
this.setState({playStatus:!this.state.playStatus}, ()=>{
this.updatePlayStatus();
});
},
//上一曲事件处理
previous:function(){
if(this.state.currentTrackIndex - 1 < 0){
alert('已经没有上一首了');
}else{
this.setState({currentTrackIndex:--this.state.currentTrackIndex},()=>{
this.updatePlayStatus();
});
}
},
//下一曲事件处理
next:function(){
if(this.state.currentTrackIndex + 1 >= this.state.currentTrackLen){
alert('已经没有下一首了');
}else{
this.setState({currentTrackIndex:++this.state.currentTrackIndex},()=>{
this.updatePlayStatus();
});
}
},
在页面渲染完成后需要执行一下updatePlayStatus
方法,根据React生命周期,我们在DOM加载完成后执行一下这个方法:
componentDidMount: function(){
this.updatePlayStatus();
},
好了,各类事件的方法基本完成,这里还需要一个监测的方法,用来实时更新播放时间和自动下一曲:
componentDidMount: function(){
this.updatePlayStatus();
setInterval(()=>{
let audio = document.getElementById('audio');
this.setState({currentTime:audio.currentTime},()=>{
if(~~this.state.currentTime >= ~~this.state.currentTotalTime){
this.next();
}
});
}, 300);
},
完整代码
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.min.css';
var Player = React.createClass({
getDefaultProps: function() {
//歌单列表
return{
"tracks": [
{
"name": "元日",
"artists": [
{
"name": "于文华",
}
],
"album": {
"name": "国学唱歌集",
"picUrl": "http://p3.music.126.net/SR9eFEjRB0NsscxN7-fHMw==/3344714372906000.jpg",
},
"duration": 136829,
"mp3Url": "http://m2.music.126.net/rUcfqqZbq7TIfJeAHfTrkw==/3376600210116829.mp3"
},
{
"name": "元日 ",
"artists": [
{
"name": "清弄",
}
],
"album": {
"name": "热门华语261",
"picUrl": "http://p4.music.126.net/ly2FJHh5-lYMdC3NZxvavQ==/7714173580661848.jpg",
},
"duration": 109000,
"mp3Url": "http://m2.music.126.net/jwwZVlWJ78HEarft42uKUQ==/7906588115920636.mp3"
},
{
"name": "青龙·花木苍苍",
"artists": [
{
"name": "五色石南叶",
}
],
"album": {
"name": "热门华语234",
"picUrl": "http://p4.music.126.net/tHAfnugCElS93EDp5cHLIw==/8909342719897560.jpg",
},
"duration": 295575,
"mp3Url": "http://m2.music.126.net/rnq_W32zFX_utQbBhE0xkg==/8934631487358481.mp3"
}]
}
},
//初始化状态
getInitialState: function() {
return{
currentTrackLen: this.props.tracks.length, //歌单歌曲数
currentTrackIndex: 0, //当前播放的歌曲索引,默认加载第一首歌
currentTime: 0, //当前歌曲播放的时间
currentTotalTime: 0, //当前歌曲的总时间
playStatus: true, //true为播放状态,false为暂停状态
}
},
//更新播放状态
updatePlayStatus: function(){
let audio = document.getElementById('audio');
if(this.state.playStatus){
audio.play();
}else{
audio.pause();
}
//更新当前歌曲总时间
this.setState({currentTotalTime: this.props.tracks[this.state.currentTrackIndex].duration / 1000});
},
//播放事件处理
play:function(){
//这里有setState是异步的,需要在回调中执行
this.setState({playStatus:!this.state.playStatus}, ()=>{
this.updatePlayStatus();
});
},
//上一曲事件处理
previous:function(){
if(this.state.currentTrackIndex - 1 < 0){
alert('已经没有上一首了');
}else{
this.setState({currentTrackIndex:--this.state.currentTrackIndex},()=>{
this.updatePlayStatus();
});
}
},
//下一曲事件处理
next:function(){
if(this.state.currentTrackIndex + 1 >= this.state.currentTrackLen){
alert('已经没有下一首了');
}else{
this.setState({currentTrackIndex:++this.state.currentTrackIndex},()=>{
this.updatePlayStatus();
});
}
},
//DOM加载完
componentDidMount: function(){
this.updatePlayStatus();
setInterval(()=>{
let audio = document.getElementById('audio');
this.setState({currentTime:audio.currentTime},()=>{
if(~~this.state.currentTime >= ~~this.state.currentTotalTime){
this.next();
}
});
}, 300);
},
render: function() {
return (
<div className="player">
{/* 播放器名称 */}
<div className="header">音乐播放器.React版</div>
{/* 音乐信息 */}
<TrackInfo track={this.props.tracks[this.state.currentTrackIndex]} />
{/* 播放进度条 */}
<Progress progress={this.state.currentTime / this.state.currentTotalTime * 100 + '%'} />
{/* 播放控制 */}
<Controls isPlay={this.state.playStatus} onPlay={this.play} onPrevious={this.previous} onNext={this.next} />
{/* 播放时间 */}
<Time currentTime={this.state.currentTime} currentTotalTime={this.state.currentTotalTime} />
{/* 音频控件 */}
<audio id="audio" src={this.props.tracks[this.state.currentTrackIndex].mp3Url}></audio>
</div>
);
}
});
var TrackInfo = React.createClass({
render: function() {
return(
<div>
<div className="albumPic" style={{'backgroundImage':'url('+ this.props.track.album.picUrl +')'}}></div>
<div className='trackInfo'>
<div className="name">{this.props.track.name}</div>
<div className="artist">{this.props.track.artists[0].name}</div>
<div className="album">{this.props.track.album.name}</div>
</div>
</div>
);
}
});
var Progress = React.createClass({
render: function(){
return (
<div className="progress" style={{'width':this.props.progress}}></div>
)
}
});
var Controls = React.createClass({
render: function(){
let className;
if(this.props.isPlay == true){
className = 'icon-pause';
}else{
className = 'icon-play';
}
return (
<div className="controls">
<div className="play" onClick={this.props.onPlay}>
<i className={className}></i>
</div>
<div className="previous" onClick={this.props.onPrevious}>
<i className="icon-previous"></i>
</div>
<div className="next" onClick={this.props.onNext}>
<i className="icon-next"></i>
</div>
</div>
)
}
});
var Time = React.createClass({
timeConvert: function(timestamp){
var minutes = Math.floor(timestamp / 60);
var seconds = Math.floor(timestamp - (minutes * 60));
if(seconds < 10) {
seconds = '0' + seconds;
}
timestamp = minutes + ':' + seconds;
return timestamp;
},
render:function() {
return(
<div className="time">
<div className="current">{this.timeConvert(this.props.currentTime)}</div>
<div className="total">{this.timeConvert(this.props.currentTotalTime)}</div>
</div>
);
}
});
ReactDOM.render(
<Player />,
document.getElementById('root')
);
发布项目
在Node.js环境下执行:
npm run build
进行代码打包处理,打包文件生成了项目目录下的build
中,执行如下命令,可直接查看build
打包后的内容:
npm install -g pushstate-server
pushstate-server build
浏览器中输入如下地址:http://localhost:9000
好了,用React来实现音乐播放器彻底完成,因为React我也是刚接触不久,代码中可能存在着缺点,我这里就抛砖引玉了。
总体来说,用React的思想来做东西,确实挺好的,逻辑上也变得比较清晰。
React 音乐播放器代码下载:代码下载
博客名称:王乐平博客
CSDN博客地址:http://blog.csdn.net/lecepin
以上是关于实战React音乐播放器的主要内容,如果未能解决你的问题,请参考以下文章