`

基于 React + Webpack 的音乐相册项目(下)

 
阅读更多

笔记仓库:https://github.com/nnngu/LearningNotes


上一篇完成了音乐相册里面的相册功能,这一篇主要总结的是音乐相册里面的音乐播放器功能。

数据准备

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

0
0
分享到:
评论

相关推荐

    基于 React + Webpack 的音乐相册+源代码+文档说明

    基于 React + Webpack 的音乐相册。 演示地址 https://nnngu.github.io/MusicPhoto - 不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审...

    react-基于ReactWebpack的音乐相册

    在本项目"react-基于ReactWebpack的音乐相册"中,开发者使用了React库和Webpack构建工具来创建一个音乐相册应用。这是一个典型的前端开发案例,重点在于利用React的组件化特性来构建用户界面,以及Webpack的模块打包...

    js 实现相册特效案例

    在网页开发中,JavaScript(简称JS)是一种必不可少的前端编程语言,它被广泛用于实现...同时,随着Web技术的不断发展,还可以结合现代前端框架(如React、Vue等)进一步优化相册功能,实现更复杂的交互和性能优化。

    3D球体特效 相册 + 动态时钟 + 免扣

    现代前端框架如React或Vue.js也有成熟的组件库,提供现成的相册解决方案。 3. **动态时钟**:动态时钟是指在网页上实时显示当前时间的功能。这通常通过JavaScript实现,使用`Date`对象获取当前时间,并通过`...

    LastLightbox-develop-源码.rar

    1. **前端框架与库**:LastLightbox可能基于某个前端框架如React、Vue或Angular构建,或者使用JavaScript库如jQuery。通过查看源码,我们可以了解这些框架或库在实际项目中的应用,学习如何组织组件、管理状态和处理...

    遇见家

    "遇见家"项目是一个基于JavaScript技术的Web应用,旨在提供一个互动式的家庭体验平台。从提供的标签"JavaScript"我们可以推断,这个项目的核心技术栈是以JavaScript为基础的,可能包括前端开发、交互设计以及可能的...

    nextjs-materialui-album:[WIP]实践项目

    MaterialUI 是一个基于 Google 的 Material Design 设计规范的 React 组件库,它提供了一系列预先设计的 UI 组件,帮助开发者快速构建美观、响应式的用户界面。 在这个名为 "nextjs-materialui-album" 的实践中,...

    album_project

    "album_project"是一个基于JavaScript技术实现的项目,很可能是一个用于创建和展示相册的应用程序。JavaScript作为客户端脚本语言,广泛应用于网页动态效果和交互设计,使得用户可以在不刷新整个页面的情况下更新...

    bl-galeries

    在 "bl-galeries" 这个项目中,我们可以推测其主要功能可能包括展示图片、组织图片成不同的相册或者画廊,以及可能提供一些交互功能,如缩放、滑动和导航。由于没有具体的描述,我们将基于 TypeScript 的常见应用...

    photo-app

    "photo-app"是一个基于TypeScript开发的项目,主要聚焦于图像管理和编辑的应用程序。TypeScript是一种强类型、静态类型的超集语言,它扩展了JavaScript,提供了更好的工具支持和可维护性,尤其适合大型项目开发。在...

    pastbook-clone-frontend

    "pastbook-clone-frontend" 是一个前端项目,很可能是对某个在线相册或记忆册应用的克隆版。从项目名我们可以推测,它可能是基于Web的,目标是创建一个类似于Pastbook的服务,允许用户上传、编辑和分享他们的照片。 ...

    my-photo-app

    "my-photo-app" 是一个基于JavaScript技术开发的相册应用程序,它充分利用了Telerik平台的交互功能,为用户提供了一个完整的照片管理和分享体验。这个项目很可能包含HTML、CSS和JavaScript文件,用于构建用户界面和...

    monkeyGoHappy相机版本

    "MonkeyGoHappy相机版本"是一个基于JavaScript开发的项目,它可能是用于创建一款具有特定功能或体验的拍照应用。JavaScript是一种广泛使用的编程语言,尤其在Web开发中,它为前端交互提供了强大的支持。在这个项目中...

Global site tag (gtag.js) - Google Analytics