使用vue.js+element ui制作音乐播放器

技术实现

通过axios加载网易云音乐API

功能包括

1.歌曲播放,暂停
2.切换上下首
3.显示歌词(自动滑动)
4.搜索音乐(分页)
5.加载评论
6.列表循环,单曲循环,列表随机
7.拉动进度条歌词自动滑动
更多功能可查看api文档https://autumnfish.cn/

外部文件引用

1.element.min.js
2.element.min.css
3.axios.min.js
4.axios.min.js

演示效果截图

1.歌曲
使用vue.js+element ui制作音乐播放器_第1张图片
2.歌词
使用vue.js+element ui制作音乐播放器_第2张图片
3.评论
使用vue.js+element ui制作音乐播放器_第3张图片

代码

DOCTYPE HTML>
<html lang="en">
<head>
    <title>听音乐title>
    <meta charset="utf-8">
    <meta name="renderer" content="webkit|ie-comp|ie-stand">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
    <link rel="stylesheet" type="text/css"  href="css/element.min.css" />
    <style>
		 li{
            list-style: none;
        }

        .playing {
            transform: rotate(360deg);
            animation: rotation 20s linear infinite;
        }

        @keyframes rotation {
            from {
                transform: rotate(0deg);
            }
            to {
                transform: rotate(360deg);
            }
        }
        .el-table .success-row {
            background: #98eed7 !important;
        }
        .el-table .active-row {
            background: #eef5f4 !important;
        }
    style>

head>

