Video player fixes:
Hide controls & cursor in fullscreen mode Fix flickering pause on start F, M, Space keyboard shortcuts
This commit is contained in:
parent
8de8aa9692
commit
2c86501ec5
@ -93,6 +93,8 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
protected onPrevClick: (target: TargetType) => void;
|
protected onPrevClick: (target: TargetType) => void;
|
||||||
protected onNextClick: (target: TargetType) => void;
|
protected onNextClick: (target: TargetType) => void;
|
||||||
protected loadMoreMedia: (older: boolean) => Promise<void>;
|
protected loadMoreMedia: (older: boolean) => Promise<void>;
|
||||||
|
|
||||||
|
protected videoPlayer: VideoPlayer;
|
||||||
|
|
||||||
constructor(topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>) {
|
constructor(topButtons: Array<keyof AppMediaViewerBase<ContentAdditionType, ButtonsAdditionType, TargetType>['buttons']>) {
|
||||||
this.log = logger('AMV');
|
this.log = logger('AMV');
|
||||||
@ -336,6 +338,11 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
|
protected async setMoverToTarget(target: HTMLElement, closing = false, fromRight = 0) {
|
||||||
|
if(this.videoPlayer) { // there could be a better place for it
|
||||||
|
this.videoPlayer.removeListeners();
|
||||||
|
this.videoPlayer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
const mover = this.content.mover;
|
const mover = this.content.mover;
|
||||||
|
|
||||||
if(!closing) {
|
if(!closing) {
|
||||||
@ -1055,6 +1062,7 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
}
|
}
|
||||||
|
|
||||||
const player = new VideoPlayer(video, true, media.supportsStreaming);
|
const player = new VideoPlayer(video, true, media.supportsStreaming);
|
||||||
|
this.videoPlayer = player;
|
||||||
/* div.append(video);
|
/* div.append(video);
|
||||||
mover.append(player.wrapper); */
|
mover.append(player.wrapper); */
|
||||||
});
|
});
|
||||||
|
@ -10,6 +10,7 @@ import { isTouchSupported } from "../helpers/touchSupport";
|
|||||||
import RangeSelector from "../components/rangeSelector";
|
import RangeSelector from "../components/rangeSelector";
|
||||||
import { onVideoLoad } from "../helpers/files";
|
import { onVideoLoad } from "../helpers/files";
|
||||||
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
import { cancelEvent } from "../helpers/dom/cancelEvent";
|
||||||
|
import ListenerSetter from "../helpers/listenerSetter";
|
||||||
|
|
||||||
export class MediaProgressLine extends RangeSelector {
|
export class MediaProgressLine extends RangeSelector {
|
||||||
private filledLoad: HTMLDivElement;
|
private filledLoad: HTMLDivElement;
|
||||||
@ -165,17 +166,21 @@ export class MediaProgressLine extends RangeSelector {
|
|||||||
|
|
||||||
let lastVolume = 1, muted = !lastVolume;
|
let lastVolume = 1, muted = !lastVolume;
|
||||||
export default class VideoPlayer {
|
export default class VideoPlayer {
|
||||||
public wrapper: HTMLDivElement;
|
private wrapper: HTMLDivElement;
|
||||||
public progress: MediaProgressLine;
|
private progress: MediaProgressLine;
|
||||||
private skin: string;
|
private skin: string;
|
||||||
|
|
||||||
|
private listenerSetter: ListenerSetter;
|
||||||
|
|
||||||
/* private videoParent: HTMLElement;
|
/* private videoParent: HTMLElement;
|
||||||
private videoWhichChild: number; */
|
private videoWhichChild: number; */
|
||||||
|
|
||||||
constructor(public video: HTMLVideoElement, play = false, streamable = false, duration?: number) {
|
constructor(private video: HTMLVideoElement, play = false, streamable = false, duration?: number) {
|
||||||
this.wrapper = document.createElement('div');
|
this.wrapper = document.createElement('div');
|
||||||
this.wrapper.classList.add('ckin__player');
|
this.wrapper.classList.add('ckin__player');
|
||||||
|
|
||||||
|
this.listenerSetter = new ListenerSetter();
|
||||||
|
|
||||||
video.parentNode.insertBefore(this.wrapper, video);
|
video.parentNode.insertBefore(this.wrapper, video);
|
||||||
this.wrapper.appendChild(video);
|
this.wrapper.appendChild(video);
|
||||||
|
|
||||||
@ -184,7 +189,7 @@ export default class VideoPlayer {
|
|||||||
this.stylePlayer(duration);
|
this.stylePlayer(duration);
|
||||||
|
|
||||||
if(this.skin === 'default') {
|
if(this.skin === 'default') {
|
||||||
let controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement;
|
const controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement;
|
||||||
this.progress = new MediaProgressLine(video, streamable);
|
this.progress = new MediaProgressLine(video, streamable);
|
||||||
controls.prepend(this.progress.container);
|
controls.prepend(this.progress.container);
|
||||||
}
|
}
|
||||||
@ -228,10 +233,12 @@ export default class VideoPlayer {
|
|||||||
`;
|
`;
|
||||||
const volumeSvg = volumeDiv.firstElementChild as SVGSVGElement;
|
const volumeSvg = volumeDiv.firstElementChild as SVGSVGElement;
|
||||||
|
|
||||||
volumeSvg.addEventListener('click', (e) => {
|
const onMuteClick = (e?: Event) => {
|
||||||
cancelEvent(e);
|
e && cancelEvent(e);
|
||||||
video.muted = !video.muted;
|
video.muted = !video.muted;
|
||||||
});
|
};
|
||||||
|
|
||||||
|
this.listenerSetter.add(volumeSvg, 'click', onMuteClick);
|
||||||
|
|
||||||
const volumeProgress = new RangeSelector(0.01, 1, 0, 1);
|
const volumeProgress = new RangeSelector(0.01, 1, 0, 1);
|
||||||
volumeProgress.setListeners();
|
volumeProgress.setListeners();
|
||||||
@ -270,7 +277,7 @@ export default class VideoPlayer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// не вызовется повторно если на 1 установить 1
|
// не вызовется повторно если на 1 установить 1
|
||||||
video.addEventListener('volumechange', () => {
|
this.listenerSetter.add(video, 'volumechange', () => {
|
||||||
muted = video.muted;
|
muted = video.muted;
|
||||||
lastVolume = video.volume;
|
lastVolume = video.volume;
|
||||||
setVolume();
|
setVolume();
|
||||||
@ -287,46 +294,56 @@ export default class VideoPlayer {
|
|||||||
leftControls.insertBefore(volumeDiv, timeElapsed.parentElement);
|
leftControls.insertBefore(volumeDiv, timeElapsed.parentElement);
|
||||||
|
|
||||||
Array.from(toggle).forEach((button) => {
|
Array.from(toggle).forEach((button) => {
|
||||||
return button.addEventListener('click', () => {
|
this.listenerSetter.add(button, 'click', () => {
|
||||||
this.togglePlay();
|
this.togglePlay();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
video.addEventListener('click', () => {
|
this.listenerSetter.add(video, 'click', () => {
|
||||||
if(!isTouchSupported) {
|
if(!isTouchSupported) {
|
||||||
this.togglePlay();
|
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) {
|
if(isTouchSupported) {
|
||||||
let showControlsTimeout = 0;
|
this.listenerSetter.add(player, 'click', () => {
|
||||||
|
showControls();
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
player.addEventListener('touchstart', () => {
|
this.listenerSetter.add(player, 'touchstart', () => {
|
||||||
player.classList.add('show-controls');
|
player.classList.add('show-controls');
|
||||||
clearTimeout(showControlsTimeout);
|
clearTimeout(showControlsTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
player.addEventListener('touchend', () => {
|
this.listenerSetter.add(player, 'touchend', () => {
|
||||||
if(player.classList.contains('is-playing')) {
|
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('play', () => {
|
||||||
}); */
|
}); */
|
||||||
|
|
||||||
video.addEventListener('dblclick', () => {
|
this.listenerSetter.add(video, 'dblclick', () => {
|
||||||
if(isTouchSupported) {
|
if(!isTouchSupported) {
|
||||||
return;
|
this.toggleFullScreen(fullScreenButton);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return this.toggleFullScreen(fullScreenButton);
|
this.listenerSetter.add(fullScreenButton, 'click', (e) => {
|
||||||
})
|
this.toggleFullScreen(fullScreenButton);
|
||||||
|
|
||||||
fullScreenButton.addEventListener('click', (e) => {
|
|
||||||
return this.toggleFullScreen(fullScreenButton);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'.split(' ').forEach(eventName => {
|
'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();
|
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');
|
this.wrapper.classList.add('is-playing');
|
||||||
});
|
});
|
||||||
|
|
||||||
video.addEventListener('pause', () => {
|
this.listenerSetter.add(video, 'pause', () => {
|
||||||
this.wrapper.classList.remove('is-playing');
|
this.wrapper.classList.remove('is-playing');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -380,21 +399,8 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public togglePlay(stop?: boolean) {
|
public togglePlay() {
|
||||||
//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;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.video[this.video.paused ? 'play' : 'pause']();
|
this.video[this.video.paused ? 'play' : 'pause']();
|
||||||
//this.wrapper.classList.toggle('is-playing', !this.video.paused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildControls() {
|
private buildControls() {
|
||||||
@ -514,7 +520,11 @@ export default class VideoPlayer {
|
|||||||
const isFullscreenNow = document.webkitFullscreenElement !== null;
|
const isFullscreenNow = document.webkitFullscreenElement !== null;
|
||||||
if(!isFullscreenNow) {
|
if(!isFullscreenNow) {
|
||||||
this.wrapper.classList.remove('ckin__fullscreen');
|
this.wrapper.classList.remove('ckin__fullscreen');
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public removeListeners() {
|
||||||
|
this.listenerSetter.removeAll();
|
||||||
|
this.progress.removeListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,14 @@
|
|||||||
font-size: 0;
|
font-size: 0;
|
||||||
//overflow: hidden;
|
//overflow: hidden;
|
||||||
//border-radius: 5px;
|
//border-radius: 5px;
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
|
&.show-controls video {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -123,11 +126,18 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate3d(-50%, -50%, 0) scale(1);
|
transform: translate3d(-50%, -50%, 0) scale(1);
|
||||||
font-size: 64px;
|
font-size: 64px;
|
||||||
transition: all .2s;
|
transition: visibility .2s, opacity .2s;
|
||||||
touch-action: manipulation;
|
touch-action: manipulation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.played) {
|
||||||
|
.default__button--big {
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__slider {
|
&__slider {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@ -138,7 +148,7 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
transition: all .3s;
|
transition: transform .3s;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
@ -168,7 +178,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
|
||||||
transition: all .3s;
|
transition: transform .3s;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,17 +191,22 @@
|
|||||||
transform: translate3d(0, 50px, 0);
|
transform: translate3d(0, 50px, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
html.no-touch &:hover,
|
.default__controls {
|
||||||
&.show-controls {
|
transform: translate3d(0, 52px, 0);
|
||||||
.default__gradient-bottom {
|
}
|
||||||
transform: translateZ(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);
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.show-controls) {
|
||||||
|
cursor: none;
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
@ -203,11 +218,7 @@
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.default__controls {
|
.toggle:not(.default__button--big) {
|
||||||
transform: translate3d(0, 52px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle {
|
|
||||||
&:before {
|
&:before {
|
||||||
content: $tgico-pause;
|
content: $tgico-pause;
|
||||||
}
|
}
|
||||||
@ -238,6 +249,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.progress-line {
|
.progress-line {
|
||||||
|
--color: #fff;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
|
||||||
@ -246,19 +258,10 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__filled {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__seek {
|
&__seek {
|
||||||
--thumb-size: 15px;
|
--thumb-size: 15px;
|
||||||
|
|
||||||
&::-webkit-slider-thumb {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-buffering {
|
&.is-buffering {
|
||||||
@ -309,6 +312,7 @@ video::-webkit-media-controls-enclosure {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
caret-color: var(--color);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user