vue-music关于Player播放器组件详解

本文实例为大家分享了关于Player播放器组件的具体内容,供大家参考,具体内容如下

迷你播放器:

1.播放器组件会在各个页面的情况下会打开。 首先在vuex state.js 中定义全局的播放器状态

import {playMode} from 'common/js/config.js';

const state = {
 singer:{},
 playing:false, //是否播放
 fullScreen:false, //是否全屏
 playList:[], //播放列表
 sequenceList:[], // 非顺序播放列表
 mode:playMode.sequence, // 播放模式(顺序0,循环1,随机2)
 currentIndex:-1, //当前播放索引
}
export default state  

---------------------------------------------
// config.js

export const playMode = {
 sequence:0,
 loop:1,
 random:2
}

2.进入播放器页面时获取播放列表数据,改变播放状态   在music-list列表中打开

在song-list 组件中派发事件到父组件,传入当前歌曲的信息和索引

<li @click="selectItem(song,index)" v-for="(song,index) in songs" class="item">

------------------------------
selectItem(item,index){
 this.$emit('select',item,index)
},

在music-list 组件中接受派发事件。

<song-list :rank="rank" :songs="songs" @select="selectItem"></song-list> 

3. 如果commit 多个状态在actions 里设置

import {playMode} from 'common/js/config.js'

export const selectPlay = function({commit,state},{list,index}){
 commit(types.SET_SEQUENCE_LIST, list)
 commit(types.SET_PLAYLIST, list)
 commit(types.SET_CURRENT_INDEX, index)
 commit(types.SET_FULL_SCREEN, true)
 commit(types.SET_PLAYING_STATE, true)
}

4. 在music-list 组件中 用mapActions提交 改变值

import {mapActions} from 'vuex'

methods:{
  selectItem(item,index){
   this.selectPlay({
    list:this.songs,
    index
   })
  },
  ...mapActions([
   'selectPlay'
  ])
 },

5.在palyer 中获取vuex 全局状态,赋值状态到相应位置(代码为完整代码,对照后面讲解慢慢理解)

<div class="player" v-show="playList.length>0">    // 如果有列表数据则显示
    <div class="normal-player" v-show="fullScreen">  //如果全屏
     <div class="background">
      <img :src="currentSong.image" alt="" width="100%" height="100%">    //模糊背景图
     </div>
      <div class="top">
       <div class="back" @click="back">
        <i class="icon-back"></i>
       </div>
       <h1 class="title" v-html="currentSong.name"></h1>    //当前歌曲名称
       <h2 class="subtitle" v-html="currentSong.singer"></h2>  //当前歌手名
      </div>
      <div class="middle">
       <div class="middle-l">
        <div class="cd-wrapper">
         <div class="cd" :class="cdCls">
          <img :src="currentSong.image" alt="" class="image">    //封面图
         </div>
        </div>
       </div>
      </div>
      <div class="bottom">
     <div class="progress-wrapper">
      <span class="time time-l">{{ format(currentTime) }}</span>
      <div class="progress-bar-wrapper">
       <progress-bar :percent="percent" @percentChange="onProgressBarChange"></progress-bar>
      </div>
      <span class="time time-r">{{ format(currentSong.duration) }}</span>
     </div>
         <div class="operators">
        <div class="icon i-left">
         <i :class="iconMode" @click="changeMode"></i>
        </div>
          <div class="icon i-left" :class="disableCls">
            <i @click="prev" class="icon-prev"></i>
           </div>
           <div class="icon i-center" :class="disableCls">
            <i :class="playIcon" @click="togglePlaying"></i>
           </div>
           <div class="icon i-right" :class="disableCls">
            <i @click="next" class="icon-next"></i>
           </div>
           <div class="icon i-right">
            <i class="icon icon-not-favorite"></i>
           </div>
        </div>
       </div>
     </div>
     </transition>
     <transition name="mini">
       <div class="mini-player" v-show="!fullScreen" @click="open">
         <div class="icon">
           <img :src="currentSong.image" alt="" width="40" height="40" :class="cdCls">
         </div>
         <div class="text">
           <h2 class="name" v-html="currentSong.name"></h2>
           <p class="desc" v-html="currentSong.singer"></p>
         </div>
         <div class="control">
           <i :class="miniIcon" @click.stop="togglePlaying"></i>
         </div>
         <div class="control">
           <i class="icon-playlist"></i>
         </div>
       </div>
     </transition>
     <audio :src="currentSong.url" ref="audio" @canplay="ready" @error="error" @timeupdate="updateTime" @ended="end"></audio>
 </div>

