Nuxt3+Dplayer 实现Video自动播放(添加刷新按钮/限制游客观看时长/提示用户打开声音)

本文介绍了如何在Vue项目中使用DPlayer播放器,处理自动播放限制,包括静音策略、全屏功能、登录弹窗计时以及与Hls.js结合解析m3u8直播流。
摘要由CSDN通过智能技术生成
1.安装dplayer

        见官网  https://dplayer.diygod.dev/zh/guide.html#special-thanks
        注:本人使用版本 v1.25。0

2.组件引入dplyer
import DPlayer from 'dplayer'
import Hls from 'hls.js';//用于解析m3u8直播流
import "dplayer/dist/DPlayer.min.css" //v1.25.0版本需要引入css
3.tempate
<template>
    <ClientOnly>
        <div class="bit" :class="{ 'full': state.isFull }">
            <div ref="videoRef" class="myvideo"></div>
            <div class="mute" @click="changeVolume" v-show="state.show && !state.showLogin">
                <img src="~/assets/images/mute.svg" alt="">
                <div>CLICK TO UNMUTE</div>
            </div>
            <div class="goLogin" v-if="state.showLogin">
                <div class="logo">
                    <img src="~/assets/images/logo.png" alt="">
                    <div v-if="state.countDown > 0">
                        {{ state.countDown }}s
                        <van-icon @click="closeLogin" name="cross" />
                    </div>
                </div>
                <div class="txt" v-if="state.watchTime < 600">
                    Inicie sesión para eliminar la ventana emergente y mirar en modo de pantalla completa
                </div>
                <div class="txt" v-else>
                    Su período de prueba de 10 minutos de hoy ha expirado. Por favor, inicie sesión para ver el juego.
                </div>
                <div class="login-btn" @click="goLogin">Login Account</div>
            </div>
        </div>

    </ClientOnly>
</template>
4.完整代码
<script setup>

