前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue 2.x折腾记 - (13) Nuxt.js写一个常规音频的播放组件,动态注入微信,新浪微博的js-sdk

Vue 2.x折腾记 - (13) Nuxt.js写一个常规音频的播放组件,动态注入微信,新浪微博的js-sdk

作者头像
CRPER
发布2024-02-19 08:42:26
1330
发布2024-02-19 08:42:26
举报
文章被收录于专栏:CRPER折腾记CRPER折腾记

前言

只是一个常规的播放组件,需要考虑微信,微博这类环境的播放

微信和微博,若没有用其官方的js-sdk初始化,没法播放。

我的文章从来都不推崇copy,仅供参考学习..具体业务具体分析定制才是最合理的

前置基础

  • vue && vuex
  • ES5+
  • Nuxt的基本用法

这篇文章的内容需基于上篇内容的,要用到一些设备信息

效果图

这是当前服务端版本的效果,因为还没上线,LOGO已经马赛克

实现思路

之前老的客户端实现思路

  • 在主入口实现一个单例,绑定到vue.prototype
  • 在音频组件的beforeMount创建script标签,引入对应js,然后用promise拿到成功加入head的状态
  • vuex来维护播放状态
  • 在对应的函数初始化音频的加载,之后就可以正常使用了

服务端的思路也差不多

考虑的东西多些,在之前客户端实现的基础上加以完善

用中间件这些来动态注入js-sdk

代码实现

客户端渲染实现的版本

版本1

全部耦合到组件内,虽然可以正常播放(包括微信和微博) 且不是单例模式,对于多音频页面,有毒

代码语言:javascript
复制
<template>
  <div class="play-voice-area">
    <div class="cover-player">
      <div
        :class="playState?'active':''"
        class="cover-pic">
        <img :src="coverUrl ? coverUrl:defaultAvatar">
        <i
          :class="playState? 'sx-mobile-icon-':'sx-mobile-bofang'"
          class="sx-mobile cover-icon"
          @click="playAudio" />
      </div>
    </div>
    <div class="sound-desrc">
      <p class="username">{{ userName }}的声兮</p>
      <p class="timeline">{{ currentPlayTime }}/{{ voiceTime }}</p>
    </div>
  </div>
</template>

