實戰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 音樂播放器程式碼下載:程式碼下載
部落格名稱:王樂平部落格
部落格地址:http://blog.lepingde.com
CSDN部落格地址:http://blog.csdn.net/lecepin