实战React音乐播放器

    xiaoxiao2021-03-26  39

    上篇文章《一步一步实战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

    博客地址:http://blog.csdn.net/lecepin

    转载请注明原文地址: https://ju.6miu.com/read-658375.html

    最新回复(0)