<script>
export default {
    props: {
        userName: {
            type: String,
            default: 'Super Hero'
        },
        duration: {
            type: [String, Number],
            default: ''
        },
        autoplay: {
            type: [Boolean, String],
            default: false
        },
        sourceUrl: {
            type: String,
            default: ''
        },
        coverpic: {
            type: String,
            default: ''
        }
    },
    data() {
        return {
            defaultAvatar: require('@/assets/share/yourAppIcon@2x.png'), // 默认头像
            audioElm: '', // 音频播放器 DOM
            soundCurrentStopTime: '', // 当前声音暂停的时间戳
            playState: false, // 播放状态的图标控制
            timeStepState: '', // 时间迭代
            voicePlayMessage: '', // 音频资源的状况
            currentPlayTime: '00:00', // 当前播放的时间,默认为0
            cacheCurrentTime: 0 // 缓存播放时间
        };
    },
    computed: {
        coverUrl() {
            if (!this.coverpic) {
                return this.defaultAvatar;
            }
            return this.coverpic;
        },
        voiceTime() {
            if (this.duration) {
                return this.second2time(Number(this.duration));
            }
        }
    },
    watch: {
        sourceUrl(newVal, oldVal) {
            if (newVal) {
                this.playAudio();
            }
        }
    },
    created() {
        this.$store.commit('OPEN_LOADING');
    },
    beforeMount() {
        // 初始化音频播放器
        this.initAudioElm();
    },
    mounted() {
        // 检测微博微信平台
        this.checkWeiBo_WeiChat();
        this.audioElm.addEventListener('stalled', this.stalled);
        this.audioElm.addEventListener('loadstart', this.loadstart);
        this.audioElm.addEventListener('loadeddata', this.loadeddata);
        this.audioElm.addEventListener('canplay', this.canplay);
        this.audioElm.addEventListener('ended', this.ended);
        this.audioElm.addEventListener('pause', this.pause);
        this.audioElm.addEventListener('timeupdate', this.timeupdate);
        this.audioElm.addEventListener('error', this.error);
        this.audioElm.addEventListener('abort', this.abort);
    },
    beforeDestroy() {
        this.audioElm.removeEventListener('loadstart', this.loadstart);
        this.audioElm.removeEventListener('stalled', this.stalled);
        this.audioElm.removeEventListener('canplay', this.canplay);
        this.audioElm.removeEventListener('timeupdate', this.timeupdate);
        this.audioElm.removeEventListener('pause', this.pause);
        this.audioElm.removeEventListener('error', this.error);
        this.audioElm.removeEventListener('ended', this.ended);
    },
    methods: {
        initAudioElm() {
            let audio = new Audio();
            audio.autobuffer = true; // 自动缓存
            audio.preload = 'metadata';
            audio.src = this.sourceUrl;
            audio.load();
            this.audioElm = audio;
        },
        checkWeiBo_WeiChat() {
            let ua = navigator.userAgent.toLowerCase(); // 获取判断用的对象
            const script = document.createElement('script');
            if (/micromessenger/.test(ua)) {
                // 返回一个独立的promise
                script.src = 'https://res.wx.qq.com/open/js/jweixin-1.2.0.js';
                new Promise((resolve, reject) => {
                    let done = false;
                    script.onload = script.onreadystatechange = () => {
                        if (
                            !done &&
                                (!script.readyState ||
                                    script.readyState === 'loaded' ||
                                    script.readyState === 'complete')
                        ) {
                            done = true;

                            // 避免内存泄漏
                            script.onload = script.onreadystatechange = null;
                            resolve(script);
                        }
                    };

                    script.onerror = reject;
                    document
                        .getElementsByTagName('head')[0]
                        .appendChild(script);
                }).then(res => {
                    this.initWeixinSource();
                });
            }
            if (/WeiBo|weibo/i.test(ua)) {
                script.src =
                        'https://tjs.sjs.sinajs.cn/open/thirdpart/js/jsapi/mobile.js';
                new Promise((resolve, reject) => {
                    let done = false;
                    script.onload = script.onreadystatechange = () => {
                        if (
                            !done &&
                                (!script.readyState ||
                                    script.readyState === 'loaded' ||
                                    script.readyState === 'complete')
                        ) {
                            done = true;

                            // 避免内存泄漏
                            script.onload = script.onreadystatechange = null;
                            resolve(script);
                        }
                    };

                    script.onerror = reject;
                    document
                        .getElementsByTagName('head')[0]
                        .appendChild(script);
                }).then(res => {
                    this.initWeiboSource();
                });
            }
        },
        canplay() {
            this.$store.commit('CLOSE_LOADING');
        },
        initWeixinSource() {
            wx.config({
                // 配置信息, 即使不正确也能使用 wx.ready
                debug: false,
                appId: '',
                timestamp: 1,
                nonceStr: '',
                signature: '',
                jsApiList: []
            });
            wx.ready(() => {
                let st = setTimeout(() => {
                    clearTimeout(st);
                    this.audioElm.load();
                }, 50);
            });
        },
        initWeiboSource() {
            window.WeiboJS.init(
                {
                    appkey: '3779229073',
                    debug: false,
                    timestamp: 1429258653,
                    noncestr: '8505b6ef40',
                    scope: [
                        'getNetworkType',
                        'networkTypeChanged',
                        'getBrowserInfo',
                        'checkAvailability',
                        'setBrowserTitle',
                        'openMenu',
                        'setMenuItems',
                        'menuItemSelected',
                        'setSharingContent',
                        'openImage',
                        'scanQRCode',
                        'pickImage',
                        'getLocation',
                        'pickContact',
                        'apiFromTheFuture'
                    ]
                },
                ret => {
                    this.audioElm.load();
                }
            );
        },
        playAudio() {
            // 播放暂停音频
            if (this.audioElm.readyState > 2) {
                // 当资源可以播放的时候
                if (this.audioElm.paused) {
                    this.cacheCurrentTime === 0
                        ? (this.audioElm.currentTime = 0)
                        : (this.audioElm.currentTime = this.cacheCurrentTime);
                    this.playState = true;
                    this.audioElm.play();
                } else {
                    this.audioElm.pause();
                }
            }
        },
        second2time(currentTime) {
            // 秒数化为分钟
            let min = Math.floor(currentTime / 60); // 向下取整分钟
            let second = Math.floor(currentTime % 60); // 取模得到剩余秒数
            if (min < 10) {
                min = '0' + min;
            }
            if (second < 10) {
                second = '0' + second;
            }
            return `${min}:${second}`;
        },
        stalled() {
            // 资源需要缓存的时候暂停
            this.audioElm.pause();

            // 缓存加载待播的时候,若是当前播放时间已经走动则触发播放
            if (this.audioElm.currentTime !== 0) {
                // 判断当前播放的时间是否到达结束,否则则继续播放
                if (this.audioElm.currentTime !== this.audioElm.duration) {
                    this.playAudio();
                } else {
                    this.ended();
                }
            }
        },
        timeupdate() {
            if (
                this.audioElm.readyState > 2 &&
                    this.audioElm.currentTime > 0.2
            ) {
                this.cacheCurrentTime = this.audioElm.currentTime;
                this.currentPlayTime = this.second2time(
                    Number(this.audioElm.currentTime)
                );
                if (
                    this.audioElm.ended ||
                        this.audioElm.currentTime === this.audioElm.duration
                ) {
                    this.ended();
                }
            }
        },
        ended() {
            this.audioElm.pause();
            // 清除缓存的时间
            this.cacheCurrentTime = 0;
            this.voicePlayMessage = '';
        },
        pause() {
            // 当音频/视频已暂停时
            this.playState = false;
        },
        error(err) {
            // 当在音频/视频加载期间发生错误时
            this.audioElm.pause();
            this.voicePlayMessage = '音频加载资源错误!';
            console.log('我报错了:' + err);
        },
        abort() {
            this.audioElm.pause();
        }
    }
};
</script>