import DPlayer from 'dplayer'
import Hls from 'hls.js';
import "dplayer/dist/DPlayer.min.css"
import { ref, reactive, onBeforeUnmount, onMounted, onDeactivated, onActivated } from 'vue'
import logo from '~/assets/images/logo.png'
import load from '~/assets/images/reload.svg'
import { storage } from "~~/utils/storage";
import { useCounterStore } from "~/stores/conter";
const counterStore = useCounterStore();
import { useRoute, useRouter } from "vue-router";
const router = useRouter();
const route = useRoute()
const videoRef = ref()
const state = reactive({
    instance: null,
    player: {},
    showLogin: false,
    watchTime: 0,//观看总时长
    showNum: 0,//showlogin 次数
    countDown: 0,//showlogin 倒计时
    minTime: 0,//分钟倒计时
    show: false,//是否显示静音
    isFull: false,//播放器是否全屏
    isEnter: false,//画中画模式

})
const counter = ref(0);
let intervalId = null
const hls = new Hls(); //实例化Hls  用于解析m3u8
//dplayer 参数设置
const props = defineProps({
    // 是否自动播放
    autoplay: {
        type: Boolean,
        default: false 
    },
    // 主题色
    theme: {
        type: String,
        default: '#0093ff'
    },
    // 视频是否循环播放
    loop: {
        type: Boolean,
        default: false
    },
    airplay: {
        type: Boolean,
        default: true
    },
    // 语言(可选值: 'en', 'zh-cn', 'zh-tw')
    lang: {
        type: String,
        default: 'en'
    },
    // 是否开启截图(如果开启,视频和视频封面需要允许跨域)
    screenshot: {
        type: Boolean,
        default: false
    },
    // 是否开启热键
    hotkey: {
        type: Boolean,
        default: true
    },
    // 视频是否预加载(可选值: 'none', 'metadata', 'auto')
    preload: {
        type: String,
        default: 'auto'
    },
    // 默认音量
    volume: {
        type: Number,
        default: 0.7
    },
    // 可选的播放速率,可以设置成自定义的数组
    playbackSpeed: {
        type: Array,
        default: [0.5, 0.75, 1, 1.25, 1.5, 2]
    },
    // 在左上角展示一个 logo,你可以通过 CSS 调整它的大小和位置
    logo: {
        type: String,
        default: logo
    },
    // 视频信息
    video: {
        type: Object,
        default: () => {

        },
    },
    // 外挂字幕
    // subtitle: {
    //     type: Object,
    //     default: {}
    // },
    // 显示弹幕
    // danmaku: {
    //     type: Object,
    //     default: {}
    // },
    // 自定义右键菜单
    contextmenu: {
        type: Array,
        default: []
    },
    // 自定义进度条提示点
    highlight: {
        type: Array,
        default: []
    },
    // 阻止多个播放器同时播放,当前播放器播放时暂停其他播放器
    mutex: {
        type: Boolean,
        default: false
    },
    // 直播
    live: {
        type: Boolean,
        default: false
    },
    video_url: {
        type: String,
        default: ''
    },
    isPlay: {
        type: Boolean,
        default: false
    },
    pic: {
        type: String,
        default: ''
    },
})
//跳转登录页
const goLogin = () => {
    router.push({ path: '/login' });
}
// 初始化播放器
const init = async () => {
    let player = {
        container: videoRef.value,
        autoplay: props.autoplay,
        theme: props.theme,
        loop: props.loop,
        lang: props.lang,
        screenshot: props.screenshot,
        hotkey: props.hotkey,
        preload: props.preload,
        volume: props.volume,
        playbackSpeed: props.playbackSpeed,
        logo: props.logo,
        video: props.video,
        // contextmenu: props.contextmenu,
        // highlight: props.highlight,
        mutex: props.mutex,
        live: props.live
    }
    player.video = {
        url: props.video_url,
        type: 'customHls',
        customType: {
            customHls: function (video, player) {
                //实例化Hls  用于解析m3u8
                hls.loadSource(video.src);
                hls.attachMedia(video);
            }
        },
        pic: props.pic
        // muted:true
    }
    state.player = player
    state.instance = new DPlayer(player)
    console.log(state.instance, player)

    let video = document.querySelector("video"); //获取video元素
    let videoPlay = video.play(); 
    setTimeout(() => {
        // 判断video是否能够自动播放
        videoPlay.then(() => {
            console.log('canplay');
        }).catch((err) => {
            console.log(err);
            console.log("no canplay");
            //视频元素可以选择静音后再播放,提示用户打开声音
            state.instance?.pause()
            setTimeout(() => {
                state.instance?.volume(0, true, true);
                state.instance?.play()
            }, 1500)

            // video.muted = true;
        });
    })
    //在全屏按钮旁边添加视频刷新按钮
    if (state.instance) {
        let dom = document.getElementsByClassName('dplayer-icons-right')[0]
        let newElement = document.createElement('img');
        newElement.src = load
        newElement.className += 'reload'
        // dom.appendChild(newElement)
        dom.insertBefore(newElement, dom.firstChild);
        newElement.addEventListener("click", function () {
            // console.log("点击了新添加的元素!");
            // state.instance.video.load()
            state.instance.destroy()
            setTimeout(() => {
                init()
            }, 500);
        });
    }
    state.instance.on('pause', function () {
        // console.log('pause')
    })
    // 监听视频是否画中画模式
    video.addEventListener('enterpictureinpicture', function () {
        // 已进入画中画模式
        // console.log('已进入画中画模式')
        state.isEnter = true
    });
    video.addEventListener('leavepictureinpicture', function () {
        // 已退出画中画模式
        // console.log('已退出画中画模式')
        state.isEnter = false
        if (state.instance?.paused) {
            setTimeout(() => {
                state.instance?.play()
            }, 500);
        }

    });
    // 监听video在IOS中是否全屏
    video.addEventListener("webkitbeginfullscreen", () => {
        console.log("webkitbeginfullscreen");
        state.isFull = true
    });
    video.addEventListener("webkitendfullscreen", () => {
        console.log("webkitendfullscreen");
        state.isFull = false
        if (state.instance?.paused) {
            setTimeout(() => {
                state.instance?.play()
            }, 500);
        }
    });
    state.instance.on('waiting', function () {
        // console.log('waiting')
    })
    state.instance.on('volumechange', function () {
        console.log('volumechange', state.instance.video.volume)
        if (state.instance.video.volume == 0) {
            state.show = true
        } else {
            state.show = false
        }
    })
    // 监听是否全屏
    state.instance.on('fullscreen', function () {
        state.isFull = true
        console.log(state.isFull)
    })
    state.instance.on('fullscreen_cancel', function () {
        // console.log(state.instance.video)
        state.isFull = false
        console.log(state.isFull)
    })
}
let timerId = null
// 登录弹窗倒计时
const downTime = () => {
    // let timer = 0 
    state.showLogin = true
    // 如果video进入全屏或画中画模式,观看时长达到限制后自动退出全屏和画中画
    if (state.isFull) {
        document.querySelector("video").webkitExitFullscreen()
        state.isFull = false
        state.instance.fullScreen.cancel()
        //判断退出全屏后视频是否暂停,暂停继续播放
        setTimeout(() => {
            if (state.instance?.paused) {
                setTimeout(() => {
                    state.instance?.play()
                }, 500);
            }
        }, 500);
        // console.log("webkitendfullscreen",state.isFull,2);
    }
    // 
    if (state.isEnter) {
        document.exitPictureInPicture();
    }
    state.showNum++
    if (state.showNum == 1) {
        state.countDown = 15
        timerId = setInterval(() => {
            state.countDown--
            console.log('countDown0', state.countDown)
            if (state.countDown <= 0) {
                clearInterval(timerId)
                state.showLogin = false
                state.minTime = 0
                state.countDown = 0
                return watchTimeFun()
            }
        }, 1000);
    }
    if (state.showNum == 2) {
        state.countDown = 30
        timerId = setInterval(() => {
            state.countDown--
            console.log('countDown1', state.countDown)
            if (state.countDown <= 0) {
                clearInterval(timerId)
                // state.showNum++
                state.showLogin = false
                state.minTime = 0
                state.countDown = 0
                return watchTimeFun()
            }
        }, 1000);
    }
}
// 记录游客观看视频时长
const watchTimeFun = () => {
    if (counterStore.data.userInfo?.id) {
        storage.set('liveTime', 0)
        storage.set('startWatchTime', 0)
        return
    }
    if (JSON.parse(JSON.stringify(storage.get('startWatchTime')))) {
        if (Date.now() - JSON.parse(JSON.stringify(storage.get('startWatchTime'))) > 4 * 60 * 60 * 1000) {
            storage.set('startWatchTime', Date.now())
            storage.set('liveTime', 0)
        }
    } else {
        storage.set('startWatchTime', Date.now())
    }
    state.watchTime = JSON.parse(JSON.stringify(storage.get('liveTime'))) || 0
    // let time = 0
    // 这边需求是4个小时内 游客总计观看时长10分钟,每观看一分钟提醒用户去登录(弹出登录弹窗)
    if (state.watchTime >= 600) {
        state.showLogin = true
        state.minTime = 0
        state.countDown = 0
        clearInterval(intervalId)
        clearInterval(timerId)
        return
    }
    intervalId = setInterval(() => {
        state.minTime++
        if (state.minTime <= 60 && state.countDown <= 0) {
            state.watchTime++;
            if (state.watchTime >= 600) {
                state.showLogin = true
                if (state.isFull) {
                    // let video = document.querySelector("video")
                    // if (video.exitFullscreen) {
                    //     video.exitFullscreen();
                    // } else if (video.mozCancelFullScreen) {
                    //     video.mozCancelFullScreen();
                    // } else if (video.webkitCancelFullScreen) {
                    //     video.webkitCancelFullScreen();
                    // }
                    document.querySelector("video").webkitExitFullscreen()
                    state.isFull = false
                    state.instance.fullScreen.cancel()
                    setTimeout(() => {
                        if (state.instance?.paused) {
                            setTimeout(() => {
                                state.instance?.play()
                            }, 500);
                        }
                    }, 500);
                }
                if (state.isEnter) {
                    document.exitPictureInPicture();
                }
                state.minTime = 0
                state.countDown = 0
                clearInterval(intervalId)
                clearInterval(timerId)
            }
            storage.set('liveTime', state.watchTime)
        } else {
            clearInterval(intervalId)
            if (state.showNum <= 2) {
                downTime()
            } else {
                state.showLogin = true;
                if (state.isFull) {
                    // let video = document.querySelector("video")
                    // if (video.exitFullscreen) {
                    //     video.exitFullscreen();
                    // } else if (video.mozCancelFullScreen) {
                    //     video.mozCancelFullScreen();
                    // } else if (video.webkitCancelFullScreen) {
                    //     video.webkitCancelFullScreen();
                    // }
                    document.querySelector("video").webkitExitFullscreen()
                    state.isFull = false
                    state.instance.fullScreen.cancel()
                    setTimeout(() => {
                        if (state.instance?.paused) {
                            setTimeout(() => {
                                state.instance?.play()
                            }, 500);
                        }
                    }, 500);
                }
                if (state.isEnter) {
                    document.exitPictureInPicture();
                }
                return
            }

        }
        // console.log(state.watchTime, state.countDown, state.showNum, state.minTime)
    }, 1000);
}
const closeLogin = () => {
    clearInterval(intervalId)
    clearInterval(timerId)
    state.showLogin = false
    state.minTime = 0
    state.countDown = 0
    watchTimeFun()
}
const changeVolume = () => {
    state.instance.video.muted = false
    state.instance.volume(.7, false, true);
}
onMounted(() => {
    nextTick(async () => {
        await init()
        console.log('onMounted')
        watchTimeFun()
    })
})
// 销毁
onBeforeUnmount(() => {
    console.log('onBeforeUnmount')
    // state.instance
    clearInterval(intervalId)
    clearInterval(timerId)
    hls.destroy() //销毁视频后不继续请求m3u8资源
    state.instance.destroy() //销毁视频
})
onActivated(() => {
    nextTick(() => {
        console.log('onActivated')

    })
})
onDeactivated(() => {
    console.log('onDeactivated')
    setInterval(intervalId)
})


