上篇文章《一步一步实战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 音乐播放器代码下载:代码下载
博客名称:王乐平博客
博客地址:http://blog.lepingde.com
CSDN博客地址:http://blog.csdn.net/lecepin