实战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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401682.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASP.NET MVC5+EF6+EasyUI 后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】...

系列目录 上次的探讨没有任何结果&#xff0c;我浏览了大量的文章和个别系统的参考&#xff01;决定用Cache来做&#xff0c;这可能有点难以接受但是配合mvc过滤器来做效果非常好&#xff01; 由于之前的过滤器我们用过了OnActionExecuting这个方法来判断权限 现在在方法被执行…

理解关键的渲染路径

本文转载自&#xff1a;《Understanding the Critical Rendering Path》,原文地址&#xff1a;https: //bitsofco.de/understanding-the-critical-rendering-path/ 当浏览器从服务器接收到一个HTML页面的请求时&#xff0c;到屏幕上渲染出来要经过很多个步骤。浏览器完成这一系…

Openfire3.9.3源代码导入eclipse中开发配置指南(转载)

看到这篇文章的的网友应该已经安装了jdk,eclipse&#xff0c;我就不在安装这些开发工具上赘述了&#xff0c;附载一下openfire的下载地址&#xff1a;http://www.igniterealtime.org/downloads/index.jsp。1、下载源码openfire_src_3_9_3.zip&#xff0c;目前最新的版本是3.9.3…

Gulp在前端的常用操作实例

以前在做代码优化的时候&#xff0c;一般都用一些网上的在线工具来完成&#xff0c;写LESS的时候&#xff0c;一般用Koala来编译&#xff0c;感觉用起来也挺不错的。但是现在构建工具的出现&#xff0c;让以前做的那些繁琐操作变的更方便一些了&#xff0c;我在这里也用构建工具…

深入了解CSS字体度量,行高和vertical-align

本文英文出处:http: //iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align 著作权归作者所有。 转载自https: //www.w3cplus.com/css/css-font-metrics-line-height-and-vertical-align.html line-height和vertical-align在CSS中是两个简单的属性。如此简单&…

HTML5 Canvas制作雷达图实战

雷达图又叫蜘蛛网图&#xff0c;是一种对各项数据查看很明显的表现图&#xff0c;在很多游戏中&#xff0c;对游戏中的每个角色的分析图一般也用这种图。 下面&#xff0c;用HTML5的Cavas来实现雷达图。 效果 一、创建Canvas var mW 400; var mH 400; var mCtx null;var c…

AlphaBlend

AlphaBlend实现透明效果&#xff0c;只是仅仅能针对某块区域进行alpha操作&#xff0c;透明度可设。 TransparentBlt能够针对某种颜色进行透明&#xff0c;只是透明度不可设。 AlphaBlend&#xff1a; BLENDFUNCTION bn; bn.AlphaFormat 0; bn.BlendFlags 0; bn.BlendOp AC_…

ECMAScript 6网页样式修正器

最近在看ES6这一方面的图书&#xff0c;在搜索的过程中发现了《ECMAScript 6 入门-阮一峰》&#xff0c;感觉还不错。因为我个从比较喜欢看纸质的书&#xff0c;就想把这本书给打印下来。 但是网页版的《ECMAScript 6 入门-阮一峰》设置的样式只适合在网页上查看&#xff0c;并…

PWA(Progressive Web App)入门系列:(一)PWA简介

前言 PWA做为一门Google推出的WEB端的新技术&#xff0c;好处不言而喻&#xff0c;但目前对于相关方面的知识不是很丰富&#xff0c;这里我推出一下这方面的入门教程系列&#xff0c;提供PWA方面学习。 什么是PWA PWA全称Progressive Web App&#xff0c;直译是渐进式WEB应用…

Vue DevTools可使用修正方法

因为工作要求&#xff0c;目前主要在用Vue.js技术栈做开发&#xff0c;调试是必不可少的&#xff0c;这里会用的Vue DevTools的调试工具&#xff0c;问题就出在这里&#xff0c;当用Vue DevTools做调试时&#xff0c;很多时候都不能用&#xff0c;提示没有监测到Vue&#xff0c…