</script>

<style lang='scss' scoped>
.bit {
    height: 100% !important;
    width: 100% !important;
    position: relative;

    &.full {
        position: fixed;
        // width: 100vw!important;
        // height: 100vh!important;
        left: 0;
        top: 0;
    }
}

.goLogin {
    height: 100% !important;
    width: 100% !important;
    position: absolute;
    top: 0;
    // border-radius: 20px;
    background: rgba(0, 0, 0, .8);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: .16rem;
    box-sizing: border-box;
    gap: .24rem;
    font-size: .28rem;
    color: #fff;
    z-index: 999999;

    .logo {
        position: absolute;
        top: 0;
        width: 100%;
        display: flex;
        justify-content: space-between;
        align-items: center;
        // padding: 0;
        padding: .16rem;
        box-sizing: border-box;
        font-size: .24rem;

        img {
            max-height: .70rem;
            max-width: 2.40rem;
        }

        >div {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: .12rem;

            ::v-deep .van-icon {
                font-size: .36rem;
                cursor: pointer;
            }
        }
    }

    .txt {
        text-align: center;
    }

    .login-btn {
        height: .80rem;
        width: 3.20rem;
        border-radius: 16px;
        background: #69AE64;
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: .32rem;
        cursor: pointer;
    }
}

.dplayer {
    height: 100% !important;
    width: 100% !important;

    .dplayer-menu {
        display: none !important;
        visibility: hidden !important;
    }

    .dplayer-menu-show {
        display: none !important;
        visibility: hidden !important;
    }
}