打开播放器的状态

import {mapGetters,mapMutations} from 'vuex';

...mapGetters([
  'fullScreen',
  'playList',
  'currentSong',
  'playing',
  'currentIndex',
])

注意:不可在组件中直接赋值改版vuex 中的状态 this.fullScreen = false 需要通过mutations 改变,定义mutation-types 和mutations 然后 用vuex的 mapMutations 代理方法调用

[types.SET_FULL_SCREEN](state, flag) {
  state.fullScreen = flag
 },

import {mapGetters,mapMutations} from 'vuex';
methods:{
  ...mapMutations({
  setFullScreen:"SET_FULL_SCREEN",
  }),
  back(){
  this.setFullScreen(false)
  },
}

设置点击播放按钮方法

 <i :class="playIcon" @click="togglePlaying"></i>
togglePlaying(){
  this.setPlayingState(!this.playing);  //改变全局变量playing 的属性
},

// 然后watch 监听playing 操作实际的audio 标签的播放暂停
watch:{
   playing(newPlaying){
     let audio = this.$refs.audio;
     this.$nextTick(() => {
       newPlaying ? audio.play():audio.pause();
     })
   }
 },

// 用计算属性改变相应的播放暂停图标
playIcon(){
  return this.playing? 'icon-pause':'icon-play'
},

设置点击播放上一首和下一首按钮方法。用mapGetters 获取currentIndex 的值(加一或减一) 并改变,从而改变 currentSong 的状态,监听切换播放。判断播放列表界限重置。

prev(){
    if(!this.songReady){
      return;
    }

  let index = this.currentIndex - 1;
  if(index === -1){    //判断播放列表界限重置
    index = this.playList.length-1;
  }
  this.setCurrentIndex(index);
  if(!this.playing){  //判断是否播放改变播放暂停的icon
    this.togglePlaying();
  }
  this.songReady = false;
},
next(){
  if(!this.songReady){
    return;
   }
  let index = this.currentIndex + 1;
  if(index === this.playList.length){    //判断播放列表界限重置
    index = 0;
  }
  this.setCurrentIndex(index);
  if(!this.playing){
    this.togglePlaying();
  }
  this.songReady = false;
},

监听audio 元素标签的canpaly 事件,当歌曲加载就绪 和 error 事件,当歌曲发生错误的时候,做用户体验,防止用户快速切换导致报错。

设置songReady 标志位 如果歌曲没有准备就绪,点击下一首的时候直接return false

data(){
  return {
    songReady:false,
  }
},

ready(){
  this.songReady = true;
},
error(){
  this.songReady = true;
},

进度条

audio元素监听 timeupdate 事件获取当前播放时间的 可读写属性 时间戳。用formt做格式化时间处理,(_pad 为补零函数 )

获取音频总时长 currentSong.duration

<div class="progress-wrapper">
 <span class="time time-l">{{ format(currentTime) }}</span>
 <div class="progress-bar-wrapper">
  <progress-bar :percent="percent" @percentChange="onProgressBarChange"></progress-bar>
 </div>
 <span class="time time-r">{{ format(currentSong.duration) }}</span>
</div>

<audio :src="currentSong.url" ref="audio" @canplay="ready" @error="error" @timeupdate="updateTime" @ended="end"></audio>
updateTime(e){
 this.currentTime = e.target.currentTime;  // 获取当前播放时间段
},

format(interval){
 interval = interval | 0;
 const minute = interval/60 | 0;
 const second = this._pad(interval % 60);
 return `${minute}:${second}`;
},

_pad(num,n=2){
 let len = num.toString().length;
 while(len<n){
  num = '0' + num;
  len ++;
 }
 return num;
},

建立progress-bar 组件 接收pencent 进度参数,设置进度条宽度和小球的位置。player组件 设置计算属性percent

percent(){
 return this.currentTime / this.currentSong.duration   // 当前时长除以总时长
},

progress-bar 组件

<div class="progress-bar" ref="progressBar" @click="progressClick">
  <div class="bar-inner">
   <div class="progress" ref="progress"></div>
   <div class="progress-btn-wrapper" ref="progressBtn"
      @touchstart.prevent="progressTouchStart"
      @touchmove.prevent="progressTouchMove"
      @touchend="progressTouchEnd"
   >
    <div class="progress-btn"></div>
   </div>
  </div>
 </div>
