浏览代码

视频修改

jwyu 2 年之前
父节点
当前提交
04a9145336
共有 2 个文件被更改,包括 467 次插入1 次删除
  1. 462 0
      src/components/VideoPlayBox.vue
  2. 5 1
      src/views/report/Index.vue

+ 462 - 0
src/components/VideoPlayBox.vue

@@ -0,0 +1,462 @@
+<script setup>
+import { nextTick, reactive,ref,onMounted, computed } from "vue-demi"
+import {debounce} from 'lodash'
+/**
+ * 格式化时间
+ * @param {number} e 秒
+ * @returns string eg:03:27
+ */
+function formatTime(e){
+    const h=parseInt(e/3600)
+    const m=parseInt(e/60%60)
+    const s=parseInt(e%60)
+    return `${h>0?h>9?h+':':'0'+h+':':''}${m>9?m:'0'+m}:${s>9?s:'0'+s}`
+}
+
+/**
+ * 是否全屏
+ * @returns boolean
+ */
+function isFullScreen(){
+    return document.webkitIsFullScreen || document.fullscreen
+}
+/**
+ * 设置全屏
+ */
+function setFullScreen(el){
+    el.requestFullscreen() || el.webkitRequestFullScreen()
+}
+/**
+ * 关闭全屏
+ */
+function closeFullScreen(){
+    document.exitFullscreen() || document.webkitExitFullScreen()
+}
+//监听页面全屏变化
+function listenFullScreen(){
+    document.addEventListener('fullscreenchange',()=>{
+        videoState.isFullScreen=isFullScreen()
+    })
+}
+
+
+const props=defineProps({
+    videoUrl:{
+        type:String,
+        require:true
+    },
+    speedOpt:{
+        type:Array,
+        default:['2.0','1.5','1.25','1.0','0.8','0.5']
+    }
+})
+
+const videoIns=ref(null)//视频实例
+let videoWrap=ref(null)//视频盒子
+let preloadSlider=computed(()=>{//缓冲的进度
+    return (videoState.cacheTime/videoState.duration)*100+'%'
+})
+let activeSlider=computed(()=>{//当前播放的进度
+    return (videoState.time/videoState.duration)*100+'%'
+})
+//视频状态
+let videoState=reactive({
+    play:false,
+    isPageFullScreen:false,//网页全屏
+    isFullScreen:false,//是否全屏
+    duration:0,//视频时长
+    time:0,//当前播放时长
+    cacheTime:0,//当前缓冲到的时间
+    speed:'1.0',
+    speedOpt:props.speedOpt,
+    volume:100,
+    isHover:false
+})
+const emit=defineEmits(['play','pause','timeupdate'])
+//视频事件
+function videoPlay(){
+    videoState.play=true
+    emit('play')
+}
+function videoCanPlay(e){
+    const target=e.target
+    videoState.duration=target.duration
+}
+function videoTimeUpdate(e){
+    const target=e.target
+    videoState.time=target.currentTime
+    videoState.duration=target.duration
+    emit('timeupdate')
+}
+function videoPause(){
+    videoState.play=false
+    emit('pause')
+}
+function videoProgress(e){
+    const target=e.target
+    videoState.cacheTime=target.buffered.length && target.buffered.end(target.buffered.length - 1)
+}
+
+// 点击暂停\播放视频
+function handleChangeVideoPlay(){
+    if(videoState.play){
+        videoIns.value.pause()
+    }else{
+        videoIns.value.play()
+    }
+}
+
+//切换倍速
+function handleChangeVideoSpeed(item){
+    if(videoState.speed==item) return
+    videoState.speed=item
+    videoIns.value.playbackRate=Number(item)
+}
+
+// 音量变化
+function handleChangeVideoVolume(){
+    videoIns.value.volume=videoState.volume/100
+}
+//一键静音
+function handleVideoMute(){
+    videoState.volume=0
+    videoIns.value.volume=0
+}
+
+//拖动进度条
+function handleDragVideoSlider(val){
+    console.log(val);
+    videoIns.value.currentTime=val
+}
+
+//点击切换全屏
+function handleChangeVideoScreen(){
+    // 如果网页全屏为true 
+    if(videoState.isPageFullScreen){
+        videoState.isPageFullScreen=false
+        return
+    }
+
+    if(!isFullScreen()){
+        setFullScreen(videoWrap.value)
+        setTimeout(() => {
+            // 无法设置全屏 则设置为网页全屏
+            if(!isFullScreen()){
+                videoState.isPageFullScreen=true
+            }
+        }, 100);
+    }else{
+        closeFullScreen()
+    }
+}
+
+// 鼠标进入视频区域
+function mouseMoveVideoWrap(){
+    videoState.isHover=true 
+    hideVideoControlBox()
+}
+//隐藏控制器
+const hideVideoControlBox=debounce(()=>{
+    videoState.isHover=false
+},3000)
+
+onMounted(() => {
+    listenFullScreen()
+})
+
+
+</script>
+
+<template>
+    <div 
+        ref="videoWrap" 
+        :class="[
+            'video-wrap',
+            videoState.isPageFullScreen?'page-fullscreen':''
+        ]"
+        @contextmenu="e=>e.preventDefault()"
+        @mousemove="mouseMoveVideoWrap"
+    >
+        <video 
+            class="video" 
+            ref="videoIns"
+            :src="videoUrl"
+            loop
+            @canplay="videoCanPlay"
+            @play="videoPlay" 
+            @pause="videoPause" 
+            bindended="" 
+            @timeupdate="videoTimeUpdate" 
+            @progress="videoProgress"
+            @click="handleChangeVideoPlay"
+            bindwaiting="" 
+            binderror=""
+        >
+            <span>您的浏览器不支持 video 标签。</span>
+        </video>
+
+        <!-- 视频控制器模块 -->
+        <div class="video-control-box" v-show="videoState.isHover||!videoState.play">
+            <!-- 进度条 -->
+            <div class="video-progress-slider-box" >
+                <!-- 缓冲进度条 -->
+                <div class="preload-slider" :style="{width:preloadSlider}"></div>
+                <!-- 实时进度 -->
+                <el-slider 
+                    class="active-slider" 
+                    size="small" 
+                    v-model="videoState.time" 
+                    :max="videoState.duration" 
+                    :show-tooltip="false" 
+                    @input="handleDragVideoSlider"
+                />
+            </div>
+            <div class="top-btn-box">
+                <div style="display:flex;align-items: center;">
+                    <div class="play-btn hover-btn" @click.stop="handleChangeVideoPlay">
+                        <svg v-if="!videoState.play" t="1668758429272" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2674" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M870.2 466.333333l-618.666667-373.28a53.333333 53.333333 0 0 0-80.866666 45.666667v746.56a53.206667 53.206667 0 0 0 80.886666 45.666667l618.666667-373.28a53.333333 53.333333 0 0 0 0-91.333334z" fill="#ffffff" p-id="2675" data-spm-anchor-id="a313x.7781069.0.i0" class="selected"></path>
+                        </svg>
+                        <svg v-else t="1668760616711" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3627" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M428.539658 833.494155c0 15.954367-13.053294 29.007661-29.007661 29.007661L285.613458 862.501816c-15.954367 0-29.007661-13.053294-29.007661-29.007661l0-639.423111c0-15.954367 13.053294-29.007661 29.007661-29.007661l113.918539 0c15.954367 0 29.007661 13.053294 29.007661 29.007661L428.539658 833.494155z" p-id="3628" data-spm-anchor-id="a313x.7781069.0.i3" class="selected" fill="#ffffff"></path><path d="M760.124635 833.494155c0 15.954367-13.053294 29.007661-29.007661 29.007661l-113.918539 0c-15.954367 0-29.007661-13.053294-29.007661-29.007661l0-639.423111c0-15.954367 13.053294-29.007661 29.007661-29.007661l113.918539 0c15.954367 0 29.007661 13.053294 29.007661 29.007661L760.124635 833.494155z" p-id="3629" data-spm-anchor-id="a313x.7781069.0.i4" class="selected" fill="#ffffff"></path>
+                        </svg>
+                    </div>
+                    <span class="video-time">{{formatTime(videoState.time)}}/{{formatTime(videoState.duration)}}</span>
+                </div>
+                <div style="display:flex;align-items: center;">
+                    <!-- 声音大小 -->
+                    <div class="volume-btn">
+                        <svg v-if="videoState.volume==0" t="1669010015626" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3708" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M128 420.576v200.864h149.12l175.456 140.064V284.288l-169.792 136.288H128z m132.256-64l204.288-163.968a32 32 0 0 1 52.032 24.96v610.432a32 32 0 0 1-51.968 24.992l-209.92-167.552H96a32 32 0 0 1-32-32v-264.864a32 32 0 0 1 32-32h164.256zM752 458.656L870.4 300.8a32 32 0 1 1 51.2 38.4L792 512l129.6 172.8a32 32 0 0 1-51.2 38.4l-118.4-157.856-118.4 157.856a32 32 0 0 1-51.2-38.4l129.6-172.8-129.6-172.8a32 32 0 0 1 51.2-38.4l118.4 157.856z" p-id="3709" data-spm-anchor-id="a313x.7781069.0.i3" class="selected" fill="#ffffff"></path>
+                        </svg>
+                        <svg v-else @click.stop="handleVideoMute" t="1669010104115" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6297" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M257.493333 322.4l215.573334-133.056c24.981333-15.413333 57.877333-7.914667 73.493333 16.746667 5.301333 8.373333 8.106667 18.048 8.106667 27.914666v555.989334C554.666667 819.093333 530.784 842.666667 501.333333 842.666667c-9.994667 0-19.786667-2.773333-28.266666-8L257.493333 701.6H160c-41.237333 0-74.666667-33.013333-74.666667-73.738667V396.138667c0-40.725333 33.429333-73.738667 74.666667-73.738667h97.493333z m26.133334 58.4a32.298667 32.298667 0 0 1-16.96 4.8H160c-5.888 0-10.666667 4.714667-10.666667 10.538667v231.733333c0 5.813333 4.778667 10.538667 10.666667 10.538667h106.666667c5.994667 0 11.872 1.664 16.96 4.8L490.666667 770.986667V253.013333L283.626667 380.8zM800.906667 829.653333a32.288 32.288 0 0 1-45.248-0.757333 31.317333 31.317333 0 0 1 0.768-44.693333c157.653333-150.464 157.653333-393.962667 0-544.426667a31.317333 31.317333 0 0 1-0.768-44.682667 32.288 32.288 0 0 1 45.248-0.757333c183.68 175.306667 183.68 460.010667 0 635.317333z m-106.901334-126.186666a32.288 32.288 0 0 1-45.248-1.216 31.328 31.328 0 0 1 1.237334-44.672c86.229333-80.608 86.229333-210.56 0-291.178667a31.328 31.328 0 0 1-1.237334-44.672 32.288 32.288 0 0 1 45.248-1.216c112.885333 105.546667 112.885333 277.418667 0 382.965333z" p-id="6298" data-spm-anchor-id="a313x.7781069.0.i5" class="selected" fill="#ffffff"></path>
+                        </svg>
+                        <div class="volume-slider-box">
+                            <el-slider v-model="videoState.volume" vertical height="100px" @input="handleChangeVideoVolume" />
+                        </div>
+                    </div>
+                    <!-- 倍速 -->
+                    <div class="speed-btn">
+                        <span class="num">{{videoState.speed}}X</span>
+                        <div class="speed-opt-box">
+                            <span 
+                                :class="['item',videoState.speed==item&&'active']" 
+                                v-for="item in videoState.speedOpt" 
+                                :key="item"
+                                @click.stop="handleChangeVideoSpeed(item)"
+                            >{{item}}x</span>
+                        </div>
+                    </div>
+                    <!-- 全屏按钮 -->
+                    <div class="fullpage-screen-btn hover-btn" @click.stop="handleChangeVideoScreen">
+                        <svg v-if="videoState.isPageFullScreen||videoState.isFullScreen" t="1668999840019" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1951" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M298.666667 0c-25.6 0-42.666667 17.066667-42.666667 42.666667v192c0 12.8-8.533333 21.333333-21.333333 21.333333H42.666667c-25.6 0-42.666667 17.066667-42.666667 42.666667s17.066667 42.666667 42.666667 42.666666h192C294.4 341.333333 341.333333 294.4 341.333333 234.666667V42.666667c0-25.6-17.066667-42.666667-42.666666-42.666667zM789.333333 341.333333H981.333333c25.6 0 42.666667-17.066667 42.666667-42.666666s-17.066667-42.666667-42.666667-42.666667h-192c-12.8 0-21.333333-8.533333-21.333333-21.333333V42.666667c0-25.6-17.066667-42.666667-42.666667-42.666667s-42.666667 17.066667-42.666666 42.666667v192C682.666667 294.4 729.6 341.333333 789.333333 341.333333zM234.666667 682.666667H42.666667c-25.6 0-42.666667 17.066667-42.666667 42.666666s17.066667 42.666667 42.666667 42.666667h192c12.8 0 21.333333 8.533333 21.333333 21.333333V981.333333c0 25.6 17.066667 42.666667 42.666667 42.666667s42.666667-17.066667 42.666666-42.666667v-192C341.333333 729.6 294.4 682.666667 234.666667 682.666667zM981.333333 682.666667h-192c-59.733333 0-106.666667 46.933333-106.666666 106.666666V981.333333c0 25.6 17.066667 42.666667 42.666666 42.666667s42.666667-17.066667 42.666667-42.666667v-192c0-12.8 8.533333-21.333333 21.333333-21.333333H981.333333c25.6 0 42.666667-17.066667 42.666667-42.666667s-17.066667-42.666667-42.666667-42.666666z" p-id="1952" data-spm-anchor-id="a313x.7781069.0.i0" class="selected" fill="#ffffff"></path>
+                        </svg>
+                        <svg v-else t="1668762444009" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2682" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
+                            <path d="M170.666667 170.666667v213.333333H85.333333V85.333333h298.666667v85.333334H170.666667z m682.666666 213.333333V170.666667h-213.333333V85.333333h298.666667v298.666667h-85.333334zM170.666667 640v213.333333h213.333333v85.333334H85.333333v-298.666667h85.333334z m682.666666 0h85.333334v298.666667h-298.666667v-85.333334h213.333333v-213.333333z" p-id="2683" data-spm-anchor-id="a313x.7781069.0.i0" class="selected" fill="#ffffff"></path>
+                        </svg>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <!-- 弹幕画布 -->
+        <canvas class="barrage-box"></canvas>
+    </div>
+</template>
+
+<style lang="scss" scoped>
+// 清除浮动
+.clear-float::after {
+  content: "";
+  height: 0;
+  line-height: 0;
+  display: block;
+  visibility: hidden;
+  clear: both;
+}
+.video-wrap{
+    position: relative;
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+    background-color: rgba(0,0,0,1);
+    .video{
+        width: 100%;
+        height: 100%;
+        object-fit: contain;
+        display: block;
+        box-sizing: border-box;
+    }
+    .video-control-box{
+        position: absolute;
+        z-index: 5;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        .top-btn-box{
+            padding: 5px;
+            background-color: rgba(0,0,0,0.7);
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            position: relative;
+            z-index: 10;
+            .hover-btn{
+                cursor: pointer;
+                &:hover{
+                    background-color: #303031;
+                }
+            }
+            .play-btn{
+                width: 40px;
+                height: 40px;
+                padding-left: 10px;
+                padding-top: 10px;
+                border-radius: 50%;
+                .icon{
+                    width: 20px;
+                    height: 20px;
+                }
+            }
+            .video-time{
+                color: #fff;
+                font-size: 14px;
+                display: inline-block;
+                margin-left: 10px;
+            }
+            .fullpage-screen-btn{
+                width: 40px;
+                height: 40px;
+                padding-left: 10px;
+                padding-top: 10px;
+                border-radius: 50%;
+                .icon{
+                    width: 20px;
+                    height: 20px;
+                }
+            }
+            .speed-btn{
+                color: #fff;
+                font-size: 13px;
+                margin-right: 10px;
+                cursor: pointer;
+                display: inline-block;
+                padding: 3px 8px;
+                border-radius: 20px;
+                position: relative;
+                &:hover{
+                    background-color: #303031;
+                    .speed-opt-box{
+                        display: block;
+                    }
+                }
+                .speed-opt-box{
+                    display: none;
+                    position: absolute;
+                    width: 80px;
+                    bottom: 105%;
+                    left: 50%;
+                    transform: translateX(-50%);
+                    background-color: #303031;
+                    color: #fff;
+                    font-size: 14px;
+                    border-radius: 2px;
+                    padding: 5px 0;
+                    .item{
+                        display: block;
+                        text-align: center;
+                        padding: 5px 0;
+                        &.active{
+                            color: #F3A52F;
+                        }
+                    }
+                }
+            }
+            .volume-btn{
+                width: 40px;
+                height: 40px;
+                padding-left: 10px;
+                padding-top: 10px;
+                border-radius: 50%;
+                cursor: pointer;
+                margin-right: 10px;
+                position: relative;
+                &:hover{
+                    .volume-slider-box{
+                        display: block;
+                    }
+                }
+                .icon{
+                    width: 20px;
+                    height: 20px;
+                }
+                .volume-slider-box{
+                    display: none;
+                    position: absolute;
+                    bottom: 100%;
+                    left: 50%;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+
+        .video-progress-slider-box{
+            position: relative;
+            width: 100%;
+            height: 3px;
+            background-color: #303031;
+            cursor: pointer;
+            .preload-slider{
+                position: absolute;
+                height: 100%;
+                background-color: #717171;
+                left: 0;
+                z-index: 1;
+            }
+            .active-slider{
+                position: absolute;
+                height: 100%;
+                left: 0;
+                z-index: 15;
+                --el-slider-height:3px;
+                --el-slider-button-wrapper-size:18px;
+                --el-slider-button-size:16px;
+                --el-slider-button-wrapper-offset:-12px;
+                :deep(.el-slider__button-wrapper){
+                    display: none;
+                }
+                :deep(.el-slider__runway){
+                    background-color: transparent;
+                    &:hover{
+                        .el-slider__button-wrapper{
+                            display: block;
+                        }
+                    }
+                }
+                
+            }
+        }
+    }
+    .barrage-box{
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        left: 0;
+        top: 0;
+        right: 0;
+        bottom: 0;
+        pointer-events: none;
+        z-index: 10;
+    }
+}
+.page-fullscreen{
+    position: fixed;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+}
+
+
+</style>

+ 5 - 1
src/views/report/Index.vue

@@ -3,6 +3,7 @@ import {ref,reactive, onMounted} from 'vue'
 import { useRoute, useRouter } from "vue-router";
 import {apiReportClassifyList,apiReportList} from '@/api/report'
 import { useWindowSize } from '@vueuse/core'
+import videoPlayBox from '@/components/VideoPlayBox.vue'
 
 const { width, height } = useWindowSize()
 
@@ -215,7 +216,10 @@ let filterSize=ref('30%')
         <div class="about-us-box" v-show="listState.firstClassifyId==0||(listState.firstClassifyId!=0&&listState.secClassifyId==0&&width<768)">
             <div class="flex box top-box">
                 <div class="left">
-                    <video controls autoplay loop src="https://hongze.oss-cn-shanghai.aliyuncs.com/static/video_03.mp4"></video>
+                    <!-- <video controls loop src="https://hongze.oss-cn-shanghai.aliyuncs.com/static/video_03.mp4"></video> -->
+                    <videoPlayBox
+                        videoUrl="https://hongze.oss-cn-shanghai.aliyuncs.com/static/video_03.mp4"
+                    />
                 </div>
                 <div class="right">
                     <div class="con">