迷你播放器

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)
}
},

vue-music 关于Player (播放器组件)--播放和进度条的更多相关文章

  1. 阿里播放器踩坑记录 进度条重构 video loadByUrl失效解决方案

    如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ 如果解决不了,可以在文末进群交流. 文档地址:https://player.alicdn.com/aliplayer/index. ...

  2. html5 vedio 播放器,禁掉进度条快进快退事件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. 模仿迅L看看<音频播放器> 实现点击进度条,跳转播放

    <Style x:Key="btnFallback" TargetType="{x:Type Button}"> <Setter Proper ...

  4. 利用Docker挂载Nginx-rtmp(服务器直播流分发)+FFmpeg(推流)+Vue.js结合Video.js(播放器流播放)来实现实时网络直播

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_75 众所周知,在视频直播领域,有不同的商家提供各种的商业解决方案,其中比较靠谱的服务商有阿里云直播,腾讯云直播,以及又拍云和网易云 ...

  5. iOS 多个播放器同时播放,双击全屏,单击退出全屏

    前言:公司需求如下:点击一个按钮播放一个视频,最多同时播放4个:双击某视频让其全屏,单击再恢复原来的样子.IOS的播放器有两种,MPMoviePlayerController,AVAudioPlaye ...

  6. IOS 集成 Bilibili IJKPlayer播放器,播放rtmp视频流

    因为公司项目需要,我一个连iPhone都没用过的人竟然跑去开发iOS APP.近一段时间一直忙于赶项目,到今天差不多了,所以记录一下当时遇到的各种坑,先从ios 集成 ijkplayer播放器说起! ...

  7. 解决Ubuntu14.04下Clementine音乐播放器不能播放wma文件的问题

    参考:Ubuntu 14.04 安装深度音乐的方法 问题描述:播放wma文件时提示"GStreamer插件未安装". 解决方法:安装gstreamer-ffmpeg插件即可解决问题 ...

  8. 百度播放器SDK 播放MP4格式视频有声音无画面问题解决

    此处为记录解决过程. 所链接使用的MP4格式视频为codec id是mp4v-20.使用手机自带播放器可以播放,使用百度云媒体播放器不能无画面.经调试,Android Baidu-Cloud-Play ...

  9. java调用本地播放器播放视频文件。调用本地播放器不能播放指定文件的说明。

    public class OpenExe extends HttpServlet { //打开本地播放器并播放视频 public static void openExe(String file) { ...

随机推荐

  1. Java设计模式の工厂模式

    -------------------------------------------------------- 目录:  一.序言 二.简单工厂模式 三.工厂方法模式 四.简单工厂和工厂方法模式的比 ...

  2. intellij idea 中添加maven远程仓库

    在intellij idea 中配置好maven后 是这样的 如果加载失败,则需要自定义远程仓库,这里以阿里maven仓库为例, 在项目的pom文件中添加以下配置 <repositories&g ...

  3. Linux下Tomcat重启脚本

    我们重启Tomcat服务的时候,Tomcat自带的shutdown.sh脚本有时并不能真正杀死进程,经常需要我们用“kill -9 pid”的方式来杀死进程. 下面的脚本可以简化我们的操作,执行可杀死 ...

  4. 【BZOJ2815】【ZJOI2012】灾难 [LCA]

    灾难 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 阿米巴是小强的好朋友.  阿米巴和小强 ...

  5. 【51NOD-0】1011 最大公约数GCD

    [算法]欧几里德算法 #include<cstdio> int gcd(int a,int b) {?a:gcd(b,a%b);} int main() { int a,b; scanf( ...

  6. jq_从右向右的滑入滑出效果

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. spring boot web 入门

    ① 新建一个maven项目. ② pom中parent设为 spring-boot-starter-parent .建议使用最新的 RELEASE 版本.否则可能需要设置 <repositori ...

  8. pandas中DataFrame使用

    切片选择 #显示第一行数据print(df.head(1)) #显示倒数三行数据 print(df.tail(3)) loc  df.loc[row_index,col_index]  注意loc是根 ...

  9. windows 上启动appium

    import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecuteResultHandl ...

  10. beego 相关

    bee api bapi bee run -downdoc=true -docgen=true