From 2c86501ec59b5c0b0a00c7cdb4585b259fdeaea8 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Thu, 1 Jul 2021 05:30:05 +0300 Subject: [PATCH] Video player fixes: Hide controls & cursor in fullscreen mode Fix flickering pause on start F, M, Space keyboard shortcuts --- src/components/appMediaViewer.ts | 8 ++ src/lib/mediaPlayer.ts | 126 +++++++++++++++++-------------- src/scss/partials/_ckin.scss | 54 +++++++------ 3 files changed, 105 insertions(+), 83 deletions(-) diff --git a/src/components/appMediaViewer.ts b/src/components/appMediaViewer.ts index 2c026d6f..b4235ee3 100644 --- a/src/components/appMediaViewer.ts +++ b/src/components/appMediaViewer.ts @@ -93,6 +93,8 @@ class AppMediaViewerBase void; protected onNextClick: (target: TargetType) => void; protected loadMoreMedia: (older: boolean) => Promise; + + protected videoPlayer: VideoPlayer; constructor(topButtons: Array['buttons']>) { this.log = logger('AMV'); @@ -336,6 +338,11 @@ class AppMediaViewerBase { - cancelEvent(e); + const onMuteClick = (e?: Event) => { + e && cancelEvent(e); video.muted = !video.muted; - }); + }; + + this.listenerSetter.add(volumeSvg, 'click', onMuteClick); const volumeProgress = new RangeSelector(0.01, 1, 0, 1); volumeProgress.setListeners(); @@ -270,7 +277,7 @@ export default class VideoPlayer { }; // не вызовется повторно если на 1 установить 1 - video.addEventListener('volumechange', () => { + this.listenerSetter.add(video, 'volumechange', () => { muted = video.muted; lastVolume = video.volume; setVolume(); @@ -287,46 +294,56 @@ export default class VideoPlayer { leftControls.insertBefore(volumeDiv, timeElapsed.parentElement); Array.from(toggle).forEach((button) => { - return button.addEventListener('click', () => { + this.listenerSetter.add(button, 'click', () => { this.togglePlay(); }); }); - video.addEventListener('click', () => { + this.listenerSetter.add(video, 'click', () => { if(!isTouchSupported) { this.togglePlay(); - return; } }); + let showControlsTimeout = 0; + + const showControls = () => { + if(showControlsTimeout) clearTimeout(showControlsTimeout); + else player.classList.add('show-controls'); + + showControlsTimeout = window.setTimeout(() => { + showControlsTimeout = 0; + player.classList.remove('show-controls'); + }, 3e3); + }; + if(isTouchSupported) { - let showControlsTimeout = 0; - - const t = () => { - showControlsTimeout = window.setTimeout(() => { - showControlsTimeout = 0; - player.classList.remove('show-controls'); - }, 3e3); - }; - - player.addEventListener('click', () => { - if(showControlsTimeout) { - clearTimeout(showControlsTimeout); - } else { - player.classList.add('show-controls'); - } - - t(); + this.listenerSetter.add(player, 'click', () => { + showControls(); }); - player.addEventListener('touchstart', () => { + this.listenerSetter.add(player, 'touchstart', () => { player.classList.add('show-controls'); clearTimeout(showControlsTimeout); }); - player.addEventListener('touchend', () => { + this.listenerSetter.add(player, 'touchend', () => { if(player.classList.contains('is-playing')) { - t(); + showControls(); + } + }); + } else { + this.listenerSetter.add(this.wrapper, 'mousemove', () => { + showControls(); + }); + + this.listenerSetter.add(document, 'keydown', (e: KeyboardEvent) => { + if(e.code === 'KeyF') { + this.toggleFullScreen(fullScreenButton); + } else if(e.code === 'KeyM') { + onMuteClick(); + } else if(e.code === 'Space') { + this.togglePlay(); } }); } @@ -342,32 +359,34 @@ export default class VideoPlayer { /* video.addEventListener('play', () => { }); */ - video.addEventListener('dblclick', () => { - if(isTouchSupported) { - return; + this.listenerSetter.add(video, 'dblclick', () => { + if(!isTouchSupported) { + this.toggleFullScreen(fullScreenButton); } + }); - return this.toggleFullScreen(fullScreenButton); - }) - - fullScreenButton.addEventListener('click', (e) => { - return this.toggleFullScreen(fullScreenButton); + this.listenerSetter.add(fullScreenButton, 'click', (e) => { + this.toggleFullScreen(fullScreenButton); }); 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'.split(' ').forEach(eventName => { - player.addEventListener(eventName, this.onFullScreen, false); + this.listenerSetter.add(player, eventName, this.onFullScreen, false); }); - video.addEventListener('timeupdate', () => { + this.listenerSetter.add(video, 'timeupdate', () => { timeElapsed.innerHTML = String(video.currentTime | 0).toHHMMSS(); }); + + this.listenerSetter.add(video, 'play', () => { + this.wrapper.classList.add('played'); + }, {once: true}); } - video.addEventListener('play', () => { + this.listenerSetter.add(video, 'play', () => { this.wrapper.classList.add('is-playing'); }); - video.addEventListener('pause', () => { + this.listenerSetter.add(video, 'pause', () => { this.wrapper.classList.remove('is-playing'); }); @@ -380,21 +399,8 @@ export default class VideoPlayer { } } - public togglePlay(stop?: boolean) { - //console.log('video togglePlay:', stop, this.video.paused); - - if(stop) { - this.video.pause(); - this.wrapper.classList.remove('is-playing'); - return; - } else if(stop === false) { - this.video.play(); - this.wrapper.classList.add('is-playing'); - return; - } - + public togglePlay() { this.video[this.video.paused ? 'play' : 'pause'](); - //this.wrapper.classList.toggle('is-playing', !this.video.paused); } private buildControls() { @@ -514,7 +520,11 @@ export default class VideoPlayer { const isFullscreenNow = document.webkitFullscreenElement !== null; if(!isFullscreenNow) { this.wrapper.classList.remove('ckin__fullscreen'); - } else { } }; + + public removeListeners() { + this.listenerSetter.removeAll(); + this.progress.removeListeners(); + } } diff --git a/src/scss/partials/_ckin.scss b/src/scss/partials/_ckin.scss index 4d5845cf..68ef0f0d 100644 --- a/src/scss/partials/_ckin.scss +++ b/src/scss/partials/_ckin.scss @@ -64,11 +64,14 @@ font-size: 0; //overflow: hidden; //border-radius: 5px; - cursor: pointer; display: flex; align-items: center; justify-content: center; + &.show-controls video { + cursor: pointer; + } + &:before { content: ''; position: absolute; @@ -123,11 +126,18 @@ left: 50%; transform: translate3d(-50%, -50%, 0) scale(1); font-size: 64px; - transition: all .2s; + transition: visibility .2s, opacity .2s; touch-action: manipulation; } } + &:not(.played) { + .default__button--big { + opacity: 0; + visibility: hidden; + } + } + &__slider { width: 10px; height: 30px; @@ -138,7 +148,7 @@ bottom: 0; right: 0; left: 0; - transition: all .3s; + transition: transform .3s; text-align: left; direction: ltr; z-index: 6; @@ -168,7 +178,7 @@ position: absolute; background-repeat: repeat-x; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==); - transition: all .3s; + transition: transform .3s; pointer-events: none; } @@ -181,17 +191,22 @@ transform: translate3d(0, 50px, 0); } - html.no-touch &:hover, - &.show-controls { - .default__gradient-bottom { - transform: translateZ(0); - } + .default__controls { + transform: translate3d(0, 52px, 0); + } - .default__controls { + /* html.no-touch &:hover,*/ + &.show-controls.ckin__fullscreen, + &.show-controls:not(.ckin__fullscreen):hover { + .default__gradient-bottom, .default__controls { transform: translateZ(0); } } + &:not(.show-controls) { + cursor: none; + } + &:before { opacity: 0; visibility: hidden; @@ -203,11 +218,7 @@ visibility: hidden; } - .default__controls { - transform: translate3d(0, 52px, 0); - } - - .toggle { + .toggle:not(.default__button--big) { &:before { content: $tgico-pause; } @@ -238,6 +249,7 @@ } .progress-line { + --color: #fff; margin: 0; width: 50px; @@ -246,19 +258,10 @@ display: none; } - &__filled { - background: #fff; - } - &__seek { --thumb-size: 15px; - - &::-webkit-slider-thumb { - background: #fff; - } } - } - + } } &.is-buffering { @@ -309,6 +312,7 @@ video::-webkit-media-controls-enclosure { padding: 0; margin: 0; outline: none; + caret-color: var(--color); &:focus { outline: none;