const progressBtnWidth = 16  //小球宽度

props:{
 percent:{
  type:Number,
  default:0
 }
},

watch:{
 percent(newPercent){
  if(newPercent>=0 && !this.touch.initated){    
   const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
   const offsetWidth = newPercent * barWidth;
   this.$refs.progress.style.width = `${offsetWidth}px`;
   this.$refs.progressBtn.style.transform=`translate3d(${offsetWidth}px,0,0)`
  }
 }
}

设置拖动

在进度条小按钮progressBtn 上添加touchstart,touchmove,touchend 事件监听方法,事件添加 prevent 防止拖动默认浏览器行为,获取拖动的信息进行计算

在实例上创建一个touch 对象维护不同的回调之间的通讯共享状态信息。  touchstart事件方法中 ,首先设置this.touch.initated为true,表示拖动开始。  记录开始点击位置 e.touches[0].pageX 存到 touch 对象上,记录当前的进度宽度。

在touchmove 中首先判断 是否先进入了 touchstart 方法,计算得到 移动的位置 减去 点击开始的位置的 偏移量长度。 let deltax = e.touches[0].pageX - this.touch.startX

就可以 设置进度条 已有的长度加上偏移量长度。最大不能超过父级progressbar 的宽度

调用this._offset(offsetWidth) 方法设置进度条宽度

在touchend 事件方法中将 this.touch.initated 设置为false,表示拖动结束,并派发事件到player 组件将audio的currentTime 值设置为正确值,参数为pencent

在progressbar 中增加点击事件,调用this._offset(e.offsetX),并且派发事件

 created(){
  this.touch = {};
 },

