import { $, isMobile } from '@oplayer/core'
import { Icons } from '../functions'
import { UIInterface } from '../types'
import { DRAG_EVENT_MAP, formatTime } from '../utils'
import renderHighlight, { highlightCls } from './highlight'
import renderThumbnail, { vttThumbnailsCls } from './Thumbnail'
import { buffered, dot, hit, played, progress, progressDragging, progressInner } from './Progress.style'
const render = (it: UIInterface, el: HTMLElement) => {
const { player, config } = it
const $dom = (it.$progress = $.create(
`div.${progress}`,
{},
`
00:00
${Icons.get('progressIndicator') || ``}
`
))
const firstElement = $dom.firstElementChild! as HTMLDivElement
if (config.thumbnails?.isVTT) {
console.warn('vtt thumbnails support by @oplayer/pluins')
} else {
renderThumbnail(it, firstElement)
}
//@ts-ignore
it.vttThumbnailsCls = vttThumbnailsCls
renderHighlight(it, firstElement)
const $buffered = $dom.querySelector(`.${buffered}`)!
const $played = $dom.querySelector(`.${played}`)!
const $playedDto = $dom.querySelector(`.${dot}`)!
const $hit = $dom.querySelector(`.${hit}`)!
let isDargMoving = false
const getSlidingValue = (event: MouseEvent | TouchEvent) => {
const rect = $dom.getBoundingClientRect()
const value =
(((event).clientX || (event).changedTouches[0]!.clientX) - rect.left) /
rect.width
return value >= 1 ? 1 : value <= 0 ? 0 : value
}
const sync = (e: MouseEvent | TouchEvent) => {
const rate = getSlidingValue(e)
const percentage = rate * 100
$played.style.width = percentage + '%'
$playedDto.style.transform = `translateX(${percentage}%)`
$hit.innerText = formatTime(player.duration * rate)
$hit.style.left = `${percentage}%`
return rate
}
// dragging
$dom.addEventListener(DRAG_EVENT_MAP.dragStart, (e) => {
isDargMoving = true
$dom.classList.add(progressDragging)
const rate = sync(e)
it.progressHoverCallback.forEach((cb) => cb(rate))
function moving(e: MouseEvent | TouchEvent) {
e.preventDefault()
const rate = sync(e)
it.progressHoverCallback.forEach((cb) => cb(rate))
}
document.addEventListener(DRAG_EVENT_MAP.dragMove, moving, { passive: false })
document.addEventListener(
DRAG_EVENT_MAP.dragEnd,
(e) => {
$dom.classList.remove(progressDragging)
isDargMoving = false
document.removeEventListener(DRAG_EVENT_MAP.dragMove, moving)
if (!isNaN(player.duration)) player.seek(getSlidingValue(e) * player.duration)
},
{ once: true }
)
})
if (!isMobile) {
$dom.addEventListener('mouseenter', () => {
if (isDargMoving) return
it.progressHoverCallback.forEach((cb) => cb())
})
$dom.addEventListener(
'mousemove',
(e) => {
if (isDargMoving) return
$dom.classList.add(progressDragging)
if ((e.target).classList.contains(highlightCls)) {
$hit.style.display = 'none'
} else {
$hit.removeAttribute('style')
}
const rate = getSlidingValue(e)
$hit.innerText = formatTime(player.duration * rate)
$hit.style.left = `${rate * 100}%`
it.progressHoverCallback.forEach((cb) => cb(rate))
},
{ passive: false }
)
$dom.addEventListener('mouseleave', () => {
if (!isDargMoving) $dom.classList.remove(progressDragging)
})
}
player.on(['timeupdate', 'seeking'], () => {
if (isDargMoving) return
const { currentTime, duration } = player
const playedWidth = (currentTime / duration) * 100 || 0
$played.style.width = playedWidth + '%'
$playedDto.style.transform = `translateX(${playedWidth}%)`
})
player.on('progress', () => {
const buffered = player.buffered.length
? (player.buffered.end(player.buffered.length - 1) / player.duration) * 100
: 0
$buffered.style.width = Math.min(buffered, 100) + '%'
})
player.on('videosourcechange', () => {
$buffered.style.width = '0%'
$played.style.width = '0%'
$playedDto.style.transform = `translateX(0%)`
})
$.render($dom, el)
}
export default render