mm.site/src/components/VideoPlayer.js

236 lines
5.0 KiB
JavaScript
Raw Normal View History

2023-10-04 11:38:35 +00:00
import { LitElement, css, html } from 'lit'
import './RangeSlider.js'
import './SvgIcon.js'
import { formatSeconds } from '../api/utils.js'
class VideoPlayer extends LitElement {
static properties = {
media: { type: String },
autoplay: { type: Boolean },
videoEl: { state: true },
playing: { state: true },
controls: { state: true },
ctrlTimer: { state: true },
position: { state: true },
duration: { state: true },
remain: { state: true }
}
constructor() {
super()
this.media = ''
this.hostHeight = 0
this.hostWidth = 0
this.playing = false
this.controls = true
this.ctrlTimer = {}
this.position = 0
this.duration = 0
this.remain = true
this.autoplay = false
this.videoEl = document.createElement('video')
this.videoEl.playsInline = true
this.videoEl.preload = 'metadata'
}
firstUpdated() {
this.videoEl.addEventListener('loadeddata', () => {
this.duration = this.videoEl.duration
})
this.videoEl.addEventListener('timeupdate', () => {
this.position = this.videoEl.currentTime
})
}
willUpdate(att) {
if (att.has('media')) {
this.videoEl.src = this.media
if (this.autoplay) {
this.togglePlay()
this.interact()
}
}
this.update()
}
togglePlay() {
if (this.playing) {
this.videoEl.pause()
} else {
this.videoEl.play()
}
this.playing = !this.playing
}
interact() {
if (this.playing) {
// show controls
this.controls = true
clearTimeout(this.ctrlTimer)
// timeout
this.ctrlTimer = setTimeout(() => {
if (this.playing) {
this.controls = false
}
}, 2000)
}
}
toggleFullscreen() {
const container = this.shadowRoot.querySelector('.videoContainer')
if (container.webkitSupportsFullscreen) {
container.webkitEnterFullscreen()
return
}
if (document.fullscreenElement !== null) {
document.exitFullscreen()
container.setAttribute('data-fullscreen', false)
} else {
container.requestFullscreen()
container.setAttribute('data-fullscreen', true)
}
}
toggleDuration() {
console.log(this.remain)
this.remain = !this.remain
}
seek({ detail }) {
if (detail) {
this.interact()
this.videoEl.currentTime = detail.value
this.position = detail.value
}
}
render() {
return html`
<div class="videoContainer">
${this.videoEl}
<div class="touchbox" @click=${this.interact}>
<div class="playPause ${this.controls ? '' : 'invisible'}" @click=${this.togglePlay}>
<mm-icon class="play ${this.playing ? '' : 'invisible'}" name="pause"></mm-icon>
<mm-icon class="pause ${this.playing ? 'invisible' : ''}" name="play"></mm-icon>
</div>
<div class="controls ${this.controls ? '' : 'invisible'}">
<mm-icon @click=${this.toggleFullscreen} class="fullscreen" name="fullscreen"></mm-icon>
<mm-range
value="${this.position}"
max="${this.duration}"
step="0.1"
@input=${this.seek}
></mm-range>
<div class="time">
<span class="pos">${formatSeconds(this.position)}</span>
<span class="dur" @click=${this.toggleDuration}>${ this.remain ? `-${formatSeconds(this.duration - this.position)}` : formatSeconds(this.duration)}</span>
</div>
</div>
</div>
</div>
`
}
static styles = css`
:host {
height: 100%;
width: 100%;
position: relative;
}
video {
position: absolute;
display: block;
inset: 0;
width: 100%;
height: 100%;
background: black;
}
.controls {
position: absolute;
padding-inline: 0.75em;
padding-block: 0.5em;
left: 0;
right: 0;
bottom: 0;
z-index: 5;
transition: opacity 0.25s ease;
}
.time {
display: flex;
color: white;
font-size: 0.65em;
padding-inline: 0.125em;
padding-block: 1em 0.5em;
}
.dur {
margin-inline-start: auto;
}
mm-icon {
display: block;
color: white;
}
mm-icon.fullscreen {
font-size: 1.25em;
width: 1em;
margin-inline-start: auto;
margin-block-end: 1em;
}
mm-range {
--track-height: 0.3em;
--thumb-size: 0.8em;
--track-color: #ffffff50;
}
.touchbox {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
inset: 0;
opacity: 1;
transition: opacity 0.25s ease;
}
.playPause {
display: grid;
align-items: center;
justify-content: center;
font-size: 2.25em;
transition: opacity 0.25 ease;
}
.playPause > mm-icon {
inset: 0;
grid-column: 1 / span 1;
grid-row: 1 / span 1;
}
.invisible {
pointer-events: none;
opacity: 0;
}
`
}
customElements.define('mm-vplayer', VideoPlayer)