<style lang="scss" scoped>
    .play-voice-area {
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: center;
}
.cover-player {
    position: relative;

    display: flex;
    align-items: center;
    flex-direction: column;
    flex-shrink: 0;
    justify-content: center;
    .cover-pic {
    display: block;
    overflow: hidden;

    width: 446px;
    height: 446px;

    transition: animation 0.28s;

    border: 15px solid hsla(0, 0%, 100%, 0.1);
    border-radius: 223px;
    img {
        display: inline-block;

        width: 446px;
        height: 446px;
    }
    &.active {
        animation: rotation 8s 0.1s linear infinite;
    }
    }
    .cover-icon {
    position: absolute;
    top: 50%;
    left: 50%;

    transform: translate(-50%, -50%);

    color: #fff;

    font-size: 100px;
    }
    a,
    button,
    input,
    textarea {
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
    }
}

.sound-desrc {
    display: flex;
    overflow: hidden;
    align-items: center;
    flex-direction: column;
    justify-content: center;

    padding: 40px 0 0 0;
    .username {
    min-width: 243px;
    height: 38px;
    margin: 22px 0;

    text-align: center;
    letter-spacing: 0px;
    text-overflow: ellipsis;

    color: #c4c9e2;

    font-size: 36px;
    font-weight: normal;
    font-weight: 700;
    font-stretch: normal;
    line-height: 38px;
    }
    .timeline {
    width: 243px;
    height: 38px;

    text-align: center;

    color: #c4c9e2;

    font-size: 36px;
    font-weight: normal;
    font-stretch: normal;
    line-height: 38px;
    line-height: 38px;
    }
}

@keyframes rotation {
    from {
    -webkit-transform: rotate(0deg);
    }
    to {
    -webkit-transform: rotate(360deg);
    }
}
</style>
版本2

这个版本考虑了多音频播放,所以在主入口直接单例挂载了一个播放器