<body>
<div id="app" style="margin-top: 100px;">
    <tips>tips>
    <div style="width: 100%;height: 600px;position: relative">
        <div style="float: right;width: 40%;height: 600px">
            <img v-if="!lyricFlag"  @click="changeLyricFlag()" id="img" :src="imageUrl" width="300px" height="300px"
                 style="border-radius: 50%" :class="{playing:isplaying}">
            <div v-if="lyricFlag">
                <div v-if="lyric" style="width: 330px;height: 40px;background-color: #eef5f4;" >
                    <el-button @click="changeLyricFlag()" style="outline: none;margin: 5px;position: relative" size="mini">返回el-button>
                    <span style="margin-left: 20%">歌词span>
                div>
                <el-card id="lyric" v-if="lyric" style="width: 330px;height: 300px;overflow: auto;position: relative" class="box-card">
                    <el-table
                            :row-class-name="lyricRowClassName"
                            :data="lyric"
                            :row-style="{height: '0'}"
                            :cell-style="{padding: '3px'}"
                            style="width: 100%;text-align: center">
                        <el-table-column
                                prop="n"
                                label=""
                                width="260">
                            <template slot-scope="scope">
                        <span style="color: #8f9393;" >
                            {{scope.row[1]}}
                        span>
                            template>
                        el-table-column>
                    el-table>
                el-card>
            div>
            <br> <br>
                <el-radio-group style="border: 1px solid #f1f1f1;padding: 10px" v-model="type">
                    <el-radio :label="1">列表循环el-radio>
                    <el-radio :label="2">单曲循环el-radio>
                    <el-radio :label="3">列表随机el-radio>
                el-radio-group>
            <br><br>
            <el-button-group>
                <el-button @click="preSong()" size="medium" style="outline: none;height: 40px" type="default" >上一首el-button>
                <el-button style="outline: none;height: 40px" size="medium" type="default" ><marquee align="left" behavior="scroll" width="110px"  direction="left"  loop="-1" scrollamount="2" scrolldelay="2">{{text}}marquee>el-button>
                <el-button @click="lasSong()" size="medium" style="outline: none;height: 40px" type="default">下一首el-button>
            el-button-group>
            <br> <br>
            <audio id="audio" :src="url" controls autoplay @play="play" @pause="pause">audio>
        div>
        <div style="float: left;width: 30%;margin-left: 15%;height: 600px">
            <el-input  @keyup.enter.native="search" placeholder="搜索歌名或歌手" v-model="keyword" class="input-with-select">
                <el-button @click="search()" slot="append" ><svg t="1649990553012" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2028" width="16" height="16"><path d="M1005.312 914.752l-198.528-198.464A448 448 0 1 0 0 448a448 448 0 0 0 716.288 358.784l198.4 198.4a64 64 0 1 0 90.624-90.432zM448 767.936A320 320 0 1 1 448 128a320 320 0 0 1 0 640z" fill="#262626" p-id="2029">path>svg>el-button>
            el-input>
            <div id="tab" style="overflow: auto;height: 500px;">
                <el-table
                        v-loading="isShowLoading"
                        v-loading="loading"
                        element-loading-text="拼命加载中"
                        :row-class-name="tableRowClassName"
                        :data="songList"
                        style="width: 100%">
                    <el-table-column
                            type="index"
                            label="序号"
                            width="50">el-table-column>
                    <el-table-column
                            prop="name"
                            label="歌名"
                            width="180">
                        <template slot-scope="scope">
                        <span style="color: #0f9ae0;cursor: pointer" @click="getSing(scope.row.id,scope.$index)" >
                            {{scope.row.name}}
                        span>
                        template>
                    el-table-column>
                    <el-table-column
                            prop="ar[0].name"
                            label="歌手">
                    el-table-column>
                    <el-table-column
                            label=""
                            width="100">
                        <template slot-scope="scope">
                            <el-button type="text" size="small">
                                <span>
                                    <svg v-if="scope.row.id!=song.id" @click="getSing(scope.row.id,scope.$index)"  t="1650264949142" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1999" width="16" height="16"><path d="M512 0C230.4 0 0 230.4 0 512c0 281.6 230.4 512 512 512 117.76 0 227.84-38.4 320-110.08 10.24-7.68 12.8-23.04 5.12-35.84-7.68-10.24-23.04-12.8-35.84-5.12C719.36 939.52 616.96 972.8 512 972.8 256 972.8 51.2 768 51.2 512 51.2 256 256 51.2 512 51.2 768 51.2 972.8 256 972.8 512c0 87.04-25.6 171.52-69.12 243.2-7.68 12.8-2.56 28.16 7.68 33.28 12.8 7.68 28.16 2.56 33.28-7.68 51.2-79.36 76.8-174.08 76.8-271.36C1024 230.4 793.6 0 512 0z" p-id="2000">path><path d="M714.24 458.24c-17.92-15.36-245.76-222.72-245.76-222.72-10.24-10.24-25.6-7.68-35.84 2.56-5.12 5.12-7.68 12.8-7.68 17.92 0 0 0 0 0 0 0 0 0 499.2 0 512 0 15.36 10.24 25.6 25.6 25.6 5.12 0 12.8-2.56 15.36-7.68 2.56-2.56 217.6-186.88 240.64-207.36 23.04-20.48 33.28-38.4 33.28-64C742.4 491.52 732.16 473.6 714.24 458.24zM680.96 535.04c-7.68 5.12-204.8 176.64-204.8 176.64l0-399.36c0 0 186.88 166.4 202.24 181.76C696.32 512 698.88 519.68 680.96 535.04z" p-id="2001">path>svg>
                                span>
                                <span v-if="scope.row.id==song.id">
                                    <svg @click="play()" v-if="!isplaying"  t="1650264949142" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1999" width="16" height="16"><path d="M512 0C230.4 0 0 230.4 0 512c0 281.6 230.4 512 512 512 117.76 0 227.84-38.4 320-110.08 10.24-7.68 12.8-23.04 5.12-35.84-7.68-10.24-23.04-12.8-35.84-5.12C719.36 939.52 616.96 972.8 512 972.8 256 972.8 51.2 768 51.2 512 51.2 256 256 51.2 512 51.2 768 51.2 972.8 256 972.8 512c0 87.04-25.6 171.52-69.12 243.2-7.68 12.8-2.56 28.16 7.68 33.28 12.8 7.68 28.16 2.56 33.28-7.68 51.2-79.36 76.8-174.08 76.8-271.36C1024 230.4 793.6 0 512 0z" p-id="2000">path><path d="M714.24 458.24c-17.92-15.36-245.76-222.72-245.76-222.72-10.24-10.24-25.6-7.68-35.84 2.56-5.12 5.12-7.68 12.8-7.68 17.92 0 0 0 0 0 0 0 0 0 499.2 0 512 0 15.36 10.24 25.6 25.6 25.6 5.12 0 12.8-2.56 15.36-7.68 2.56-2.56 217.6-186.88 240.64-207.36 23.04-20.48 33.28-38.4 33.28-64C742.4 491.52 732.16 473.6 714.24 458.24zM680.96 535.04c-7.68 5.12-204.8 176.64-204.8 176.64l0-399.36c0 0 186.88 166.4 202.24 181.76C696.32 512 698.88 519.68 680.96 535.04z" p-id="2001">path>svg>
                                    <svg @click="pause()" v-if="isplaying"  t="1650265428253" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3012" width="16" height="16"><path d="M352 768c-17.664 0-32-14.304-32-32L320 288c0-17.664 14.336-32 32-32s32 14.336 32 32l0 448C384 753.696 369.664 768 352 768z" p-id="3013">path><path d="M672 768c-17.696 0-32-14.304-32-32L640 288c0-17.664 14.304-32 32-32s32 14.336 32 32l0 448C704 753.696 689.696 768 672 768z" p-id="3014">path>svg>
                                span>
                            el-button>
                        template>
                    el-table-column>
                el-table>
            div>
            <div class="block">
                <el-pagination
                        @size-change="handleSizeChange"
                        @current-change="handleCurrentChange"
                        :current-page.sync="currentPage"
                        :page-size="pageSize"
                        layout="prev, pager, next, jumper"
                        :total="total">
                el-pagination>
            div>
        div>
    div>
    <div v-if="song" style="width: 70%;margin: 0 auto">
        <h3><svg t="1650270638169" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2976" width="32" height="32"><path d="M371.2 454.4m-51.2 0a0.8 0.8 0 1 0 102.4 0 0.8 0.8 0 1 0-102.4 0Z" p-id="2977">path><path d="M512 454.4m-51.2 0a0.8 0.8 0 1 0 102.4 0 0.8 0.8 0 1 0-102.4 0Z" p-id="2978">path><path d="M652.8 454.4m-51.2 0a0.8 0.8 0 1 0 102.4 0 0.8 0.8 0 1 0-102.4 0Z" p-id="2979">path><path d="M377.6 896c-6.4 0-6.4 0-12.8 0-12.8-6.4-19.2-19.2-19.2-32l0-128C211.2 684.8 128 569.6 128 448c0-179.2 172.8-320 384-320s384 140.8 384 320c0 172.8-166.4 313.6-371.2 320l-128 121.6C390.4 896 384 896 377.6 896zM512 192C332.8 192 192 307.2 192 448c0 102.4 76.8 192 192 236.8 12.8 6.4 19.2 19.2 19.2 32l0 76.8 83.2-76.8C492.8 704 505.6 704 512 704c179.2 0 320-115.2 320-256S691.2 192 512 192z" p-id="2980">path>svg>评论区h3>
            <el-table
                    :data="reviewList"
                    style="width: 100%;">
                <el-table-column
                        prop="date"
                        label=""
                        height="30px"
                        width="1000px">
                    <template slot-scope="scope">
                        <li>
                            <img :src="scope.row.user.avatarUrl" style="border-radius: 50%;float: left" width="50px" height="50px">
                            <p style="float: left;width: 90%;position: relative">
                                <span style="font-weight: bold">{{scope.row.user.nickname}}:span><span>{{scope.row.content}}span>
                                <span style="float: right;position: relative">
                                    <span>{{scope.row.timeStr}}
                                span>span>
                            p>
                        li>
                    template>
                el-table-column>
            el-table>
        <div class="block" style="margin-top: 40px;margin-bottom: 30px;">
            <el-pagination
                    @size-change="commentHandleSizeChange"
                    @current-change="commentHandleCurrentChange"
                    :current-page.sync="commentCurrentPage"
                    :page-size="commentPageSize"
                    layout="prev, pager, next, jumper"
                    :total="commentTotal">
            el-pagination>
        div>
    div>
