Electron+vue实现多平台音乐搜索软件
- 先上演示
一、首现要知道audio的一些属性控制
<audio src="1.mp3" id="audio" />
audio 属性方法
<audio> 标签属性: src:音乐的URL preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制
video的属性和方法
<video> 标签属性: src:视频的URL poster:视频封面,没有播放时显示的图片 preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制条 width:视频宽度 height:视频高度
Media方法和属性:HTMLVideoElement 和 HTMLAudioElement 均继承自 HTMLMediaElement
var Media = document.getElementById("media"); //错误状态 Media.error; //null:正常 Media.error.code; //1.用户终止 2.网络错误 3.解码错误 4.URL无效 //网络状态 Media.currentSrc; //返回当前资源的URL Media.src = value; //返回或设置当前资源的URL Media.canPlayType(type); //是否能播放某种格式的资源 Media.networkState; //0.此元素未初始化 1.正常但没有使用网络 2.正在下载数据 3.没有找到资源 Media.load(); //重新加载src指定的资源 Media.buffered; //返回已缓冲区域,TimeRanges Media.preload; //none:不预载 metadata:预载资源信息 auto: //准备状态 Media.readyState; //1:HAVE_NOTHING 2:HAVE_METADATA 3.HAVE_CURRENT_DATA 4.HAVE_FUTURE_DATA 5.HAVE_ENOUGH_DATA Media.seeking; //是否正在seeking //回放状态 Media.currentTime = value; //当前播放的位置,赋值可改变位置 Media.startTime; //一般为0,如果为流媒体或者不从0开始的资源,则不为0 Media.duration; //当前资源长度 流返回无限 Media.paused; //是否暂停 Media.defaultPlaybackRate = value;//默认的回放速度,可以设置 Media.playbackRate = value;//当前播放速度,设置后马上改变 Media.played; //返回已经播放的区域,TimeRanges,关于此对象见下文 Media.seekable; //返回可以seek的区域 TimeRanges Media.ended; //是否结束 Media.autoPlay; //是否自动播放 Media.loop; //是否循环播放 Media.play(); //播放 Media.pause(); //暂停 //控制 Media.controls;//是否有默认控制条 Media.volume = value; //音量 Media.muted = value; //静音 //TimeRanges(区域)对象 TimeRanges.length; //区域段数 TimeRanges.start(index) //第index段区域的开始位置 TimeRanges.end(index) //第index段区域的结束位置
二、中如何使用音乐属性
<audio :src='url' controls="controls" ref="player" preload="true" @canplay="startPlay" @timeupdate="timeupdate" @ended="ended"> </audio>
1、关于音乐的上下曲播放问题
由于播放组件和列表组件是分开的,所以我这边是用父子传值 头部搜索组件
<head-nav @selected='searched'></head-nav>
底部播放组件
<fdooter :musicarryd='musicarry' ref="footed" @getactive="getactive"></fdooter>
播放列表
<li :class="{selected:active==index}" v-for="(item,index) in musicarry" :key="index" @click="playUrl(item,index)"> <div class="li_left pubspan"> <span>{{index+1}}</span> <span><i class="el-icon-star-off"></i></span> <span @click.stop="playUrl()" @click="downMusic(item)"><i class="el-icon-bottom"></i></span> <span>{{item.title}}</span> </div> <div class="li_right pubspan"> <span>{{item.author}}</span> <span> <img :src="item.pic" alt=""> </span> </div> </li>
2、初始化值
当列表搜索完数据后,通过this.$refs.footed.musicarry = music将数据传给播放组件里面 播放组件里通过
watch: { musicarry (newVal, oldVal) { if(this.musicarryd.length>0){ this.musicarry = this.musicarryd this.url = this.musicarry[0].url }--获取到初始化第一个值 }, },
3、播放
通过 获取到音乐的播放器的id,然后通过audio的属性play实现播放
let audio = this.audio = document.getElementById('audio') audio.play() --播放
4、上下曲播放
上下曲播放有很多方法,可已根据当前播放的URL和列表数组里去遍历和这一个URL相等的那个数组的索引,当点击下一曲时,把那个索引加一,然后获取到这个数组的当前加一的索引,上一曲也是一样,不过这样有个问题,就是点击下一曲时,如有15个歌曲的数组 我判断当前的索引加一小于15
if(index+1<this.musicarry.length){ --你会发现14+1<15也走这个 里面 }else{ }
所以我使用的是自定义的索引值active,默认为0, 当默认初始化数据播放时,active为0,当点击页面播放时,会将当前音乐的url和索引传给子组件
this.$refs.footed.url = item.url this.$refs.footed.active = index
父组件实现点击播放和暂停
playUrl(item,index){ //点击列表播放 this.active = index this.currentUrl = item.url this.$refs.footed.url = item.url this.$refs.footed.active = index if(item.play == false){ this.$refs.footed.play = true setTimeout(() => { this.$refs.footed.playAudio() }, 200); this.musicarry[index].play = true }else{ this.$refs.footed.play = false this.$refs.footed.stopAudio() this.musicarry[index].play = false } this.$store.commit('playstatus',item) },
子组件通过
previous(){ //上一曲 if(this.musicarry.length>1){ if(this.active>0){ this.songprev('reduce',this.active-1) this.active = this.active-1 }else{ this.songprev('last',this.musicarry.length-1) this.active = this.musicarry.length-1 } } }, handnext(){ //下一曲 if(this.musicarry.length>1){ if(this.active<this.musicarry.length){ this.active = this.active+1 if(this.musicarry[this.active]!=undefined){ this.songprev('next',this.active) }else{ this.songprev('first',0) this.active = 0 } }else{ this.songprev('first',0) this.active = 0 } } }, songprev(type,index){ //调用上下曲播放 this.url = this.musicarry[index].url this.$emit("getactive",index) setTimeout(() => { this.playAudio() }, 200); }, playAudio(){ //开始播放 let audio = this.audio = document.getElementById('audio') this.play = false if(this.url){ audio.play() if((this.duration>0)&&(this.duration==this.currentTime)){ this.play = false } } },
当进行上下曲播放时,由于获取到音乐的url需要时间,所以当不设置延迟几毫秒立即播放的话会出现播放失败,所以要演示100——200ms就行了
5、音乐快进
使用音乐的两个属性
@timeupdate="updateTime" @loadedmetadata="onLoadedmetadata" onLoadedmetadata(res) { // 当加载语音流元数据完成后,会触发该事件的回调函数 this.duration = parseInt(res.target.duration) --音乐总时长 }, updateTime(e) { // 当timeupdate事件大概每秒一次,用来更新音频流的当前播放时间 this.currentTime = e.target.currentTime; //获取audio当前播放时间 this.slider = parseInt(this.currentTime / this.duration * 100) -- },
我使用的是element的UI组件,所以滑块进度用的是el-slider 初始化获取到音乐的url时,就要给slider赋值
this.slider = parseInt(this.currentTime / this.duration * 100)
当进度条改变时,也要改变当前音乐的进度
chanslide(value){ //改变 this.currentTime = parseInt(value / 100 * this.duration) this.$refs.audio.currentTime = this.currentTime this.playAudio() },
6、音量控制
control_volume(value){ //音量改变控制 this.$refs.audio.volume = value / 100 this.volume = value }
主要代码 父组件代码
<template> <div> <el-container class='main'> <el-header> <head-nav @selected='searched'></head-nav> </el-header> <el-container> <el-aside width="200px" class='scroll'> <Aside></Aside> </el-aside> <el-container> <el-main class='scroll'> <ul v-loading="loading"> <li :class="{selected:active==index}" v-for="(item,index) in musicarry" :key="index" @click="playUrl(item,index)"> <div class="li_left pubspan"> <span>{{index+1}}</span> <span><i class="el-icon-star-off"></i></span> <span @click.stop="playUrl()" @click="downMusic(item)"><i class="el-icon-bottom"></i></span> <span>{{item.title}}</span> </div> <div class="li_right pubspan"> <span>{{item.author}}</span> <span> <img :src="item.pic" alt=""> </span> </div> </li> <img class='empty' v-if="musicarry.length==0" src="../assets/image/emptory.jpg" alt=""> </ul> <el-button class="loadingmore" v-if="musicarry.length>0" type="text" @click="uploading">加载更多</el-button> </el-main> <el-footer> <fdooter :musicarryd='musicarry' ref="footed" @getactive="getactive"></fdooter> </el-footer> </el-container> </el-container> </el-container> </div> </template> <script> var { ipcRenderer, remote, shell } = require("electron"); var remote = require("electron").remote; var dialog = remote.dialog; var path = require('path') var fs = require('fs'); import { mapGetters, mapActions } from 'vuex' import HeadNav from '@/components/head' import fdooter from '@/components/footer' import Aside from '@/components/aside' import { getsong } from '@/api/index' export default { name: '', components: { HeadNav, fdooter, Aside }, data () { return { dynamicValidateForm: { email: '', name: '', intresting: '', jobs: '' }, active:-1,//选中状态 tablelist: [], musicarry:[], currentUrl:'', loading:false,//加载状态 } }, mounted () { this.$refs.footed.url = '' //初始化url数据为空 }, methods: { searched(music){ //搜索返回 this.musicarry = [] this.active = 0 if(music!==''){ music.map((item)=>{ item.play = false }) this.musicarry = music this.$refs.footed.musicarry = music } }, getactive(val){ this.active = val }, playUrl(item,index){ //点击列表播放 this.active = index this.currentUrl = item.url this.$refs.footed.url = item.url this.$refs.footed.active = index if(item.play == false){ this.$refs.footed.play = true setTimeout(() => { this.$refs.footed.playAudio() }, 200); this.musicarry[index].play = true }else{ this.$refs.footed.play = false this.$refs.footed.stopAudio() this.musicarry[index].play = false } this.$store.commit('playstatus',item) }, uploading(){ //加载更多数据 this.loading = true let list = { input: this.$store.state.Counter.pragram.input, filter: this.$store.state.Counter.pragram.filter, type: this.$store.state.Counter.pragram.type, page: this.$store.state.Counter.pragram.page+1, size:15 } if(this.$store.state.Counter.pragram.page>0){ this.$store.commit('update',list) this.$http.post(api接口,list).then(res => { if(res.data.code=='200'){ //this.$store.commit('updatelist',res.data.data) this.musicarry = this.musicarry.concat(res.data.data) this.$refs.footed.musicarry = this.musicarry this.$store.state.Counter.playlist = this.$store.state.Counter.playlist.concat(res.data.data) setTimeout(() => { this.loading = false }, 300); }else{ this.loading = false this.$message('没有更多的数据了呢!'); } }).catch(()=>{ setTimeout(() => { this.loading = false }, 300); }) } }, downMusic(item){ //下载音乐 ipcRenderer.send('download',item); } } } </script>
播放组件代码
<template> <div class='playbody'> <audio @timeupdate="updateTime" @loadedmetadata="onLoadedmetadata" ref="audio" :src="url" id='audio' @ended="handnext()"></audio> <el-row :gutter="20"> <el-col :span="4"> <div class="play_contol"> <span @click="previous"><i class='iconfont conicon'></i></span> <span class='played' @click="playAudio" v-if="play"><i class="iconfont conicon"></i></span> <span class='played' @click="stopAudio" v-else><i class="iconfont conicon"></i></span> <span @click="handnext"><i class="iconfont conicon"></i></span> </div> </el-col> <el-col :span="12"> <div class="slider_control"> <span>{{ currentTime | formatSecond }}</span> <el-slider v-model="slider" :show-tooltip="false" :format-tooltip="formatTooltip" @change='chanslide'></el-slider> <span>{{ duration | formatSecond }}</span> </div> </el-col> <el-col :span="4"> <div class="volume"> <span @click='stopvolume'> <i class="iconfont conicon">{{muted==true?'':''}}</i> </span> <el-slider v-model="volume" :show-tooltip="false" @change='control_volume'></el-slider> </div> </el-col> <el-col :span="4"><div class="grid-content bg-purple"></div></el-col> </el-row> </div> </template> <script> // 将整数转换成 时:分:秒的格式 function realFormatSecond(second) { var secondType = typeof second if (secondType === 'number' || secondType === 'string') { second = parseInt(second) var hours = Math.floor(second / 3600) second = second - hours * 3600 var mimute = Math.floor(second / 60) second = second - mimute * 60 return hours + ':' + ('0' + mimute).slice(-2) + ':' + ('0' + second).slice(-2) } else { return '0:00:00' } } export default { name: "footer", props: { musicarryd:{ type:Array, defalut:null } }, components: {}, data() { return { play:true, muted:true,//音量状态 slider:0,//音乐进度 volume:0,//音量进度 url:'',//音乐链接地址 duration:0, //音乐总时长 currentTime:0,//当前音乐播放时间 musicarry:[ {url:require('../../assets/heart.mp3')}, {url:require('../../assets/nowgo.mp3')}, {url:require('../../assets/shatan.mp3')} ], active:0 }; }, filters: { // 将整数转化成时分秒 formatSecond(second = 0) { return realFormatSecond(second) } }, watch: { musicarry (newVal, oldVal) { if(this.musicarryd.length>0){ this.musicarry = this.musicarryd this.url = this.musicarry[0].url } }, duration(val,old){ console.log('初始化值',val,old) if((this.duration>0)&&(this.duration==this.currentTime)){ console.log('时长',this.duration,this.currentTime) //this.next() } } }, mounted() { this.musicarry = this.$store.state.Counter.playlist console.log(888,this.$store.state) console.log(9898,this.musicarryd) if(this.musicarry){ this.url = this.musicarry[0].url } }, methods: { onLoadedmetadata(res) { // 当加载语音流元数据完成后,会触发该事件的回调函数 this.duration = parseInt(res.target.duration) }, updateTime(e) { // 当timeupdate事件大概每秒一次,用来更新音频流的当前播放时间 this.currentTime = e.target.currentTime; //获取audio当前播放时间 this.slider = parseInt(this.currentTime / this.duration * 100) }, playAudio(){ //开始播放 console.log(this.slider,this.currentTime) let audio = this.audio = document.getElementById('audio') this.play = false if(this.url){ audio.play() if((this.duration>0)&&(this.duration==this.currentTime)){ this.play = false } } }, stopAudio(){//暂停播放 this.play = true let audio = document.getElementById('audio') audio.pause() }, chanslide(value){ //改变 this.currentTime = parseInt(value / 100 * this.duration) this.$refs.audio.currentTime = this.currentTime this.playAudio() }, formatTooltip(val) { //格式化 val = parseInt(this.duration / 100 * val) return '进度条: ' + realFormatSecond(val) }, previous(){ //上一曲 if(this.musicarry.length>1){ if(this.active>0){ this.songprev('reduce',this.active-1) this.active = this.active-1 }else{ this.songprev('last',this.musicarry.length-1) this.active = this.musicarry.length-1 } } }, handnext(){ //下一曲 if(this.musicarry.length>1){ if(this.active<this.musicarry.length){ this.active = this.active+1 if(this.musicarry[this.active]!=undefined){ this.songprev('next',this.active) }else{ this.songprev('first',0) this.active = 0 } }else{ this.songprev('first',0) this.active = 0 } } }, songprev(type,index){ //调用上下曲播放 this.url = this.musicarry[index].url this.$emit("getactive",index) setTimeout(() => { this.playAudio() }, 200); }, stopvolume(){ this.$refs.audio.muted = !this.$refs.audio.muted this.muted = !this.muted }, control_volume(value){ //音量改变控制 this.$refs.audio.volume = value / 100 this.volume = value } }, }; </script>