123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- <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){
- if (el.requestFullscreen) {
- el.requestFullscreen()
- return true
- } else if (el.msRequestFullscreen) {
- el.msRequestFullscreen()
- return true
- } else if (el.mozRequestFullScreen) {
- el.mozRequestFullScreen()
- return true
- } else if (el.webkitRequestFullScreen) {
- el.webkitRequestFullScreen()
- return true
- }
- return false
- }
- /**
- * 关闭全屏
- */
- function closeFullScreen(){
- document.exitFullscreen() || document.webkitExitFullScreen() || document.msExitFullscreen() || document.mozCancelFullScreen()
- }
- //监听页面全屏变化
- 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(e){
- videoState.play=true
- emit('play',e)
- }
- 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(e){
- videoState.play=false
- emit('pause',e)
- }
- 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"
- autoplay
- @canplay="videoCanPlay"
- @play="videoPlay"
- @pause="videoPause"
- @timeupdate="videoTimeUpdate"
- @progress="videoProgress"
- @click="handleChangeVideoPlay"
- :controls="false"
- x5-playsinline
- raw-controls
- controls360=no
- playsinline
- webkit-playsinline
- x-webkit-airplay="allow"
- x5-video-player-type="h5"
- x5-video-player-fullscreen
- x5-video-orientation="portraint"
- >
- <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>
|