electron+vue音乐集成软件

简介: 上下曲播放有很多方法,可已根据当前播放的URL和列表数组里去遍历和这一个URL相等的那个数组的索引,当点击下一曲时,把那个索引加一,然后获取到这个数组的当前加一的索引,上一曲也是一样,不过这样有个问题,就是点击下一曲时,如有15个歌曲的数组 我判断当前的索引加一小于15

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


  • 先上演示


X6IGEJjCYDgc98pBKWSONfWKWsQMEXFmPLydULLF.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、关于音乐的上下曲播放问题


由于播放组件和列表组件是分开的,所以我这边是用父子传值 头部搜索组件


<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>


相关文章
|
12天前
|
人工智能 移动开发 JavaScript
如何用uniapp打包桌面客户端exe包,vue或者uni项目如何打包桌面客户端之electron开发-优雅草央千澈以开源蜻蜓AI工具为例子演示完整教程-开源代码附上
如何用uniapp打包桌面客户端exe包,vue或者uni项目如何打包桌面客户端之electron开发-优雅草央千澈以开源蜻蜓AI工具为例子演示完整教程-开源代码附上
|
19天前
|
安全 定位技术 API
婚恋交友系统匹配功能 婚恋相亲软件实现定位 语音社交app红娘系统集成高德地图SDK
在婚恋交友系统中集成高德地图,可实现用户定位、导航及基于地理位置的匹配推荐等功能。具体步骤如下: 1. **注册账号**:访问高德开放平台,注册并创建应用。 2. **获取API Key**:记录API Key以备开发使用。 3. **集成SDK**:根据开发平台下载并集成高德地图SDK。 4. **配置功能**:实现定位、导航及基于位置的匹配推荐。 5. **注意事项**:保护用户隐私,确保API Key安全,定期更新地图数据,添加错误处理机制。 6. **测试优化**:完成集成后进行全面测试,并根据反馈优化功能。 通过以上步骤,提升用户体验,提供更便捷的服务。
|
2月前
|
缓存 资源调度 JavaScript
Vue集成Excalidraw实现在线画板功能
Excalidraw是一款开源在线绘图工具,适用于白板、思维导图、原型设计等场景。支持手绘风格、多种图形元素、导出功能及多人协作,深受开发者喜爱。本文档介绍了如何在Vue项目中集成Excalidraw,包括安装依赖、配置文件修改、页面添加等步骤,帮助开发者快速上手。
188 0
Vue集成Excalidraw实现在线画板功能
|
4月前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
88 3
|
4月前
|
JavaScript
vue第一个项目之音乐播放器
vue第一个项目之音乐播放器
|
5月前
|
存储 JavaScript 前端开发
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
文章展示了在Vue项目中通过集成Quill富文本编辑器实现公告功能的完整开发过程,包括前端的公告发布、修改、删除操作以及后端的数据存储和处理逻辑。
Vue中通过集成Quill富文本编辑器实现公告的发布。Vue项目中vue-quill-editor的安装与使用【实战开发应用】
|
5月前
|
JavaScript Linux 开发工具
开源项目:使用 Atom-Electron 和 Vue.js 制作的简单 RSS 阅读器!!
开源项目:使用 Atom-Electron 和 Vue.js 制作的简单 RSS 阅读器!!
|
5月前
|
Web App开发 资源调度 前端开发
electron 中如何安装或更新 vuejs-devtool 最新稳定版
electron 中如何安装或更新 vuejs-devtool 最新稳定版
|
5月前
|
开发者 持续交付 Android开发
Xamarin开发者的秘密武器:如何通过持续集成与持续部署(CI/CD)实现高效、高质量的软件交付
【8月更文挑战第31天】在当今追求高效、高质量软件交付的时代,Xamarin开发者需像大厨般迅速烹制数字化佳肴,而持续集成(CI)与持续部署(CD)则是关键工具。CI要求开发者频繁将代码集成到共享仓库,利用自动化工具如Azure Pipelines或Jenkins自动编译、测试代码,确保质量。CD在此基础上进一步实现自动化部署,简化从开发到生产的全过程。借助如Visual Studio App Center这样的工具,Xamarin项目得以快速构建、测试并部署至Android和iOS平台,显著提升开发效率和代码质量,助力团队乘风破浪,驶向成功的彼岸。
38 0
|
5月前
|
前端开发 Java UED
JSF遇上Material Design:一场视觉革命,如何让传统Java Web应用焕发新生?
【8月更文挑战第31天】在当前的Web开发领域,用户体验和界面美观性至关重要。Google推出的Material Design凭借其独特的动画、鲜艳的颜色和简洁的布局广受好评。将其应用于JavaServer Faces(JSF)项目,能显著提升应用的现代感和用户交互体验。本文介绍如何通过PrimeFaces等组件库在JSF应用中实现Material Design风格,包括添加依赖、使用组件及响应式布局等步骤,为用户提供美观且功能丰富的界面。
59 0