.mute {
    position: absolute;
    top: .20rem;
    left: .20rem;
    display: flex;
    height: .60rem;
    background: #69AE64;
    border-radius: .08rem;
    align-items: center;
    padding: .12rem .24rem;
    gap: .16rem;
    font-size: .24rem;
    box-sizing: border-box;
    z-index: 9;
    color: #fff;

    img {
        width: .40rem;
        height: .40rem;
    }

    .line {
        width: .02rem;
        height: 100%;
        background: #000;
    }
}

::v-deep {
    .dplayer-full-in-icon {
        display: none !important;
    }

    .dplayer-icons-right {
        display: flex;
        align-items: center;
        gap: .16rem;

        .reload {
            cursor: pointer;
            height: .40rem;
            width: .46rem;
            background: url('~/assets/images/reload.svg') no-repeat;
            opacity: .9;
        }
    }
}

::v-deep .dplayer-menu,
.dplayer-mask {
    display: none !important;
}
</style>
5.总结

由于浏览器策略限制了网站在用户没有交互的情况下禁止自动播放音频,故 采用静音播放(如果能有声自动播放则自动播放,不能播放则设置视频音量为静音播放)。
以上兼容pc和移动端(ios也有效)

6.部分效果图

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nuxt.js 3 基于 Vite 构建的项目,默认情况下会使用 Vite 的 Server Side Rendering (SSR) 功能进行服务端渲染。在 SSR 模式下,Nuxt.js 3 会使用 Node.js 进程来生成 HTML 文档,并将其发送给客户端进行展示。 为了提高性能和减少服务器负载,Nuxt.js 3 支持服务端缓存,它可以将生成的 HTML 文档缓存到内存或磁盘中,当下次有相同的请求时,直接从缓存中读取 HTML 文档,而不需要重新生成。这样可以大大提高服务器的响应速度和吞吐量。 在 Nuxt.js 3 中,服务端缓存是通过内置的缓存插件 `@nuxtjs/cache` 来实现的。该插件可以将 HTML 文档缓存到内存或磁盘中,并支持多种缓存策略,包括按时间、按请求参数、按请求头等方式进行缓存。 要使用服务端缓存,你需要先安装 `@nuxtjs/cache` 插件: ```bash npm install @nuxtjs/cache ``` 然后在 `nuxt.config.js` 配置文件中添加以下内容: ```js export default { // ... buildModules: ['@nuxtjs/cache'], cache: { pages: [ // 缓存首页和文章页 '/', '/posts/:id' ], store: { type: 'memory' } } } ``` 在上面的示例中,我们首先将 `@nuxtjs/cache` 插件添加到 `buildModules` 中,然后在 `cache` 属性中配置了缓存策略。我们指定了需要缓存的页面路径,包括首页和文章页,缓存的类型是 `memory`,表示将缓存存储在内存中。 当用户访问缓存的页面时,Nuxt.js 3 会先从缓存中读取 HTML 文档,并将其返回给客户端。如果缓存中不存在对应的 HTML 文档,则会重新生成并缓存。 需要注意的是,在使用服务端缓存时,应该特别注意缓存的键名和缓存时间的设置,以免造成缓存混乱或缓存过期的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值