美文网首页
00002-vue-mock-musicAPP-MOB3

00002-vue-mock-musicAPP-MOB3

作者: 前端辉羽 | 来源:发表于2020-01-15 17:46 被阅读0次

    本文目录

    • 1.全屏播放器动画
    • 2.配置vuex
    • 3.歌曲的播放与模式转换
    • 4.进度条
    • 5.歌词的滚动效果
    • 6.删除歌曲

    1.全屏播放器动画

    项目的所有项目下默认都会有一个默认的mini播放器,同时也会有一个专门的全屏播放器也页面,这两个播放器显示和隐藏是相反的,所以我们在新建的player组件中(我们把player组件直接引入到App.vue中),可以在data中定义一个fullScreen变量(默认值为true),然后对两个播放器的div进行绑定

        <div>
            <div v-show="!fullScreen" class="mini-player"></div>
            <div v-show="fullScreen" class="player"></div>
        </div>
    

    两个播放器的z-index都不小于前面所说的page样式,否则会显示不出来,可以也设置成9999

    当mini播放器和全屏播放器切换的时候,全屏播放器会有一个透明度从0到1的过渡动画,同时全屏播放器的头部区域player-header有一个从上往下的过渡以及底部player-operate有一个从下往上的过渡。
    把全屏播放器player包裹在<transition name="player">标签中,然后添加上样式代码就可以实现效果了

    .player-enter-active,
    .player-leave-active {
        transition: all 0.3s;
        opacity: 1;
    
        .player-header,
        .player-operate {
            transform: translate3d(0, 0, 0);
            transition: all 0.3s cubic-bezier(0.86, 0.18, 0.82, 1.32);
        }
    }
    
    .player-enter,
    .player-leave-to {
        opacity: 0;
    
        .player-header {
            transform: translate3d(0, -100px, 0);
        }
    
        .player-operate {
            transform: translate3d(0, 100px, 0);
        }
    }
    

    2.配置vuex

    整个项目的很多个地方都可以控制播放器,很显然这需要大量的跨组件通讯,所以需要配置vuex进行状态管理
    vue-cli创建目录文件中本来就知道store.js,但是state、mutations等都是集合在一起的,我们需要新建一个store文件夹,去进行进一步的细分和管理。
    新建actions.js

    const actions = {}
    export default actions
    

    getters.js

    const getters= {}
    export default getters
    

    mutations.js

    const mutations= {}
    export default mutations
    

    state.js

    const state= {}
    export default state
    

    index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    import state from './state'
    import mutations from './mutations'
    import getters from './getters'
    import actions from './actions'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
        state,
        mutations,
        getters,
        actions
    })
    
    export default store
    

    最后修改一下项目入口文件main.js的store引入路径就完成了vuex的配置了
    import store from './store/index';

    3.歌曲的播放与模式转换

    使用示例:歌曲的循环模式mode,mode的值我们可以设置的更加语义化,同时把这个值的设置单独写出来,写在common文件夹的js文件夹中的aliasConfig.js中

    export const playMode = {
        sequence:0,
        loop:1,
        random:2
    }
    

    在state中引用playMode并且定义mode

    import { playMode } from '../../common/js/aliasConfig'
    const state = {
        mode:playMode.sequence,
    }
    export default state
    

    在mutations.js中定义改变mode的方法

    SET_MODE(state, val) {
        state.mode = val
    }
    

    在getters.js中去动态计算并返回这个值

    mode(state) {
        return state.mode
    }
    

    我们在要改变这个mode值的页面中先使用语法糖引用mutations
    import { mapMutations } from 'vuex'
    然后就可以将mutations中的方法映射到methods属性中

    methods: {
        ...mapMutations([
            'SET_MODE'
        ]),
        addToPlay(item,index){
            this.SET_MODE(1)
        }
    }
    

    在刚开始拿到歌曲信息的时候,主要都是些歌曲的基本信息,歌曲的播放链接和歌词需要根据歌曲信息的id值去发送对应的请求获得。
    在拿到歌曲的链接之后我们就可以把链接赋值给页面中的audio标签
    <audio :src="musicData.url" ref="audio" @timeupdate="updataTime" @ended="end"></audio>
    通过togglePlay事件去控制音乐的播放与暂停

    togglePlay(val) {
        if (!this.currentSong) return;
        if (val === true || val === false) {
            this.playing = val;
        } else {
            this.playing = !this.playing;
        }
        const audio = this.$refs.audio;
        if (this.playing) {
            audio.play();
        } else {
            audio.pause();
        }
    }
    

    上一首下一首功能的实现是通过改变currentSongIndex的值来实现的,同时监听了currentSongIndex的变化,当currentSongIndex变化的时候,会触发请求歌曲信息以及歌曲资源等事件。

    切换播放模式,尤其是随机模式,打乱的是备份出来的播放列表,而且注意打乱之后,当前播放歌曲的index也需要进行更新

    const newIndex = newPlayList.findIndex(
        item => item.id === this.currentSong.id
    );
    

    打乱播放列表的函数:

    getRandomList(arr) {
        const newArr = [].concat(arr);
        return newArr.sort((a, b) => (Math.random() > 0.5 ? -1 : 1));
    }
    

    三种播放模式的样式判断(在计算属性computed中)

    modeIcon() {
        return this.mode === playMode.sequence
            ? "icon-liebiaoxunhuan"
            : this.mode === playMode.loop
            ? "icon-danquxunhuan"
            : "icon-suiji";
    }
    

    把其绑定在启动的标签上
    <i class="iconfont ft-40" :class="modeIcon"></i>

    4.进度条

    audio标签自带timeupdate事件,当歌曲播放的时候会去自动触发
    @timeupdate="updataTime"

    updataTime(e) {
        if (!this.touchbarWillMove) {
            this.currentTime = e.target.currentTime;
            this.overTime = e.target.duration;
        }
        // 歌词滚动
        if (this.lyricData) {
            this.moveLyric();
        }
    }
    

    计算已播放的百分比(在计算属性computed中)

    barPercent() {
        let per = this.currentTime / this.overTime;
        if (per === 0) {
            return 0;
        }
        //四舍五入
        per = Number(per * 100).toFixed();
        return `${per}%`;
    }
    

    这个百分比我们可以动态的赋值给进度条的width和小圆点的left
    :style="{width:${barPercent}}"

    实现小圆点的拖动功能

    <div
        class="bar-btn"
        :style="{left:`${barPercent}`}"
        @touchmove.prevent="progressMove"
        @touchend="progressEnd"
    ></div>
    

    手指按着小圆点移动的时候触发touchmove=>progressMove
    结束的时候触发touchend=>progressEnd

    progressMove(e) {
        const pageX = e.touches[0].pageX;
        this.calcPercent(pageX);
    },
    calcPercent(x) {
        const offsetLeft = this.$refs.progressBar.offsetLeft;
        const barWidth = this.$refs.progressBar.clientWidth;
        // 拖动点相当于进度条所占的宽度
        let moveWidth = x - offsetLeft;
        if (moveWidth > barWidth) moveWidth = barWidth;
        if (moveWidth < 0) moveWidth = 0;
        let p = moveWidth / barWidth;
        this.currentTime = this.overTime * p;
    },
    progressEnd() {
        this.resetPlayer();
    },
    resetPlayer() {
        this.$refs.audio.currentTime = this.currentTime;
        this.togglePlay(true);
    },
    

    5.歌词的滚动效果

    一开始拿到的初始歌词数据是一整个字符串,每一句歌词以\n分割
    所以首先把其转换成数组,然后将每一行歌词的时间和真正的歌词分割出来,存放到真正可以使用的歌词信息数组lyricLines

    initLines() {
        this.lyricLines = [];
        if (this.lyricData) {
            const lines = this.lyricData.split("\n");
            for (let i = 0; i < lines.length; i++) {
                // 拿到某一行的值
                const line = lines[i];
                // 时间
                const timeExp = /\[(\d{2}):(\d{2}\.\d{2,3})\]/g;
                // 匹配到每一行的时间
                const result = timeExp.exec(line);
                if (result) {
                    // 计算出来每一行歌词的时间time
                    const time =
                        Number(result[1] * 60 * 1000) +
                        Number(result[2] * 1000);
                    const txt = line.replace(timeExp, "").trim();
                    this.lyricLines.push({
                        time,
                        txt
                    });
                }
            }
        }
    },
    

    把lyricLines渲染到歌词页面上并放置到scroll中,就可以静态展示歌词信息了,接下来实现歌词的动态滚动

    moveLyric() {
        this.currentLineNumber = this.findCurrentNumber(
            this.currentTime * 1000
        );
        // 当歌词高亮在超过第六行的时候才进行滚动,这样可以让高亮行在页面中间
        if (this.currentLineNumber > 6) {
            // scrollToElement是写在scroll组件里的方法
            // scrollTo也是写在组件里的,在本页面中引用后就可以直接使用
            this.$refs.lyricScroll.scrollToElement(
                this.$refs.lyricLine[this.currentLineNumber - 6],
                1000
            );
        } else {
            // 当高亮行小于6行的时候
            this.$refs.lyricScroll.scrollTo(0, 0, 1000);
        }
    },
    findCurrentNumber(time) {
        // 返回第一个被匹配到的歌词的下标
        for (let i = 0; i < this.lyricLines.length; i++) {
            if (time < this.lyricLines[i].time) {
                return i - 1;
            }
        }
        // 当当前播放时间大于所有歌词的时间时,默认返回最后一行歌词的下标
        return this.lyricLines.length - 1;
    },
    

    这个动态滚动歌词的方法应该在歌词播放时间变化的时候去触发,并且在拖到进度条改变播放时间的时候触发。

    6.删除歌曲

    根据拿到的id值去找出播放列表中符合的那首歌曲,然后删除掉

    DEL_FROM_PLAY_LIST(state, val) {
        const index = state.playList.findIndex(item => item.id === val.delSong.id)
        state.playList.splice(index, 1)
        // 当删除的歌曲不是当前的歌曲的时候,
        // 根据传过来的当前播放歌曲的curSong.id,去查找更新后的歌曲列表playList
        // 拿到更新后的当前播放歌曲的新的currentIndex
        if (val.delSong.id !== val.curSong.id) {
            state.currentIndex = state.playList.findIndex(item => item.id === val.curSong.id)
        }
    },
    

    相关文章

      网友评论

          本文标题:00002-vue-mock-musicAPP-MOB3

          本文链接:https://www.haomeiwen.com/subject/wnuoactx.html