methods:{
 progressTouchStart(e){
  this.touch.initiated = true;
  this.touch.startX = e.touches[0].pageX;
  this.touch.left = this.$refs.progress.clientWidth;
 },
 progressTouchMove(e){
  if(!this.touch.initiated){
   return;
  }
  let deltaX = e.touches[0].pageX - this.touch.startX;
  let offsetWidth = Math.min(this.$refs.progressBar.clientWidth - progressBtnWidth,Math.max(0,this.touch.left + deltaX));
  this._offset(offsetWidth);
 },
 progressTouchEnd(e){
  this.touch.initiated = false;
  this._triggerPercent();
 },
 progressClick(e){
  const rect = this.$refs.progressBar.getBoundingClientRect();
  const offsetWidth = e.pageX - rect.left;
  this._offset(offsetWidth);
  // this._offset(e.offsetX);
  this._triggerPercent();
 },
 _offset(offsetWidth){
  this.$refs.progress.style.width = `${offsetWidth}px`;
  this.$refs.progressBtn.style[transform] = `translate3d(${offsetWidth}px,0,0)`;
 },
 _triggerPercent(){
  const barWidth = this.$refs.progressBar.clientWidth - progressBtnWidth;
  const percent = this.$refs.progress.clientWidth / barWidth;
  this.$emit("percentChange",percent)
 }
},

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue音乐播放器插件vue-aplayer的配置及其使用实例详解

    昨天在vue的官网上看到vue-aplayer这个音乐播放器的插件,由于个人有比较喜欢音乐,所以就拿过来玩一玩,感觉还是比较实用的,界面美观. 首先,我们先安装 npm install vue-aplayer --save ,之后在组件中引入  import VueAplayer from 'vue-aplayer',下面就是源码,可供参考: <template> <div> <div style="padding:10px 0;"> <a-p

  • vue-music关于Player播放器组件详解

    本文实例为大家分享了关于Player播放器组件的具体内容,供大家参考,具体内容如下 迷你播放器: 1.播放器组件会在各个页面的情况下会打开. 首先在vuex state.js 中定义全局的播放器状态 import {playMode} from 'common/js/config.js'; const state = { singer:{}, playing:false, //是否播放 fullScreen:false, //是否全屏 playList:[], //播放列表 sequenceLi

  • ffmpeg播放器实现详解之框架搭建过程

    ffplay是ffmpeg源码中一个自带的开源播放器实例,同时支持本地视频文件的播放以及在线流媒体播放,功能非常强大. FFplay: FFplay is a very simple and portable media player using the FFmpeg libraries and the SDL library. It is mostly used as a testbed for the various FFmpeg APIs. ffplay中的代码充分调用了ffmpeg中的函

  • ffmpeg播放器实现详解之视频显示(推荐)

    FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.它包括了目前领先的音/视频编码库libavcodec. FFmpeg是在 Linux 下开发出来的,但它可以在包括 Windows 在内的大多数操作系统中编译.这个项目是由 Fabrice Bellard 发起的,现在由 Michael Niedermayer 主持.可以轻易地实现多种视频格式之间的相互转换,例如可以将摄录下的视频avi等转成现在视频网站所采用的flv格式. ffplay是ffmpeg源码中一个

  • vue设计一个倒计时秒杀的组件详解

    简介: 倒计时秒杀组件在电商网站中层出不穷  不过思路万变不离其踪,我自己根据其他资料设计了一个vue版的 核心思路: 1.时间不能是本地客户端的时间  必须是服务器的时间这里用一个settimeout代替 以为时间必须统一 2.开始时间,结束时间通过父组件传入,当服务器时间在这个开始时间和结束时间的范围内  参加活动按钮可以点击,并且参加过活动以后不能再参加, 3.在组件创建的时候 同步得到现在时间服务时间差,并且在这里边设置定时器,每秒都做判断看秒杀是否开始和结束, 4.在更新时间的函数中是

  • 如何在Vue.js中实现标签页组件详解

    前言 标签页组件,即实现选项卡切换,常用于平级内容的收纳与展示. 因为每个标签页的内容是由使用组件的父级控制的,即这部分内容为一个 slot.所以一般的设计方案是,在 slot 中定义多个 div,然后在接到切换消息时,再显示或隐藏相关的 div.这里面就把相关的交互逻辑也编写进来了,我们希望在组件中处理这些交互逻辑,slot 只单纯处理业务逻辑.这可以通过再定义一个 pane 组件来实现,pane 组件嵌在 tabs 组件中. 1 基础版 因为 tabs 组件中的标题是在 pane 组件中定义

  • 适用于 Vue 的播放器组件Vue-Video-Player操作

    如果h5的标签<vedio>不能满足你的需求,那就用这个组件Vue-Video-Player吧,也许可以覆盖到你的需求. <video-player class="video-player vjs-custom-skin" ref="videoPlayer" :playsinline="true" :options="playerOptions" ></video-player> 配置参数:

  • 基于Vue单文件组件详解

    本文将详细介绍Vue单文件组件 概述 在很多 Vue 项目中,使用 Vue.component 来定义全局组件,紧接着用 new Vue({ el: '#container '}) 在每个页面内指定一个容器元素. 这种方式在很多中小规模的项目中运作的很好,在这些项目里 JavaScript 只被用来加强特定的视图.但当在更复杂的项目中,或者前端完全由 JavaScript 驱动的时候,下面这些缺点将变得非常明显: 1.全局定义 (Global definitions) 强制要求每个 compon

  • vue + element ui实现播放器功能的实例代码

    没有效果图的展示都是空口无凭 1.基于audio并结合elementUI 的进度条实现 2.实现了播放器基本的功能,播放/暂停.快进.静音.调节声音大小.下载等功能 html代码,关键部分已加注释 <div class="right di main-wrap" v-loading="audio.waiting"> <!-- 此处的ref属性,可以很方便的在vue组件中通过 this.$refs.audio获取该dom元素 --> <au

  • Vue3实现简易音乐播放器组件

    前言 用Vue3实现一个简易的音乐播放器组件 其效果图如下所示: 实现这个组件需要提前做的准备: 引入ElementUI 引入字节跳动图标库 一张唱见图片 将要播放的音乐上传到文件服务器上,并提供一个能在线访问的链接[这里使用的是阿里云的OSS服务] 准备 ElementUI ElementUI的引入可以参照其官网的引入方式: 字节跳动图标库 组件的[上一首][播放][下一首][音量]等图标都是来源自这个图标库,这是其安装文档 在main.js中,我是这样引入的: //引入字节跳动图标库 imp

  • Vue中状态管理器(vuex)详解以及实际应用场景

    目录 Vue中 常见的组件通信方式可分为三类 Vuex简介 1. State 2. Getters 3. Mutations 4. Actions 5. 使用 mapState.mapGetters.mapActions 简化 总结 传送门:Vue中 子组件向父组件传值 及 .sync 修饰符 详解 传送门:Vue中 $ attrs.$ listeners 详解及使用 传送门:Vue中 事件总线(eventBus)详解及使用 传送门:Vue中 provide.inject 详解及使用 Vue中

随机推荐