其次考虑音频的切换播放,所以必须依赖Vuex来共享状态

main.js-主入口
代码语言:javascript
复制
// 创建全局播放器
const music = new Audio();
Vue.prototype.player = music;
  • 状态

状态很简单,就一些基础信息,module的方式,state通过getters暴露

代码语言:javascript
复制
export default {
    state: {
        index: '',
        playState: false,
        curTime: '00:00'
    },
    mutations: {
        CURRENT_PLAY: (state, index) => {
            state.index = index;
        },
        CURRENT_TIME: (state, time) => {
            state.curTime = time;
        },

        SetPlayState(state, status) {
            state.playState = status;
        }
    }
};
播放组件组件
代码语言:javascript
复制
<template>
  <div
    @click="playstop"
    class="icon-wrap"
    :class="iconSize"
    :style="{color:iconColor}">
    <i
      class="sx-mobile"
      :class="playState ? iconShow.stop : iconShow.play" />
  </div>

</template>

<script>
export default {
    props: {
        iconShow: {
            type: Object,
            default: function() {
                return {
                    play: 'sx-mobile-bofang',
                    stop: 'sx-mobile-icon-'
                };
            }
        },
        iconSize: {
            type: String,
            default: 'normal'
        },
        iconColor: {
            type: String,
            default: '#FFF'
        },
        playState: {
            type: Boolean,
            default: false
        },
        sourceUrl: {
            type: String,
            default: ''
        },
        mode: {
            type: String,
            default: 'self'
        }
    },
    created() {
        // 检测微博微信平台
        this.checkWeiBo_WeiChat();
        console.log(this.sourceUrl);
    },
    mounted() {
        this.player.addEventListener('end', this.voiceEnd);
    },
    methods: {
        checkWeiBo_WeiChat() {
            let ua = navigator.userAgent.toLowerCase(); // 获取判断用的对象
            const script = document.createElement('script');
            if (/micromessenger/.test(ua)) {
                // 返回一个独立的promise
                script.src = 'https://res.wx.qq.com/open/js/jweixin-1.2.0.js';
                new Promise((resolve, reject) => {
                    let done = false;
                    script.onload = script.onreadystatechange = () => {
                        if (
                            !done &&
                              (!script.readyState ||
                                  script.readyState === 'loaded' ||
                                  script.readyState === 'complete')
                        ) {
                            done = true;

                            // 避免内存泄漏
                            script.onload = script.onreadystatechange = null;
                            resolve(script);
                        }
                    };

                    script.onerror = reject;
                    document
                        .getElementsByTagName('head')[0]
                        .appendChild(script);
                }).then(res => {
                    this.initWeixinSource();
                });
            }
            if (/WeiBo|weibo/i.test(ua)) {
                script.src =
                      'https://tjs.sjs.sinajs.cn/open/thirdpart/js/jsapi/mobile.js';
                new Promise((resolve, reject) => {
                    let done = false;
                    script.onload = script.onreadystatechange = () => {
                        if (
                            !done &&
                              (!script.readyState ||
                                  script.readyState === 'loaded' ||
                                  script.readyState === 'complete')
                        ) {
                            done = true;

                            // 避免内存泄漏
                            script.onload = script.onreadystatechange = null;
                            resolve(script);
                        }
                    };

                    script.onerror = reject;
                    document
                        .getElementsByTagName('head')[0]
                        .appendChild(script);
                }).then(res => {
                    this.initWeiboSource();
                });
            }
        },
        initWeixinSource() {
            wx.config({
                // 配置信息, 即使不正确也能使用 wx.ready
                debug: false,
                appId: '',
                timestamp: 1,
                nonceStr: '',
                signature: '',
                jsApiList: []
            });
            wx.ready(() => {
                let st = setTimeout(() => {
                    clearTimeout(st);
                    this.player.load();
                }, 50);
            });
        },
        initWeiboSource() {
            window.WeiboJS.init(
                {
                    appkey: '3779229073',
                    debug: false,
                    timestamp: 1429258653,
                    noncestr: '8505b6ef40',
                    scope: [
                        'getNetworkType',
                        'networkTypeChanged',
                        'getBrowserInfo',
                        'checkAvailability',
                        'setBrowserTitle',
                        'openMenu',
                        'setMenuItems',
                        'menuItemSelected',
                        'setSharingContent',
                        'openImage',
                        'scanQRCode',
                        'pickImage',
                        'getLocation',
                        'pickContact',
                        'apiFromTheFuture'
                    ]
                },
                ret => {
                    this.player.load();
                }
            );
        },
        second2time(currentTime) {
            /* 秒数化为分钟 */
            let min = parseInt(currentTime / 60, 10); // 向下取整分钟
            let second = parseInt(currentTime % 60, 10); // 取模得到剩余秒数
            if (min < 10) {
                min = '0' + min;
            }
            if (second < 10) {
                second = '0' + second;
            }
            return `${min}:${second}`;
        },
        playstop() {
            if (this.mode === 'self') {
                this.player.paused ? this.playVoice() : this.pauseVoice();
            } else {
                if (this.$store.getters.vindex === this.index) {
                    this.player.paused ? this.playVoice() : this.pauseVoice();
                } else {
                    this.player.src = this.sourceUrl;
                    this.player.play();
                    if (!this.player.paused) {
                        this.$store.commit('SetPlayState', true);
                        this.$store.commit('CURRENT_PLAY', this.index);
                    }
                }
            }

        },
        playVoice() {
            if (this.player.src !== '') {
                this.player.play();
                if (!this.player.paused) {
                    this.$store.commit('SetPlayState', true);
                    this.$store.commit('CURRENT_PLAY', this.index);
                    if (this.mode === 'self') {
                        this.playState = true;
                    }
                }
            } else {
                this.player.src = this.sourceUrl;
                this.playVoice();
            }

        },
        pauseVoice() {
            this.player.pause();
            this.$store.commit('SetPlayState', false);
            if (this.mode === 'self') {
                this.playState = false;
            }
        },
        voiceEnd() {
            if (this.mode === 'self') {
                this.$emit('update:playState', false);
            }
        }
    },
};
</script>

