852 lines
25 KiB
Vue
852 lines
25 KiB
Vue
<template>
|
||
<div id="videoContainer" class="vr-video">
|
||
<div class="video-wrapper">
|
||
<div id="video" @mousemove="ControlVisible()">
|
||
</div>
|
||
<div v-if="playVariables.statistics" class="vr-statistics">
|
||
<p>type:{{playVariables.type || '???'}}</p>
|
||
<p>status:{{playVariables.status || '???'}}</p>
|
||
<p>currentTime: {{playVariables.currentTime}}</p>
|
||
<p>totalTime: {{playVariables.totalTime}}</p>
|
||
<span v-if="deviceOrientationData.isSupported">
|
||
<p>
|
||
陀螺仪数据:
|
||
</p>
|
||
<p>
|
||
alpha(z轴):{{deviceOrientationData.alpha||'手机查看参数'}}
|
||
</p>
|
||
<p>
|
||
beta(x轴):{{deviceOrientationData.beta||'手机查看参数'}}
|
||
</p>
|
||
<p>
|
||
gamma(y轴):{{deviceOrientationData.gamma||'手机查看参数'}}
|
||
</p></span>
|
||
|
||
</div>
|
||
<div class="vr-func">
|
||
<div v-if="playVariables.status == 'pause'&&!playVariables.playClick"
|
||
@click="player.play();playVariables.playClick=true" class="btns-play">
|
||
<i class="iconfont icon-icon_play"></i>
|
||
</div>
|
||
<div v-else-if="playVariables.status == 'playing'&&!playVariables.playClick" @click="player.pause()"
|
||
class="btns-pause">
|
||
<i class="iconfont icon-ai07"></i>
|
||
</div>
|
||
</div>
|
||
<div v-if="playVariables.status == 'loading'" class="vr-loading">
|
||
<div class="border"></div>
|
||
<div class="slow"></div>
|
||
<div class="fast"></div>
|
||
</div>
|
||
<div class="vr-bar" v-if="playVariables.playClick" id="control">
|
||
<div class="bg"></div>
|
||
<div class="btns">
|
||
<div v-if="playVariables.status === 'playing'" @click="player.pause()" class="btns-pause">
|
||
<i class="iconfont icon-ai07"></i>
|
||
</div>
|
||
<div v-else @click="player.play()" class="btns-play">
|
||
<i class="iconfont icon-icon_play"></i>
|
||
</div>
|
||
</div>
|
||
<div v-if="playVariables.type == 'normal'" class="progress-container">
|
||
<div @click="jumpTo($event)" class="progress-wrapper">
|
||
<div class="progress" id="progress-play"></div>
|
||
</div>
|
||
<div class="btn-wrapper">
|
||
<div class="btn" id="progress-btn"></div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="type">
|
||
<span class="statu-circle"></span>
|
||
<span>{{playVariables.type}}</span>
|
||
</div>
|
||
<div @click="changeFullscreenStatu()" class="fullscreen">
|
||
<i class="iconfont icon-quanping"></i>
|
||
</div>
|
||
</div>
|
||
<div class="vr-notice" id="vrNotice" v-html="playVariables.notice">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import * as THREE from 'three'
|
||
import threeOrbitControls from 'three-orbit-controls'
|
||
import flvjs from 'flv.js'
|
||
const OrbitControls = threeOrbitControls(THREE)
|
||
|
||
export default {
|
||
name: 'VRLive',
|
||
props: {
|
||
option: {
|
||
default: Object
|
||
}
|
||
},
|
||
data () {
|
||
return {
|
||
camera: null,
|
||
scene: null,
|
||
renderer: null,
|
||
mesh: null,
|
||
video: null,
|
||
controls: null,
|
||
deviceOrientationData: {},
|
||
hls: null,
|
||
player: {},
|
||
videoContainer: '',
|
||
playVariables: {
|
||
/*
|
||
视频类型,默认Normal
|
||
Normal
|
||
HLS
|
||
FLV
|
||
*/
|
||
type: 'Normal',
|
||
/*
|
||
播放状态,与视频播放状态对应
|
||
loading:加载中,
|
||
playing:视频播放中,包括视频中间加载后继续播放
|
||
pause:暂停或用户未点开始按钮
|
||
*/
|
||
status: 'pause',
|
||
playClick: false,
|
||
// 控件显示状态
|
||
currentTime: 0,
|
||
progress: 0,
|
||
// fullscreenStatu: false,
|
||
notice: '',
|
||
statistics: false,
|
||
error: {
|
||
code: 0,
|
||
msg: ''
|
||
}
|
||
}
|
||
// cameraVariables: {
|
||
// isUserInteracting: false,
|
||
// lon: 0,
|
||
// lat: 0,
|
||
// phi: 0,
|
||
// theta: 0,
|
||
// distance: 1,
|
||
// onPointerDownPointerX: 0,
|
||
// onPointerDownPointerY: 0,
|
||
// onPointerDownLon: 0,
|
||
// onPointerDownLat: 0,
|
||
// sensitivity: 0.1
|
||
// }
|
||
}
|
||
},
|
||
// computed: {
|
||
// fullScreenStatu () {
|
||
// return document.mozFullScreen || document.webkitIsFullScreen
|
||
// }
|
||
// },
|
||
// watch: {
|
||
// playVariables (val) {
|
||
|
||
// },
|
||
// // fullScreenStatu (val) {
|
||
// // console.log(val)
|
||
// // if (val) {
|
||
// // this.videoContainer.classList.add('fullScreen-mobile')
|
||
// // } else {
|
||
// // this.videoContainer.classList.remove('fullScreen-mobile')
|
||
// // }
|
||
// // }
|
||
// },
|
||
mounted () {
|
||
if (this.check()) {
|
||
this.playVariables.statistics = this.option.statistics
|
||
this.playVariables.type = this.option.source.type
|
||
this.videoContainer = document.getElementById('videoContainer')
|
||
this.init()
|
||
}
|
||
// 检查浏览器音频支持
|
||
if (!flvjs.isSupported()) {
|
||
console.error('Browser does not support flv.js')
|
||
return
|
||
}
|
||
|
||
// 检查浏览器音频编解码器支持
|
||
const audioTest = document.createElement('audio')
|
||
if (!audioTest.canPlayType('audio/aac')) {
|
||
console.warn('Browser might not support AAC audio codec')
|
||
}
|
||
},
|
||
methods: {
|
||
check () {
|
||
if (!this.option.source) {
|
||
console.log(new Error('无播放源'))
|
||
} else if (!this.option.source.type) {
|
||
console.log(new Error('播放源type不能为空'))
|
||
} else if (!this.option.source.url) {
|
||
console.log(new Error('播放源url不能为空'))
|
||
} else {
|
||
return true
|
||
}
|
||
},
|
||
init () {
|
||
const container = document.getElementById('video')
|
||
this.initScene()
|
||
this.initCamera(container)
|
||
this.initRenderer(container)
|
||
this.initContent()
|
||
this.initControls(container)
|
||
this.render()
|
||
// this.addMouseEvent(container)
|
||
window.addEventListener('deviceorientation', function (event) {
|
||
this.deviceOrientationData.isSupported = true
|
||
this.deviceOrientationData = event
|
||
}.bind(this), false)
|
||
window.addEventListener('resize', function () {
|
||
this.onWindowResize(container)
|
||
}.bind(this))
|
||
},
|
||
initScene () {
|
||
this.scene = new THREE.Scene()
|
||
},
|
||
initCamera (el) {
|
||
this.camera = new THREE.PerspectiveCamera(75, el.clientWidth / el.clientHeight, 1, 1100)
|
||
this.camera.position.set(1, 0, 0)
|
||
// this.camera.target = new THREE.Vector3(0, 0, 0)
|
||
},
|
||
initRenderer (el) {
|
||
this.renderer = new THREE.WebGLRenderer()
|
||
this.renderer.setSize(el.offsetWidth, el.offsetHeight)
|
||
el.appendChild(this.renderer.domElement)
|
||
},
|
||
initVideo () {
|
||
this.video = document.createElement('video')
|
||
this.video.preload = 'auto'
|
||
this.video.muted = false
|
||
this.video.volume = 1.0
|
||
this.video.crossOrigin = 'anonymous'
|
||
// 立即设置 player 为 video 元素
|
||
this.player = this.video
|
||
|
||
this.video.addEventListener('waiting', function (event) {
|
||
this.playVariables.status = 'loading'
|
||
}.bind(this))
|
||
this.video.addEventListener('playing', function (event) {
|
||
this.playVariables.status = 'playing'
|
||
}.bind(this))
|
||
this.video.addEventListener('pause', function (event) {
|
||
this.playVariables.status = 'pause'
|
||
}.bind(this))
|
||
this.video.addEventListener('canplay', function (event) {
|
||
this.playVariables.duration = this.player.duration
|
||
if (this.playVariables.status === 'loading') {
|
||
this.playVariables.status = 'playing'
|
||
}
|
||
}.bind(this))
|
||
|
||
// 判断视频类型
|
||
switch (this.option.source.type) {
|
||
case 'flv':
|
||
this.getFLV()
|
||
break
|
||
case 'hls':
|
||
this.getHLS(this.option.source.url, this.video)
|
||
break
|
||
case 'normal':
|
||
this.getNormalVideo(this.option.source.url, this.video)
|
||
break
|
||
default:
|
||
this.playVariables.error.code = 1
|
||
this.playVariables.error.msg = '未知的视频类型'
|
||
break
|
||
}
|
||
},
|
||
initContent () {
|
||
this.initVideo()
|
||
var geometry = new THREE.SphereBufferGeometry(300, 90, 90)
|
||
geometry.scale(-1, 1, 1)
|
||
var texture = new THREE.VideoTexture(this.video)
|
||
texture.minFilter = THREE.LinearFilter
|
||
texture.format = THREE.RGBFormat
|
||
var material = new THREE.MeshBasicMaterial({
|
||
map: texture
|
||
})
|
||
this.mesh = new THREE.Mesh(geometry, material)
|
||
this.mesh.position.set(0, 0, 0)
|
||
this.scene.add(this.mesh)
|
||
},
|
||
initControls (el) {
|
||
this.controls = new OrbitControls(this.camera, el)
|
||
// this.controls.target = new THREE.Vector3(0, Math.PI, 0)
|
||
this.controls.rotateSpeed = 0.05
|
||
this.controls.enableDamping = true
|
||
this.controls.dampingFactor = 0.05
|
||
},
|
||
render () {
|
||
requestAnimationFrame(this.render)
|
||
this.controls.update()
|
||
// this.cameraUpdate()
|
||
this.renderer.render(this.scene, this.camera)
|
||
},
|
||
getNormalVideo (sourceURL, el) {
|
||
const source = document.createElement('source')
|
||
source.src = sourceURL
|
||
// source.type = 'video/mp4'
|
||
el.appendChild(source)
|
||
this.player = el
|
||
},
|
||
getHLS (sourceURL, el) {
|
||
const Hls = require('hls.js')
|
||
if (Hls.isSupported()) {
|
||
this.hls = new Hls()
|
||
this.hls.loadSource(sourceURL)
|
||
this.hls.attachMedia(el)
|
||
this.hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
||
console.log('加载成功')
|
||
})
|
||
this.hls.on(Hls.Events.ERROR, (event, data) => {
|
||
throw new Error(data.response.code + ' ' + data.response.text)
|
||
})
|
||
}
|
||
this.player = el
|
||
},
|
||
// abandoned 对接rtmp流
|
||
// 非原生video标签。
|
||
// confuse object?如何作为纹理渲染?
|
||
// getRTMP (sourceURL, el) {
|
||
// el.id = 'rtmpVideo'
|
||
// console.log(el)
|
||
// this.player = videojs(el, {
|
||
// sources: [{
|
||
// src: sourceURL
|
||
// }]
|
||
// })
|
||
// this.player.on('ready', function () {
|
||
// console.log('准备就绪')
|
||
// })
|
||
// },
|
||
getFLV () {
|
||
if (flvjs.isSupported()) {
|
||
const flvPlayer = flvjs.createPlayer({
|
||
type: 'flv',
|
||
hasAudio: true,
|
||
hasVideo: true,
|
||
url: this.option.source.url
|
||
})
|
||
|
||
// 添加事件监听器
|
||
flvPlayer.on(flvjs.Events.ERROR, (errorType, errorDetail) => {
|
||
console.error('FLV Player Error:', errorType, errorDetail)
|
||
})
|
||
|
||
flvPlayer.on(flvjs.Events.MEDIA_INFO, (mediaInfo) => {
|
||
console.log('FLV Media Info:', mediaInfo)
|
||
})
|
||
|
||
flvPlayer.attachMediaElement(this.video)
|
||
flvPlayer.load()
|
||
return flvPlayer
|
||
}
|
||
},
|
||
onWindowResize (el) {
|
||
this.camera.aspect = el.clientWidth / el.clientHeight
|
||
this.camera.updateProjectionMatrix()
|
||
this.renderer.setSize(el.clientWidth, el.clientHeight)
|
||
},
|
||
// jumpTo ($e) {
|
||
// console.log(e)
|
||
// }
|
||
async changeFullscreenStatu () {
|
||
if (this.playVariables.fullscreenStatu) {
|
||
this.exitFullscreen()
|
||
// screen.orientation.unlock()
|
||
// this.videoContainer.classList.remove('full-screen-mobile')
|
||
} else {
|
||
this.fullScreen()
|
||
if (/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
|
||
this.showNotice('为了更好的观看体验<br>请关闭屏幕锁定横屏观看该视频')
|
||
}
|
||
// 只在谷歌浏览器下生效
|
||
// screen.orientation.lock('landscape-primary')
|
||
// this.videoContainer.classList.add('full-screen-mobile')
|
||
// screen.lockOrientationUniversal = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation
|
||
// new ScreenOrientation().lock('landscape-secondary')
|
||
// console.log(screen)
|
||
// screen.msLockOrientation.lock('landscape-primary')
|
||
}
|
||
this.playVariables.fullscreenStatu = !this.playVariables.fullscreenStatu
|
||
},
|
||
fullScreen () {
|
||
const el = this.videoContainer
|
||
if (el.requestFullscreen) {
|
||
el.requestFullscreen()
|
||
} else if (el.mozRequestFullScreen) {
|
||
el.mozRequestFullScreen()
|
||
} else if (el.webkitRequestFullScreen) {
|
||
el.webkitRequestFullScreen()
|
||
}
|
||
},
|
||
exitFullscreen () {
|
||
const el = document
|
||
el.exitFullscreen()
|
||
if (el.exitFullscreen) {
|
||
el.exitFullscreen()
|
||
} else if (el.mozCancelFullScreen) {
|
||
el.mozCancelFullScreen()
|
||
} else if (el.webkitCancelFullScreen) {
|
||
el.webkitCancelFullScreen()
|
||
}
|
||
},
|
||
showNotice (msg) {
|
||
this.playVariables.notice = msg
|
||
const notice = document.getElementById('vrNotice')
|
||
if (notice.classList.contains('vr-notice-show')) {
|
||
notice.classList.remove('vr-notice-show')
|
||
setTimeout(() => {
|
||
notice.classList.add('vr-notice-show')
|
||
}, 0)
|
||
} else {
|
||
notice.classList.add('vr-notice-show')
|
||
}
|
||
},
|
||
ControlVisible () {
|
||
const control = document.getElementById('control')
|
||
if (this.playVariables.status === 'playing') {
|
||
if (control.classList.contains('control-hidden')) {
|
||
control.classList.remove('control-hidden')
|
||
setTimeout(() => {
|
||
control.classList.add('control-hidden')
|
||
}, 0)
|
||
} else {
|
||
control.classList.add('control-hidden')
|
||
}
|
||
}
|
||
}
|
||
// 有更好的选择 three轨道控制器
|
||
// abandoned 对接鼠标移动事件,剩余惯性尚需对接,使滑动体验更加流畅
|
||
// addMouseEvent (el) {
|
||
// el.addEventListener('mousedown', onDocumentMouseDown.bind(this), false)
|
||
// el.addEventListener('mousemove', onDocumentMouseMove.bind(this), false)
|
||
// el.addEventListener('mouseup', onDocumentMouseUp.bind(this), false)
|
||
// el.addEventListener('touchstart', onDocumentTouchStart.bind(this), false)
|
||
// el.addEventListener('touchmove', onDocumentTouchMove.bind(this), false)
|
||
// el.addEventListener('touchend', onDocumentTouchEnd.bind(this), false)
|
||
|
||
// function onDocumentMouseDown (event) {
|
||
// event.preventDefault()
|
||
|
||
// this.cameraVariables.isUserInteracting = true
|
||
|
||
// this.cameraVariables.onPointerDownPointerX = event.clientX
|
||
// this.cameraVariables.onPointerDownPointerY = event.clientY
|
||
|
||
// this.cameraVariables.onPointerDownLon = this.cameraVariables.lon
|
||
// this.cameraVariables.onPointerDownLat = this.cameraVariables.lat
|
||
// }
|
||
|
||
// function onDocumentMouseMove (event) {
|
||
// if (this.cameraVariables.isUserInteracting === true) {
|
||
// this.cameraVariables.lon =
|
||
// (this.cameraVariables.onPointerDownPointerX - event.clientX) * this.cameraVariables.sensitivity +
|
||
// this.cameraVariables.onPointerDownLon
|
||
// this.cameraVariables.lat =
|
||
// (event.clientY - this.cameraVariables.onPointerDownPointerY) * this.cameraVariables.sensitivity +
|
||
// this.cameraVariables.onPointerDownLat
|
||
// console.log(this.cameraVariables.lat)
|
||
// }
|
||
// }
|
||
|
||
// function onDocumentMouseUp () {
|
||
// this.cameraVariables.isUserInteracting = false
|
||
// }
|
||
|
||
// function onDocumentTouchStart (event) {
|
||
// event.preventDefault()
|
||
|
||
// this.cameraVariables.isUserInteracting = true
|
||
|
||
// this.cameraVariables.onPointerDownPointerX = event.touches[0].clientX
|
||
// this.cameraVariables.onPointerDownPointerY = event.touches[0].clientY
|
||
|
||
// this.cameraVariables.onPointerDownLon = this.cameraVariables.lon
|
||
// this.cameraVariables.onPointerDownLat = this.cameraVariables.lat
|
||
// }
|
||
|
||
// function onDocumentTouchMove (event) {
|
||
// if (this.cameraVariables.isUserInteracting === true) {
|
||
// this.cameraVariables.lon =
|
||
// (this.cameraVariables.onPointerDownPointerX - event.touches[0].clientX) * this.cameraVariables
|
||
// .sensitivity +
|
||
// this.cameraVariables.onPointerDownLon
|
||
// this.cameraVariables.lat =
|
||
// (event.touches[0].clientY - this.cameraVariables.onPointerDownPointerY) * this.cameraVariables
|
||
// .sensitivity +
|
||
// this.cameraVariables.onPointerDownLat
|
||
// }
|
||
// }
|
||
|
||
// function onDocumentTouchEnd () {
|
||
// this.cameraVariables.isUserInteracting = false
|
||
// }
|
||
// // const pre = {
|
||
// // x: '',
|
||
// // y: ''
|
||
// // }
|
||
// // // const cur = {
|
||
// // // x: '',
|
||
// // // y: ''
|
||
// // // }
|
||
// // let isDown = false
|
||
// // const self = this
|
||
// // el.onmousedown = function (event) {
|
||
// // isDown = true
|
||
// // }
|
||
// // el.onmouseup = function (event) {
|
||
// // isDown = false
|
||
// // }
|
||
// // el.onmousemove = function (event) {
|
||
// // if (isDown) {
|
||
// // console.log(event)
|
||
// // console.log(pre)
|
||
// // console.log(self.camera)
|
||
// // if (event.movementY && !event.movementX) {
|
||
// // self.camera.rotation.x -= event.movementY / 500
|
||
// // } else if (!event.movementY && event.movementX) {
|
||
// // self.camera.rotation.y += event.movementX / 500
|
||
// // } else {
|
||
// // pre.x = event.clientX
|
||
// // }
|
||
// // pre.y = event.clientY
|
||
// // }
|
||
// // }
|
||
// },
|
||
// cameraUpdate () {
|
||
// this.cameraVariables.lat = Math.max(-85, Math.min(85, this.cameraVariables.lat))
|
||
// this.cameraVariables.phi = THREE.Math.degToRad(90 - this.cameraVariables.lat)
|
||
// this.cameraVariables.theta = THREE.Math.degToRad(this.cameraVariables.lon)
|
||
// this.camera.position.x = this.cameraVariables.distance * Math.sin(this.cameraVariables.phi) * Math.cos(this.cameraVariables.theta)
|
||
// this.camera.position.y = this.cameraVariables.distance * Math.cos(this.cameraVariables.phi)
|
||
// this.camera.position.z = this.cameraVariables.distance * Math.sin(this.cameraVariables.phi) * Math.sin(this.cameraVariables.theta)
|
||
// this.camera.lookAt(this.camera.target)
|
||
// // 为后面的惯性对接
|
||
// setTimeout(() => {
|
||
// }, 1000)
|
||
// }
|
||
}
|
||
}
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.vr-video {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
.video-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 100%;
|
||
// min-height: 300px;
|
||
}
|
||
|
||
#video {
|
||
width: 100%;
|
||
height: 100%;
|
||
|
||
&:hover {
|
||
.control {
|
||
display: block;
|
||
animation: mousehover 3s;
|
||
animation-delay: 5s;
|
||
animation-fill-mode: forwards;
|
||
}
|
||
}
|
||
}
|
||
|
||
.control-hidden {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
animation: mousehover 2s;
|
||
animation-delay: 3s;
|
||
animation-fill-mode: forwards;
|
||
}
|
||
|
||
@keyframes mousehover {
|
||
100% {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.vr {
|
||
&-func {
|
||
position: absolute;
|
||
z-index: 2;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
height: 50px;
|
||
font-size: 16px;
|
||
|
||
.btns-play,
|
||
.btns-pause {
|
||
i {
|
||
font-size: 4em;
|
||
color: rgba(255, 255, 255, 1);
|
||
transition: color .3s;
|
||
}
|
||
|
||
&:hover {
|
||
cursor: pointer;
|
||
|
||
i {
|
||
color: rgba(255, 255, 255, 0.6);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
&-loading {
|
||
position: absolute;
|
||
z-index: 2;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
border-radius: 50%;
|
||
width: 3em;
|
||
height: 3em;
|
||
|
||
.border {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
border: 0.45em solid rgba(99, 149, 168, 0.5);
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.slow {
|
||
position: absolute;
|
||
z-index: 2;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-width: 0.45em;
|
||
border-style: solid;
|
||
border: 0.45em solid rgba(0, 0, 0, 0);
|
||
border-top-color: rgb(7, 186, 241);
|
||
animation: slow 1.5s linear infinite;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.fast {
|
||
position: absolute;
|
||
z-index: 1;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
border-width: 0.45em;
|
||
border-style: solid;
|
||
border: 0.45em solid rgba(0, 0, 0, 0);
|
||
border-top-color: rgb(10, 126, 161);
|
||
animation: fast 0.75s linear infinite;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
@keyframes slow {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
@keyframes fast {
|
||
0% {
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
25% {
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
50% {
|
||
border-top-color: rgb(195, 236, 248);
|
||
}
|
||
|
||
75% {
|
||
transform: rotate(315deg);
|
||
}
|
||
|
||
100% {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
}
|
||
|
||
&-bar {
|
||
position: absolute;
|
||
z-index: 10;
|
||
bottom: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 1em 1em;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
background-image: linear-gradient(to top, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
|
||
|
||
.btns {
|
||
padding: 0.5em;
|
||
|
||
&-play,
|
||
&-pause {
|
||
i {
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
&:hover {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.progress-container {
|
||
position: relative;
|
||
flex-grow: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 0 1em;
|
||
|
||
.progress-wrapper {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 0.2em;
|
||
border-radius: 30px;
|
||
background-color: rgba(150, 150, 150, 1);
|
||
overflow: hidden;
|
||
|
||
.progress {
|
||
position: absolute;
|
||
width: 0%;
|
||
height: 100%;
|
||
background-color: rgb(22, 175, 236);
|
||
transition: width .5s;
|
||
}
|
||
|
||
&:hover {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
|
||
.btn-wrapper {
|
||
position: absolute;
|
||
z-index: 9;
|
||
left: 1em;
|
||
width: calc(100% - 2em);
|
||
|
||
.btn {
|
||
margin-left: 0;
|
||
width: 0.8em;
|
||
height: 0.8em;
|
||
border-radius: 50%;
|
||
background-color: white;
|
||
}
|
||
}
|
||
}
|
||
|
||
.type {
|
||
padding: 0 2em;
|
||
width: 100%;
|
||
font-size: 0.95em;
|
||
font-weight: 300;
|
||
color: white;
|
||
|
||
.statu-circle {}
|
||
}
|
||
|
||
.fullscreen {
|
||
i {
|
||
font-size: 1.4em;
|
||
color: white;
|
||
}
|
||
|
||
&:hover {
|
||
cursor: pointer;
|
||
}
|
||
}
|
||
}
|
||
|
||
&-notice {
|
||
position: absolute;
|
||
z-index: 99;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
padding: 0.5em 1em;
|
||
white-space: nowrap;
|
||
font-size: 1em;
|
||
color: white;
|
||
background-color: rgba(0, 0, 0, 0.4);
|
||
border-radius: 0.5em;
|
||
|
||
&-show {
|
||
visibility: visible;
|
||
opacity: 1;
|
||
animation: notice 2s;
|
||
animation-delay: 3s;
|
||
animation-fill-mode: forwards;
|
||
}
|
||
|
||
@keyframes notice {
|
||
100% {
|
||
visibility: hidden;
|
||
opacity: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
&-statistics {
|
||
position: absolute;
|
||
z-index: 1;
|
||
top: 1em;
|
||
left: 1em;
|
||
padding: 0.2em 1em;
|
||
font-size: 13px;
|
||
color: white;
|
||
background-color: rgba(0, 0, 0, 0.4);
|
||
border-radius: 0.2em;
|
||
}
|
||
}
|
||
|
||
&:fullscreen {
|
||
.video-wrapper {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
// transform-origin: top left;
|
||
// transform: rotate(90deg) translate(-0vh, -100vw);
|
||
}
|
||
}
|
||
}
|
||
|
||
// .full-screen-mobile {
|
||
// .video-wrapper {
|
||
// position: absolute;
|
||
// z-index: 99;
|
||
// top: 0;
|
||
// left: 0;
|
||
// width: 100vh;
|
||
// height: 100vw;
|
||
// transform-origin: top left;
|
||
// transform: rotate(90deg) translate(-0vh, -100vw);
|
||
// }
|
||
|
||
// // #video {
|
||
// // width: 100%;
|
||
// // height: calc(100vw / 16 * 9);
|
||
// // }
|
||
// }
|
||
</style>
|