div>
<script src="js/vue.min.js">script>
<script src="js/axios.min.js">script>
<script src="js/element.min.js">script>
<script>
    var vm = new Vue({
        el: "#app",
        data: {
            songList: [],
            url: "",
            imageUrl: "images/music.png",
            reviewList: [],
            isplaying: false,
            hotSearch:[],
            keyword:"",
            text:"请选择歌曲播放",
            song:"",
            audio:"",
            index:-2,
            type: 1,
            lyric:"",
            lyricFlag:false,
            lyricIndex:0,
            timer:"",
            total:0,
            pageSize:10,
            currentPage:1,
            commentTotal:0,
            commentPageSize:10,
            commentCurrentPage:1,
            isShowLoading:false,
            loadingCount:0,
            searchKey:""
        },
        created(){
            this.config()
        },
        mounted(){
            this.getHotKeyword()
            this.audio =document.querySelector('#audio');
            this.audio.addEventListener('ended', this.playEndedHandler, false);
            audio.addEventListener('timeupdate', this.timeUpdate, false);
        },
        destroyed() {
            this.audio.removeEventListener('ended',this.playEndedHandler,false);
            this.audio.removeEventListener('timeupdate',this.playEndedHandler,false);
            clearInterval(this.timer)
        },
        methods: {
        	//显示加载中
            addLoading() {
                this.isShowLoading = true
                this.loadingCount++
            },
            //隐藏加载中
            isCloseLoading() {
                this.loadingCount--
                if (this.loadingCount == 0) {
                    this.isShowLoading = false
                }
            },
            //axios拦截器
            config(){
                // 添加请求拦截器
                axios.interceptors.request.use(config => {
                    var u=config.url
                    //url是搜索才显示加载
                    if(u.indexOf("search")!=-1){
                        this.addLoading()
                    }
                    return config
                }, error => {
                    this.isShowLoading = false
                    this.loadingCount = 0
                    alert('网络异常,请稍后再试')
                    return Promise.reject(error)
                })
                // 添加响应拦截器
                axios.interceptors.response.use(response => {
                    //url是搜索才显示加载
                    var u=response.config.url
                    if(u.indexOf("search")!=-1){
                        this.isCloseLoading()
                    }
                    return response
                }, error => {
                    this.isShowLoading = false
                    this.loadingCount = 0
                    alert('网络异常,请稍后再试')
                    return Promise.reject(error)
                })
            },
            handleSizeChange(val) {
            },
            commentHandleSizeChange(val) {
            },
            //列表分页重新搜索
            handleCurrentChange(val) {
                this.search();
            },
            //切换评论
            commentHandleCurrentChange(val) {
                this.getReviews(this.song.id)
                this.backTopComment()
            },
            //提示正在播发
            changeText(index){
                this.text="正在播放-"+this.songList[index].name+"-"+this.songList[index].ar[0].name
            },
            //提示已暂停
            changeText2(index){
                this.text="已暂停-"+this.songList[index].name+"-"+this.songList[index].ar[0].name
            },
            //歌词跟住进度条
            timeUpdate(){
                var data=parseInt(audio.currentTime);
                    this.lyricIndex=data*10
            },
            //是否显示歌词
            changeLyricFlag(){
                if(this.song==""){
                    return
                }
                this.lyricFlag=!this.lyricFlag
            },
            //上一首
            preSong(){
                this.isplaying=false
                var pre=this.index-1
                if(pre==-1){
                    pre=this.songList.length-1
                }
                if(this.index==-2){
                    pre=0
                }
                this.getSing(this.songList[pre].id,pre)
            },
            //下一首
            lasSong(){
                this.isplaying=false
                var las=this.index+1
                if(las==this.songList.length){
                    las=0
                }
                if(this.index==-2){
                    las=0
                }
                this.getSing(this.songList[las].id,las)
            },
            //歌曲结束前处理
            playEndedHandler(){
            	//继续播放
                if(this.type==2){
                    this.play()
                    return
                }
                //随机播放
                if(this.type==3){
                    var ran=Math.floor(Math.random()*this.songList.length);
                    this.getSing(this.songList[ran].id,ran)
                    return
                }
                //下一首播放
                this.isplaying=false
                var las=this.index+1
                if(las==this.songList.length){
                    las=0
                }
                this.getSing(this.songList[las].id,las)
            },
            //列表对应歌曲样式绑定
            tableRowClassName({row, rowIndex}) {
                if (row.id == this.song.id) {
                    this.delay()
                    return 'success-row';
                }
            },
          	//对应歌词样式绑定
            lyricRowClassName({row, rowIndex}) {
                if (row[0] <= (this.lyricIndex/10)&&row[2]>(this.lyricIndex/10)) {
                    this.moveLyric()
                    return 'active-row';
                }
                return ''
            },
            //歌曲列表滚动
            delay() {
                var self = this
                var t
                clearTimeout(t)
                t = setTimeout(function() {
                    let [parentDoc,childDoc]= [document.querySelector('#tab'),document.querySelector('.success-row')];
                    parentDoc.scrollTop = childDoc.offsetTop - parentDoc.offsetHeight /3 ; //如果大于div高度使其居中
                }, 500)
            },
            //歌词滚动
            moveLyric() {
                var self = this
                var t
                clearTimeout(t)
                t = setTimeout(function() {
                    let [parentDoc,childDoc]= [document.querySelector('#lyric'),document.querySelector('.active-row')];
                    parentDoc.scrollTop = childDoc.offsetTop - parentDoc.offsetHeight /6 ;
                }, 300)
            },
            //搜索歌曲
            search(){
                let that = this;
                if(this.keyword==""){
                    return
                }
                if(this.searchKey!=this.keyword){
                    this.currentPage=1
                }
                axios.get("https://autumnfish.cn/cloudsearch?offset="+(this.currentPage-1)*this.pageSize+"&limit="+10+"&keywords=" + this.keyword)
                    .then(function (response) {
                        that.songList = response.data.result.songs;
                        that.total=response.data.result.songCount
                        that.searchKey=that.keyword
                    }, function (error) {
                        console.log(error)
                    });
            },
            getHotSongs: function (keyword) {
                //回调函数需要使用that来传递this
                let that = this;
                for (const index in keyword) {
                    axios.get("https://autumnfish.cn/cloudsearch?limit=1&keywords=" + keyword[index].first)
                        .then(function (response) {
                            that.songList.push(response.data.result.songs[0]);
                        }, function (error) {
                            console.log(error)
                        });
                }
                this.total=10

            },
            //获取热门搜索关键字
            getHotKeyword: function () {
                //回调函数需要使用that来传递this
                let that = this;
                axios.get("https://autumnfish.cn/search/hot")
                    .then(function (response) {
                        that.hotSearch = response.data.result.hots;
                        that.getHotSongs(response.data.result.hots)
                    }, function (error) {
                        console.log(error)
                    });
            },
            //处理歌词
            splitLyric(str){
                this.lyric=[]
                let lyricArr = str.split('[').slice(1); 
                //存的数组,第一个值为播放的秒数,第二个值为歌词,第三个值为下一句的播放秒数
                let lrcObj = [];
                var i=0;
                lyricArr.forEach(item => {
                    let arr = item.split(']');
                    // 时间换算成秒
                    let m = parseInt(arr[0].split(':')[0])
                    let s = parseInt(arr[0].split(':')[1])
                    arr[0] = m*60 + s;
                    if (arr[1] != '\n') { // 去除歌词中的换行符
                        arr[2]=-1
                        lrcObj.push(arr)
                    }
                })
                for(;i<lrcObj.length-1;i++){
                    lrcObj[i][2]=lrcObj[i+1][0]
                }
                // 存储数据
                this.lyric = lrcObj;

            },
            //获取歌词
            getLyric: function () {
                //回调函数需要使用that来传递this
                let that = this;
                axios.get("https://autumnfish.cn//lyric?id="+this.song.id)
                    .then(function (response) {
                        that.splitLyric(response.data.lrc.lyric)
                    }, function (error) {
                        console.log(error)
                    });
            },
            //获得歌曲的在线url地址
            getSing: function (id,index) {
                let that = this;
                axios.get("https://autumnfish.cn/song/url?id=" + id)
                    .then(function (response) {
                        //注意返回的是数据数组,需要data[0]
                        that.url = response.data.data[0].url;
                        that.song=response.data.data[0]
                        that.index=index
                        that.changeText(index)
                        that.getLyric()
                    }, function (error) {
                        console.log(error);
                    })
                this.getImage(id);
                this.getReviews(id);
                this.isplaying=true
                clearInterval(this.timer)
            },
            //获取歌曲封面
            getImage: function (id) {
                let that = this;
                axios.get("https://autumnfish.cn/song/detail?ids=" + id)
                    .then(function (response) {
                        that.imageUrl = response.data.songs[0].al.picUrl;
                    }, function (error) {
                        console.log(error);
                    })
            },
            //获取歌曲评论
            getReviews: function (id) {
                let that = this;
                axios.get("https://autumnfish.cn/comment/hot?type=0&&offset="+this.commentCurrentPage*this.commentPageSize+"&id=" + id)
                    .then(function (response) {
                        that.reviewList = response.data.hotComments;
                        that.commentTotal=response.data.total

                    }, function (error) {
                        console.log(error);
                    })
            },
            //回到评论开始
            backTopComment() {
                const that = this
                let timer = setInterval(() => {
                    document.documentElement.scrollTop = document.body.scrollTop = 630
                    clearInterval(timer)
                }, 16)
            },
            //播放
            play: function () {
                var audio =document.querySelector('#audio');
                audio.play();
                this.isplaying = true;
                this.changeText(this.index)
            },
            //暂停
            pause: function () {
                var audio =document.querySelector('#audio');
                audio.pause();
                this.isplaying = false
                this.changeText2(this.index)
            },
        }
    });
script>
body>

html>

你可能感兴趣的:(前端,vue.js,前端)