基于 React + Webpack 的音乐相册项目(下)
上一篇我们完成了音乐相册里面的播放图片的功能,这一篇主要完成的是音乐相册里面的音乐播放器功能。最终让我们基于 React 的音乐相册图文并茂、有声有色。
我们主要从以下几个部分来展开:
数据准备
进度条功能
创建播放器组件
最终效果
数据准备
在src/data
目录添加音乐数据文件:musicDatas.js
代码如下:
export const MUSIC_LIST = [
{
id: 1,
title: '童话镇',
artist: '陈一发儿',
file: 'https://raw.githubusercontent.com/nnngu/SharedResource/master/music/%E7%AB%A5%E8%AF%9D%E9%95%87.mp3',
cover: 'https://raw.githubusercontent.com/nnngu/FigureBed/master/2018/2/6/tong_hua_zhen.jpg'
}, {
id: 2,
title: '天使中的魔鬼',
artist: '田馥甄',
file: 'http://oj4t8z2d5.bkt.clouddn.com/%E9%AD%94%E9%AC%BC%E4%B8%AD%E7%9A%84%E5%A4%A9%E4%BD%BF.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/%E9%AD%94%E9%AC%BC%E4%B8%AD%E7%9A%84%E5%A4%A9%E4%BD%BF.jpg'
}, {
id: 3,
title: '风继续吹',
artist: '张国荣',
file: 'http://oj4t8z2d5.bkt.clouddn.com/%E9%A3%8E%E7%BB%A7%E7%BB%AD%E5%90%B9.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/%E9%A3%8E%E7%BB%A7%E7%BB%AD%E5%90%B9.jpg'
}, {
id: 4,
title: '恋恋风尘',
artist: '老狼',
file: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%81%8B%E6%81%8B%E9%A3%8E%E5%B0%98.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%81%8B%E6%81%8B%E9%A3%8E%E5%B0%98.jpg'
}, {
id: 5,
title: '我要你',
artist: '任素汐',
file: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%88%91%E8%A6%81%E4%BD%A0.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%88%91%E8%A6%81%E4%BD%A0.jpg'
}, {
id: 6,
title: '成都',
artist: '赵雷',
file: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%88%90%E9%83%BD.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/%E6%88%90%E9%83%BD.jpg'
}, {
id: 7,
title: 'sound of silence',
artist: 'Simon & Garfunkel',
file: 'http://oj4t8z2d5.bkt.clouddn.com/sound-of-silence.mp3',
cover: 'http://oj4t8z2d5.bkt.clouddn.com/sound-of-silence.jpg'
}
];
进度条功能
1、在src/index.html
文件中添加一个div,作为jPlayer(音乐播放插件)的容器。如下图红色框里面的就是新添加的代码:
2、继续在src/index.html
文件中应用 jQuery 和 jPlayer 。
3、添加进度条组件:在src/components/music
目录添加 progress.js
,如下图:
progress.js
的代码如下:
import React from 'react';
require('./progress.less');
let Progress = React.createClass({
getDefaultProps() {
return {
barColor: '#2f9842'
}
},
changeProgress(e) {
let progressBar = this.refs.progressBar;
let progress = (e.clientX - progressBar.getBoundingClientRect().left) / progressBar.clientWidth;
this.props.onProgressChange && this.props.onProgressChange(progress);
},
// ......
// 省略了一部分代码
// 完整的代码请参照项目的源代码
});
export default Progress;
在同一个目录下创建Progress 的样式文件 progress.less
,代码如下:
.components-progress {
display: inline-block;
width: 100%;
height: 3px;
position: relative;
background: #aaa;
cursor: pointer;
.progress {
width: 0%;
height: 3px;
left: 0;
top: 0;
}
}
创建播放器组件
播放器组件分别对应player.js
和 player.less
两个文件。如下图:
player.js
的代码如下:
import React from 'react';
import Progress from './progress';
import {MUSIC_LIST} from '../../data/musicDatas';
let PubSub = require('pubsub-js');
require('./player.less');
let duration = null;
let Player = React.createClass({
/**
* 生命周期方法 componentDidMount
*/
componentDidMount() {
$('#player').bind($.jPlayer.event.timeupdate, (e) => {
duration = e.jPlayer.status.duration;
this.setState({
progress: e.jPlayer.status.currentPercentAbsolute,
volume: e.jPlayer.options.volume * 100,
leftTime: this.formatTime(duration * (1 - e.jPlayer.status.currentPercentAbsolute / 100))
});
});
$('#player').bind($.jPlayer.event.ended, (e) => {
this.playNext();
});
},
/**
* 生命周期方法 componentWillUnmount
*/
componentWillUnmount() {
$('#player').unbind($.jPlayer.event.timeupdate);
},
formatTime(time) {
time = Math.floor(time);
let miniute = Math.floor(time / 60);
let seconds = Math.floor(time % 60);
return miniute + ':' + (seconds < 10 ? '0' + seconds : seconds);
},
/**
* 进度条被拖动的处理方法
* @param progress
*/
changeProgressHandler(progress) {
$('#player').jPlayer('play', duration * progress);
this.setState({
isPlay: true
});
// 获取转圈的封面图片
let imgAnimation = this.refs.imgAnimation;
imgAnimation.style = 'animation-play-state: running';
},
/**
* 音量条被拖动的处理方法
* @param progress
*/
changeVolumeHandler(progress) {
$('#player').jPlayer('volume', progress);
},
/**
* 播放或者暂停方法
**/
play() {
this.setState({
isPlay: !this.state.isPlay
});
// 获取转圈的封面图片
var imgAnimation = this.refs.imgAnimation;
if (this.state.isPlay) {
$('#player').jPlayer('pause');
imgAnimation.style = 'animation-play-state: paused';
} else {
$('#player').jPlayer('play');
imgAnimation.style = 'animation-play-state: running';
}
},
/**
* 下一首
**/
next() {
PubSub.publish('PLAY_NEXT');
this.setState({
isPlay: true
});
// 开始播放下一首
this.playNext();
// 获取转圈的封面图片
let imgAnimation = this.refs.imgAnimation;
imgAnimation.style = 'animation-play-state: running';
},
/**
* 上一首
**/
prev() {
PubSub.publish('PLAY_PREV');
this.setState({
isPlay: true
});
// 开始播放上一首
this.playNext('prev');
// 获取转圈的封面图片
let imgAnimation = this.refs.imgAnimation;
imgAnimation.style = 'animation-play-state: running';
},
playNext(type = 'next') {
let index = this.findMusicIndex(this.state.currentMusitItem);
if (type === 'next') {
index = (index + 1) % this.state.musicList.length;
} else {
index = (index + this.state.musicList.length - 1) % this.state.musicList.length;
}
let musicItem = this.state.musicList[index];
this.setState({
currentMusitItem: musicItem
});
this.playMusic(musicItem);
},
playMusic(item) {
$('#player').jPlayer('setMedia', {
mp3: item.file
}).jPlayer('play');
this.setState({
currentMusitItem: item
});
},
findMusicIndex(music) {
let index = this.state.musicList.indexOf(music);
return Math.max(0, index);
},
changeRepeat() {
PubSub.publish('CHANAGE_REPEAT');
},
// constructor() {
// return {
// progress: 0,
// volume: 0,
// isPlay: true,
// leftTime: ''
// }
// },
componentWillMount() {
this.getInitialState();
},
getInitialState() {
return {
musicList: MUSIC_LIST,
currentMusitItem: MUSIC_LIST[0],
repeatType: 'cycle',
progress: 0,
volume: 0,
isPlay: true,
leftTime: ''
}
},
/**
* render 渲染方法
* @returns {*}
*/
render() {
return (
<div className="player-page">
<div className=" row">
<div className="controll-wrapper">
<h2 className="music-title">{this.state.currentMusitItem.title}</h2>
<h3 className="music-artist mt10">{this.state.currentMusitItem.artist}</h3>
<div className="row mt10">
<div className="left-time -col-auto">-{this.state.leftTime}</div>
<div className="volume-container">
<i className="icon-volume rt" style={{top: 5, left: -5}}></i>
<div className="volume-wrapper">
{/* 音量条 */}
<Progress
progress={this.state.volume}
onProgressChange={this.changeVolumeHandler}
// barColor='#aaa'
>
</Progress>
</div>
</div>
</div>
<div style={{height: 10, lineHeight: '10px'}}>
{/* 播放进度条 */}
<Progress
progress={this.state.progress}
onProgressChange={this.changeProgressHandler}
>
</Progress>
</div>
<div className="mt35 row">
<div>
<i className="icon prev" onClick={this.prev}></i>
<i className={`icon ml20 ${this.state.isPlay ? 'pause' : 'play'}`} onClick={this.play}></i>
<i className="icon next ml20" onClick={this.next}></i>
</div>
<div className="-col-auto">
{/* 播放模式按钮:单曲、循环、随机 */}
<i className={`repeat-${this.state.repeatType}`} onClick={this.changeRepeat}></i>
</div>
</div>
</div>
<div className="-col-auto cover">
<img ref="imgAnimation" src={this.state.currentMusitItem.cover} alt={this.state.currentMusitItem.title}/>
</div>
</div>
</div>
);
}
});
export default Player;
player.less
的代码如下:
.player-page {
width: 550px;
height: 210px;
//margin: auto;
//margin-top: 0px;
position: absolute;
left: 50%;
transform: translate(-50%, 0);
bottom: 20px;
z-index: 101;
//width: 100%;
//.caption {
// font-size: 16px;
// color: rgb(47, 152, 66);
//}
.cover {
width: 180px;
height: 180px;
margin-left: 20px;
img {
width: 180px;
height: 180px;
border-radius: 50%;
animation: roate 20s infinite linear; // 旋转专辑封面
border:2px solid #808080b8;
}
}
.volume-container {
position: relative;
left: 20px;
top: -3px;
}
.volume-container .volume-wrapper {
opacity: 0;
transition: opacity .5s linear;
}
.volume-container:hover .volume-wrapper {
opacity: 1;
}
.music-title {
font-size: 25px;
font-weight: 400;
color: rgb(3, 3, 3);
height: 6px;
line-height: 6px;
}
.music-artist {
font-size: 15px;
font-weight: 400;
color: rgb(74, 74, 74);
}
.left-time {
font-size: 14px;
color: #999;
font-weight: 400;
width: 40px;
}
.icon {
cursor: pointer;
}
.ml20 {
margin-left: 20px;
}
.mt35 {
margin-top: 35px;
}
.volume-wrapper {
width: 60px;
display: inline-block;
}
}
@keyframes roate {
0% {
transform: rotateZ(0)
}
100% {
transform: rotateZ(360deg)
}
}
然后在src/components/Main.js
中添加音乐播放器组件 Player
,完整的代码请参照我发布到 Github 上的源代码。
最终效果
到此,基于 React 的音乐相册的全部功能已经完成了。最终的运行效果如下:
源代码:https://github.com/nnngu/MusicPhoto
笔记仓库:https://github.com/nnngu/LearningNotes
基于 React + Webpack 的音乐相册项目(下)的更多相关文章
- 基于 React + Webpack 的音乐相册项目(上)
笔记仓库:https://github.com/nnngu/LearningNotes 上一篇文章用爬虫自动下载了一些图片,这一篇就用这些图片做一个音乐相册吧! 效果预览 点击图片,切换到背面: 演示 ...
- react.js 实现音乐播放、下一曲、以及删除歌曲(仅播放列表)
import React, { Component } from 'react'; export default class Music extends Component { construct ...
- react+es6+webpack环境搭建以及项目入门
前言:拖了这么久,小菜鸟终于开始正式应用react,和es6来开发项目了.之前超喜欢同学的一个博客风格,这里贴一下地址:https://iwenku.net/,PC端是他很久之前做的,最近他重新做了一 ...
- react+webpack搭建项目
一.环境准备 ①node ②npm 二.开始搭建 ①使用npm安装create-react-app工具,在cmd命令行中输入: npm install -g create-react-app ②使用命 ...
- 快速搭建一个基于react的项目
最近在学习react,快速搭建一个基于react的项目 1.创建一个放项目文件夹,用编辑器打开 2.打开集成终端输入命令: npm install -g create-react-app 3. cre ...
- 基于 React 封装的高德地图组件,帮助你轻松的接入地图到 React 项目中。
react-amap 这是一个基于 React 封装的高德地图组件,帮助你轻松的接入地图到 React 项目中. 文档实例预览: Github Web | Gitee Web 特性 ️ 自动加载高德地 ...
- 利用yeoman快速搭建React+webpack+es6脚手架
自从前后端开始分离之后,前端项目工程化也显得越来越重要了,之前写过一篇搭建基于Angular+Requirejs+Grunt的前端项目教程,有兴趣的可以点这里去看 但是有些项目可以使用这种方式,但有些 ...
- 成功解决react+webpack打包文件过大的问题
最近在学习并使用webpack+react+antd写了一个小项目,也可以说是demo,待全部开发完成后发现webpack的打包文件足足有将近13.3MB,快吓死宝宝了,经过连续几天的学习,和调试最后 ...
- 实例讲解基于 React+Redux 的前端开发流程
原文地址:https://segmentfault.com/a/1190000005356568 前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 s ...
随机推荐
- cobaltstrike安装加破解教程+使用教程
1.先安装java环境= = 1. 下载1.8u121的JAVA JDK (新的java JDK不稳定)!!原因:https://blog.cobaltstrike.com/2017/04/26/ja ...
- CTF---Web入门第十一题 PHP大法
PHP大法分值:20 来源: DUTCTF 难度:中 参与人数:8205人 Get Flag:2923人 答题人数:3042人 解题通过率:96% 注意备份文件 解题链接: http://ctf5.s ...
- COGS 1299. bplusa【听说比a+b还要水的大水题???】
1299. bplusa ☆ 输入文件:bplusa.in 输出文件:bplusa.out 评测插件 时间限制:1 s 内存限制:128 MB [题目描述] 输入一个整数n,将其拆为两 ...
- 2017ecjtu-summer training #2 POJ2503
...
- ThinkPHP基础知识
1.入口文件中定义的内容 // 检测PHP环境if(version_compare(PHP_VERSION,'5.3.0','<')) die('require PHP > 5.3.0 ! ...
- [国嵌攻略][060][LCD工作原理解析]
LCD硬件体系 1.LCD液晶屏 液晶属于一种有机化合物,分子形状为长棒状,在不同的电流作用下,分子会有规律旋转,这样对光线产生一定的控制形成一个像素,而很多像素右可以构成完整的图像. LCD是Liq ...
- 使用django建博客时遇到的URLcon相关错误以及解决方法。错误提示:类型错误:include0获得一个意外的关键参数app_name
root@nanlyvm:/home/mydj/mysite# python manage.py runserver Performing system checks... Unhandled exc ...
- v-for并判断当前元素是否选中:$set实现响应添加属性
前言 一直纠结着使用v-for进行列表渲染时如何为当前的元素添加是否选中的标识. 1.v-for进行列表渲染 <div class="lists"> <ul> ...
- 客户端怎么查看SVN的代码库
安装SVN客户端,比如TortoiseSVN,然后将代码库checkout到本地,或者通过客户端的版本库浏览器直接连接SVN服务器查看代码库的目录结构. 如果SVN服务器端安装的时候是和Apache集 ...
- base64是啥原理
Base64是一种基于64个可打印字符来表示二进制数据的表示方法.由于2的6次方等于64,所以每6个比特为一个单元,对应某个可打印字符.三个字节有24个比特,对应于4个Base64单元,即3个字节可表 ...