<style lang="scss" scoped>
  .icon-wrap {
    &.small {
      font-size: 16px;
    }
    &.normal {
      font-size: 32px;
    }
    &.large {
      font-size: 64px;
    }
    &.huge {
      font-size: 96px;
    }
    &.big {
      font-size: 128px;
    }
    i {
      font-size: inherit;
    }
  }
</style>

服务端渲染实现的版本(Nuxt)

audio_browser_inject_head.js件(middleware目录)
代码语言:javascript
复制
// 这里给标签加了spec标记,是为了防止多次访问同一个页面的时候,
// 无限的插入新增的js
// 这次就不再nuxt.config.js引入中间件了.因为不是面向全局,直接在对应的页面引入即可
export default context => {
  const { env } = context.deviceType;
  const HeadScript = context.app.head.script;
  if (env === "wechat") {
    if (!HeadScript[HeadScript.length - 1].spec) {
      HeadScript.push({
        src: "https://res.wx.qq.com/open/js/jweixin-1.3.2.js",
        type: "text/javascript",
        charset: "utf-8",
        spec: true,
      });
    }
  }
  if (env === "weibo") {
    if (!HeadScript[HeadScript.length - 1].spec) {
      HeadScript.push({
        src: "http://tjs.sjs.sinajs.cn/open/thirdpart/js/jsapi/mobile.js",
        type: "text/javascript",
        charset: "utf-8",
        spec: true,
      });
    }
  }
};
单例播放器(plugins目录)
  • plugins/player.js
代码语言:javascript
复制
import Vue from "vue";

export default ({ app, store }) => {
  let player = new Audio();
  player.preload = "auto";
  // 把单例的播放器提交到vuex去管控
  store.commit("voice/SetPlayer", player);
};
  • nuxt.config.js

因为audio对象只有客户端才有,所以不能服务端初始化 设置ssr:false就代表在客户端的时候才注入,默认不写ssrtrue

