import { $, isIOS, isMobile } from '@nusaplayer/core' import { Icons } from '../functions/icons' import { controllerHidden, icon, off, on, tooltip, fullscreenSafeArea } 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: isMobile ? '0 0.3em' : '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: '-2.75em', transition: 'opacity 0.3s ease', 'pointer-events': 'none', 'background-image': 'linear-gradient(transparent, rgba(0, 0, 0, .72))' }, ...fullscreenSafeArea({ bottom: '0px', x: isMobile ? '0.3em' : '0.5em' }), [`@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, webFullscreenLabel, previousLabel, nextLabel ] = [ player.locales.get('Play'), player.locales.get('Pause'), player.locales.get('Screenshot'), player.locales.get('Picture in Picture'), player.locales.get('Fullscreen'), player.locales.get('WebFullscreen'), player.locales.get('Previous'), player.locales.get('Next') ] const showWebFullscreen = Boolean( config.fullscreen && config.webFullscreen !== false && player.isFullscreenEnabled ) const fullscreenButtonLabel = player.isFullscreenEnabled ? fullscreenLabel : webFullscreenLabel 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.theme.controller?.header && config.screenshot ? `` : '' } ${ !config.theme.controller?.header && config.pictureInPicture && player.isPipEnabled ? `` : '' } ${ showWebFullscreen ? `` : '' } ${ 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="${fullscreenButtonLabel}"]` ) const $webFullscreen = $dom.querySelector( `button[aria-label="${webFullscreenLabel}"]` ) 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(() => { if ($webFullscreen) switcher($webFullscreen, isWebFullscreen(player)) if ($fullscreen) { switcher( $fullscreen, player.isFullscreenEnabled ? player.isFullScreen : isFullscreen(player) ) } }) ) } if (config.pictureInPicture && $pip) { 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) { const wrap = $volume.closest(`.${dropdown}`) if (wrap) { const isOpen = wrap.getAttribute('data-open') == 'true' player.$root.querySelectorAll('[data-open="true"]').forEach((el) => { if (el !== wrap) el.removeAttribute('data-open') }) if (isOpen) wrap.removeAttribute('data-open') else wrap.setAttribute('data-open', 'true') } break } if (player.isMuted) { player.unmute() } else { player.mute() } break case pipLabel: return player.togglePip() case webFullscreenLabel: player.emit('fullscreenchange', { isWeb: true }) return case fullscreenLabel: if (!player.isFullscreenEnabled) { player.emit('fullscreenchange', { isWeb: true }) } else { if (isWebFullscreen(player)) { player.emit('fullscreenchange', { isWeb: true }) } 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