import { $, isIOS, isMobile } from '@oplayer/core' import { Icons } from '../functions/icons' import { controllerHidden, icon, off, on, tooltip } from '../style' import { formatTime, screenShot } from '../utils' import renderVolumeBar from './VolumeBar' import renderProgress from './Progress' import { isFullscreen, isWebFullscreen } from '../listeners/fullscreen' import type { UIInterface } from '../types' import { controllers, dropdown, dropdownHoverable, expand, time, live, withIcon, centerProgressWrap, textIcon } from './ControllerBottom.style' import { progress } from './Progress.style' export const controllerBottomWrap = $.css({ position: 'absolute', left: 0, right: 0, bottom: 0, 'z-index': 7, padding: '0 0.5em', transition: 'transform 0.3s ease, padding 0.3s ease', 'min-height': 'var(--control-bar-height)', '&::before': { position: 'absolute', content: "''", width: '100%', display: 'block', bottom: 0, left: 0, 'z-index': -1, top: '-1em', transition: 'opacity 0.3s ease', 'pointer-events': 'none', 'background-image': 'linear-gradient(transparent, rgba(0, 0, 0, .3))' }, //TODO: support display-mode // '@media (display-mode: fullscreen)': { // 'padding-bottom': 'env(safe-area-inset-bottom)' // }, [`@global .${controllerHidden} &`]: { 'pointer-events': 'none', transform: 'translateY(calc(100% - 0.55em))', padding: 0, '&::before': { opacity: 0 } }, [`@global .${controllerHidden} .${progress}`]: { 'padding-left': 0, 'padding-right': 0 } }) const render = (it: UIInterface, $el: HTMLDivElement) => { const { player, config } = it const el = $.render($.create(`div.${controllerBottomWrap}`), $el) if (!config.theme.progress?.mini || player.options.isLive) { $.css({ [`@global .${controllerHidden} .${controllerBottomWrap}`]: { transform: 'translateY(100%)' } }) } const [playLabel, pauseLabel, screenshotLabel, pipLabel, fullscreenLabel, previousLabel, nextLabel] = [ player.locales.get('Play'), player.locales.get('Pause'), player.locales.get('Screenshot'), player.locales.get('Picture in Picture'), player.locales.get(player.isFullscreenEnabled ? 'Fullscreen' : 'WebFullscreen'), player.locales.get('Previous'), player.locales.get('Next') ] const [previousSvg, nextSvg] = [Icons.get('previous') || '', Icons.get('next') || ''] const $dom = (it.$controllerBottom = $.create( `div.${controllers}`, {}, `
${ previousSvg && `` } ${nextSvg && ``} ${ player.options.isLive ? `` : `${ player.options.isLive || player.$video.preload == 'none' ? '00:00' : '00:00 / --:--' }` }
${!isIOS ? `
` : ''}
${ config.screenshot ? `` : '' } ${ config.pictureInPicture && player.isPipEnabled ? `` : '' } ${ config.fullscreen ? `` : '' }
` )) if ( (config.theme.progress?.position == 'auto' && isMobile) || config.theme.progress?.position == 'center' ) { renderProgress(it, $dom.querySelector(`.${centerProgressWrap}`)!) } else { renderProgress(it, el) } const $volume = $dom.querySelector('button[aria-label=Volume]')! // IOS只能使用物理按键控制音量大小 if (!isIOS) renderVolumeBar(player, $volume.nextElementSibling! as HTMLDivElement) const $play = $dom.querySelector(`button[aria-label=${playLabel}]`)! const $time = $dom.querySelector('.' + time)! const $fullscreen = $dom.querySelector(`button[aria-label="${fullscreenLabel}"]`)! const $pip = $dom.querySelector(`button[aria-label="${pipLabel}"]`)! const switcher = (el: HTMLElement, display: boolean) => { el.classList.add(display ? on : off) el.classList.remove(display ? off : on) } if (config.fullscreen) { player.on('fullscreenchange', () => setTimeout(() => { switcher($fullscreen, isFullscreen(player)) }) ) } if (config.pictureInPicture) { player.on(['enterpictureinpicture', 'leavepictureinpicture'], () => switcher($pip, player.isInPip)) } player.on(['play', 'pause', 'videosourcechange'], () => { $play.setAttribute('aria-label', player.isPlaying ? pauseLabel : playLabel) switcher($play, player.isPlaying) }) const LIVE = player.locales.get('Live') player.on('volumechange', () => switcher($volume, player.isMuted)) player.on(['durationchange', 'timeupdate', 'seeking', 'seeked'], () => { if (player.options.isLive) { $time.innerText = `${LIVE} ${formatTime(player.currentTime < 0 ? player.duration : player.currentTime)}` return } $time.innerText = `${formatTime(player.currentTime)} / ${formatTime(player.duration)}` }) player.on('videosourcechange', () => { $time.innerText = player.options.isLive || player.$video.preload == 'none' ? '00:00' : '00:00 / --:--' }) $dom.addEventListener('click', (e: Event) => { const target = e.target! as HTMLDivElement const label = target.getAttribute('aria-label') switch (label) { case playLabel: case pauseLabel: return player.togglePlay() case 'Volume': if (isMobile && !isIOS) return if (player.isMuted) { player.unmute() } else { player.mute() } break case pipLabel: return player.togglePip() case fullscreenLabel: if (isWebFullscreen(player) || !player.isFullscreenEnabled) { player.emit('fullscreenchange', { isWeb: true }) } else { player.toggleFullScreen() } return case screenshotLabel: screenShot(player) break case nextLabel: player.emit('next') break case previousLabel: player.emit('previous') break default: break } }) $.render($dom, el) } export default render