代码语言:javascript
复制
module.exports = {
  plugins: [ { src: "~plugins/player.js", ssr: false }]
};
Vuex(store目录)
  • 默认的index.js是根状态,其他再改目录下的js文件均默认当做vuexmodule
代码语言:javascript
复制
// index.js

import Vuex from "vuex";
export const state = () => ({
  deviceType: {},
});

export const mutations = {
  SetDeviceType(state, payload) {
    state.deviceType = payload;
  },
};

export const getters = {
  deviceType(state) {
    return state.deviceType;
  },
  player(state) {
    return state.voice.player;
  },
  playState(state) {
    return state.voice.playState;
  },
  playUrl(state) {
    return state.voice.playUrl;
  },
  playIndex(state) {
    return state.voice.playIndex;
  },
  playTime(state) {
    return state.voice.playTime;
  },
  voiceTotalTime(state) {
    return state.voice.voiceTotalTime;
  },
};


// voice.js

import Vuex from "Vuex";
export const state = () => ({
  player: "", // 播放器
  playState: false, // 当前播放的状态
  playUrl: "", // 播放的链接
  playIndex: 0, // 当前播放的索引
  playTime: "00:00", // 当前的播放时间
  voiceTotalTime: "00:00", // 曲目总时长
});

export const mutations = {
  SetPlayer(state, payload) {
    state.player = payload;
  },
  SetPlayState(state, payload) {
    state.playState = payload;
  },
  SetPlayUrl(state, payload) {
    state.playUrl = payload;
    state.player.src = payload;
  },
  SetPlayIndex(state, payload) {
    state.playIndex = payload;
  },
  SetPlayTime(state, payload) {
    state.playTime = payload;
  },
  SetVoiceTotalTime(state, payload) {
    state.voiceTotalTime = payload;
  },
  ResetVoice(state) {
    state.playState = false;
    state.playUrl = "";
    state.playTime = "00:00";
    state.voiceTotalTime = "00:00";
  },
};
播放器组件
  • VoicePlayer.vue

播放状态均由vuex来管理,这样对于多音频或者跨组件控制播放非常有帮助

代码语言:javascript
复制
<template>
  <div class="player"
       :class="$store.getters.playState ? 'animation-roate':''"
       :style="{background:`url(${CoverImg}) center center no-repeat`,backgroundSize: 'cover'}">
    <div class="icon-wrap">
      <img :src="playstate? StopIcon:PlayIcon"
           alt="播放器操作按钮"
           @click="changePlayState(playstate)">
    </div>
  </div>
</template>

