menu 山水有轻音
more_vert
electron+vue音乐集成软件
2020-11-06 | vue使用,Electron | 1 条评论 | 71 次阅读 | 1246字

Electron+vue实现多平台音乐搜索软件

  • 先上演示
    音乐演示gif

一、首现要知道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)属性:controls,preload(2)事件:canplay,timeupdate,ended(3)方法:play(),pause() -->

<!--controls:向用户显示音频控件(播放/暂停/进度条/音量)-->
<!--preload:属性规定是否在页面加载后载入音频-->
<!--canplay:当音频/视频处于加载过程中时,会发生的事件-->
<!--timeupdate:当目前的播放位置已更改时-->
<!--ended:当目前的播放列表已结束时-->

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>
None
发表评论
已有 1 条评论
textsms
account_circle
email
link
    山水有轻音
    November 27th, 2020 at 06:03 pm

    讲的很详细,很赞