ZRender实现粒子网格动画实战

注&#xff1a;本博文代码基于ZRender 3.4.3版本开发&#xff0c;对应版本库地址&#xff1a;ZRender 库。 效果 实现分析 通过上面显示的效果图&#xff0c;可以看出&#xff0c;这种效果就是在Canvas中生成多个可移动的点&#xff0c;然后根据点之间的距离来确定是否连线&am…

CSS动画实战:创建一个太极Loading图

这里主要是使用CSS的animation和伪类来构建&#xff0c;分析设定关键帧的执行顺序和时间段。 效果 动画分析 首先通过效果对动画执行进行一下分析&#xff1a; 边框的四条边进行按顺序动画加载 。矩形边框变为圆行边框。太极图内部图案渐渐出现。太极图旋转。整个动画逆序执…

PWA(Progressive Web App)入门系列:(二)相关准备

前言 在上一章中&#xff0c;对PWA的相关概念做了基本介绍&#xff0c;了解了PWA的组成及优势。为了能够更快的进入PWA的世界&#xff0c;这一章主要对在PWA开发中&#xff0c;需要注意的问题&#xff0c;运行的环境及调试工具做介绍说明。 浏览器要求 因为目前各浏览器对于…

PWA(Progressive Web App)入门系列:(三)PWA关键技术Manifest

前言 前面说过&#xff0c;让Web App能够达到Native App外观体验的主要实现技术就是PWA中的manifest技术&#xff0c;本章会详细说明manifest的实现&#xff0c;及各个参数的具体含义&#xff0c;还将了解如何定义Web App的启动图标、启动样式等。 简介 manifest是一种简单的…

利用百度LBS做一个小Demo

为什么80%的码农都做不了架构师&#xff1f;>>> 申请ak&#xff08;即获取密钥&#xff09;http://lbsyun.baidu.com/apiconsole/key?applicationkey 去这儿注册一个开发者账号即可拼写发送http请求的url譬如这样的调用http://api.map.baidu.com/geocoder/v2/?ad…

PWA(Progressive Web App)入门系列:(四)Promise

前言 这一章说一下ES6的Promise对象。为什么要在PWA系列的文章中讲Promise呢&#xff1f;因为PWA中的许多技术API中都是以Promise返回的方式返回的&#xff0c;为了对后续章节中PWA技术API更好的理解&#xff0c;这里就来说一个Promise对象。 Promise出现的背景 在JavaScrip…

图文详解如何搭建Windows的Android C++开发环境

原地址:http://www.apkbus.com/android-18595-1-1.html ////TITLE:// 图文详解如何搭建Windows的Android C开发环境&#xff08;一&#xff09;//AUTHOR:// norains//DATE:// Thursday 14-April-2011//Environment:// Cygwin 1.7.9// Android NDK r5//1. 下载A…

PWA(Progressive Web App)入门系列:(五)Web Worker

前言 在说Service Worker前有必要说一下Web Worker&#xff0c;因为Service Worker本身就属于Web Worker的延伸&#xff0c;大部分功能也是基于Web Worker进行的扩展。 背景 众所周知&#xff0c;JavaScript引擎是以单线程调度的方式进行&#xff0c;我们无法同时运行多个Ja…

Glob Patterns匹配模式使用

前段时间在用workbox时&#xff0c;在做precache时&#xff0c;匹配模式基于的是Glob Pattern模式&#xff0c;于是就看了下相关文档。 下面翻译一下node-glob的使用&#xff0c;原文&#xff1a;https://github.com/isaacs/node-glob#glob-primer Glob 像在shell里面&#x…

Workbox CLI v3.x 中文版

在写PWA应用时&#xff0c;用到WorkBox工具&#xff0c;使用过程中发现没有中文的帮助文档&#xff0c;为了体验好一些&#xff0c;也为了方便自己和他人查看&#xff0c;在这里翻译了一下workbox-cli。 Workbox CLI 是什么? Workbox命令行&#xff08;在workbox-cli包内&…