<script>
  const CoverImg = require('./images/cover@2x.png');
  const PlayIcon = require('./images/play@2x.png');
  const StopIcon = require('./images/stop@2x.png');
  export default {
    data() {
      return {
        CoverImg,
        PlayIcon,
        StopIcon,
      }
    },
    props: {
      playstate: {
        type: Boolean,
        default: false
      },
      playurl: {
        type: String,
        default: 'http://www.ytmp3.cn/down/51013.mp3'
      }

    },
    mounted() {

      this.$store.getters.player.addEventListener('loadedmetadata', () => {
        // 缓存播放总时长
        this.$store.commit('voice/SetVoiceTotalTime', this.second2time(this.$store.getters.player.duration));
      })
      this.$store.getters.player.addEventListener('stalled', () => {
        // 重置播放状态
        this.$store.commit('voice/ResetVoice');
      })
      this.$store.getters.player.addEventListener('abort', () => {
        // 重置播放状态
        this.$store.commit('voice/ResetVoice');
      })
      this.$store.getters.player.addEventListener('play', () => {
        this.$store.commit('voice/SetPlayState', true);
      })
      this.$store.getters.player.addEventListener('pause', () => {
        this.$store.commit('voice/SetPlayState', false);
      })
      this.$store.getters.player.addEventListener('timeupdate', () => {
        this.$store.commit('voice/SetPlayTime', this.second2time(this.$store.getters.player.currentTime));
      })
      this.$store.getters.player.addEventListener('ended', () => {
        this.$store.commit('voice/ResetVoice');
      })
    },
    beforeDestroy() {
      this.$store.getters.player.removeEventListener('loadedmetadata', () => {
        this.$store.commit('voice/SetVoiceTotalTime', this.second2time(this.$store.getters.player.duration));
      })
      this.$store.getters.player.removeEventListener('stalled', () => {
        this.$store.commit('voice/ResetVoice');
      })
      this.$store.getters.player.removeEventListener('abort', () => {
        this.$store.commit('voice/ResetVoice');
      })
      this.$store.getters.player.removeEventListener('play', () => {
        this.$store.commit('voice/SetPlayState', true);
      })
      this.$store.getters.player.removeEventListener('pause', () => {
        this.$store.commit('voice/SetPlayState', false);
      })
      this.$store.getters.player.removeEventListener('timeupdate', () => {
        this.$store.commit('voice/SetPlayTime', this.second2time(this.$store.getters.player.currentTime));
        console.log(this.$store.getters.player.currentTime)
      })
      this.$store.getters.player.removeEventListener('ended', () => {
        this.$store.commit('voice/ResetVoice');
      })

    },
    methods: {
      changePlayState(playstate) {
        // 设置播放源
        if (!this.$store.getters.playUrl) {
          this.$store.commit('voice/SetPlayUrl', this.playurl)
        }


        // 设置播放状态
        if (playstate) {
          this.$store.getters.player.pause();
        } else {
          this.$store.getters.player.play();
        }
        playstate = !playstate;
      },
      initWeixinSource() {
        wx.config({
          // 配置信息, 即使不正确也能使用 wx.ready
          debug: false,
          appId: '',
          timestamp: 1,
          nonceStr: '',
          signature: '',
          jsApiList: []
        });
        wx.ready(() => {
          let st = setTimeout(() => {
            clearTimeout(st);
            this.player.load();
          }, 50);
        });
      },
      initWeiboSource() {
        window.WeiboJS.init(
          {
            appkey: '3779229073',
            debug: false,
            timestamp: 1429258653,
            noncestr: '8505b6ef40',
            scope: [
              'getNetworkType',
              'networkTypeChanged',
              'getBrowserInfo',
              'checkAvailability',
              'setBrowserTitle',
              'openMenu',
              'setMenuItems',
              'menuItemSelected',
              'setSharingContent',
              'openImage',
              'scanQRCode',
              'pickImage',
              'getLocation',
              'pickContact',
              'apiFromTheFuture'
            ]
          },
          ret => {
            this.audioElm.load();
          }
        );
      },
      second2time(currentTime) {
        // 秒数化为分钟
        let min = Math.floor(currentTime / 60); // 向下取整分钟
        let second = Math.floor(currentTime % 60); // 取模得到剩余秒数
        if (min < 10) {
          min = '0' + min;
        }
        if (second < 10) {
          second = '0' + second;
        }
        return `${min}:${second}`;
      },
    }
  }
</script>

<style lang="scss" scoped>
  .player {
    height: 100%;
    width: 100%;
    border-radius: 100%;
    position: relative;
    .icon-wrap {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      img {
        display: block;
        height: 94px;
        width: 94px;
      }
    }
  }

  @keyframes fade-rotate {
    from {
      opacity: 0.8;
      transform: rotate(0) scale(1);
    }
    to {
      opacity: 1;
      transform: rotate(360deg) scale(1.1);
    }
  }

  .animation-roate {
    transform: translate3d(0, 0, 0);
    animation: fade-rotate 18s ease-in-out infinite alternate;
  }
</style>

总结

有不对之处请留言,会及时修正,谢谢阅读。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 前置基础
  • 效果图
  • 实现思路
  • 代码实现
    • 客户端渲染实现的版本
      • 版本1
      • 版本2
    • 服务端渲染实现的版本(Nuxt)
      • audio_browser_inject_head.js件(middleware目录)
      • 单例播放器(plugins目录)
      • Vuex(store目录)
      • 播放器组件
  • 总结
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档