152 lines
4.7 KiB
JavaScript
152 lines
4.7 KiB
JavaScript
/**
|
|
* Mobile-specific player features:
|
|
* - Double-tap left/right to skip ±10s
|
|
* - Object-fit toggle button for widescreen fill
|
|
*/
|
|
|
|
export function isMobile() {
|
|
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
}
|
|
|
|
export function initMobileWidescreen(playerWrapper, video) {
|
|
if (!isMobile()) {
|
|
return;
|
|
}
|
|
|
|
const controls = playerWrapper.querySelector('.hstream-player__controls');
|
|
if (!controls) {
|
|
return;
|
|
}
|
|
|
|
const btn = document.createElement('button');
|
|
btn.className = 'hstream-player__button hstream-player__mobile-fill-btn';
|
|
btn.type = 'button';
|
|
btn.setAttribute('aria-label', 'Toggle screen fill');
|
|
btn.innerHTML = '<i class="fa-solid fa-arrows-left-right-to-line"></i>';
|
|
btn.title = 'Fill Screen';
|
|
|
|
const fullscreenBtn = controls.querySelector('[data-action="fullscreen"]');
|
|
if (fullscreenBtn) {
|
|
fullscreenBtn.insertAdjacentElement('beforebegin', btn);
|
|
} else {
|
|
controls.appendChild(btn);
|
|
}
|
|
|
|
let fillEnabled = true;
|
|
video.style.objectFit = 'cover';
|
|
|
|
btn.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
if (fillEnabled) {
|
|
video.style.objectFit = 'contain';
|
|
fillEnabled = false;
|
|
btn.classList.remove('hstream-player__button--active');
|
|
} else {
|
|
video.style.objectFit = 'cover';
|
|
fillEnabled = true;
|
|
btn.classList.add('hstream-player__button--active');
|
|
}
|
|
});
|
|
|
|
btn.classList.add('hstream-player__button--active');
|
|
}
|
|
|
|
export function initMobileDoubleTap(playerWrapper, video, player) {
|
|
if (!isMobile()) {
|
|
return;
|
|
}
|
|
|
|
const skipOverlay = playerWrapper.querySelector('.hstream-player__skip-overlay');
|
|
if (!skipOverlay) {
|
|
return;
|
|
}
|
|
|
|
class MultiClickCounter {
|
|
constructor() {
|
|
this.timers = [];
|
|
this.count = 0;
|
|
this.reseted = 0;
|
|
this.lastSide = null;
|
|
}
|
|
|
|
clicked() {
|
|
this.count += 1;
|
|
const xcount = this.count;
|
|
this.timers.push(setTimeout(() => this.reset(xcount), 500));
|
|
return this.count;
|
|
}
|
|
|
|
resetCount(n) {
|
|
this.reseted = this.count;
|
|
this.count = n;
|
|
this.timers.forEach(t => clearTimeout(t));
|
|
this.timers = [];
|
|
}
|
|
|
|
reset(xcount) {
|
|
if (this.count > xcount) return;
|
|
this.count = 0;
|
|
this.lastSide = null;
|
|
this.reseted = 0;
|
|
skipOverlay.classList.remove('hstream-player__skip-overlay--visible');
|
|
this.timers = [];
|
|
}
|
|
}
|
|
|
|
const counter = new MultiClickCounter();
|
|
|
|
const handleTap = (e) => {
|
|
e.preventDefault();
|
|
|
|
const count = counter.clicked();
|
|
if (count < 2) return;
|
|
|
|
const rect = e.target.getBoundingClientRect();
|
|
const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
|
|
const perc = (x / rect.width) * 100;
|
|
|
|
let shouldReset = true;
|
|
const lastSide = counter.lastSide;
|
|
|
|
if (lastSide === null) {
|
|
shouldReset = false;
|
|
}
|
|
|
|
if (perc < 40) {
|
|
if (player.currentTime === 0) return;
|
|
counter.lastSide = 'L';
|
|
if (shouldReset && lastSide !== 'L') {
|
|
counter.resetCount(1);
|
|
return;
|
|
}
|
|
const skipSeconds = (count - 1) * 10;
|
|
player.currentTime = Math.max(0, player.currentTime - skipSeconds);
|
|
skipOverlay.innerHTML = '<i class="fa-solid fa-backward"></i>' + skipSeconds + 's';
|
|
skipOverlay.classList.add('hstream-player__skip-overlay--visible');
|
|
setTimeout(() => skipOverlay.classList.remove('hstream-player__skip-overlay--visible'), 800);
|
|
} else if (perc > 60) {
|
|
if (player.currentTime >= player.duration) return;
|
|
counter.lastSide = 'R';
|
|
if (shouldReset && lastSide !== 'R') {
|
|
counter.resetCount(1);
|
|
return;
|
|
}
|
|
const skipSeconds = (count - 1) * 10;
|
|
player.currentTime = Math.min(player.duration, player.currentTime + skipSeconds);
|
|
skipOverlay.innerHTML = '<i class="fa-solid fa-forward"></i>' + skipSeconds + 's';
|
|
skipOverlay.classList.add('hstream-player__skip-overlay--visible');
|
|
setTimeout(() => skipOverlay.classList.remove('hstream-player__skip-overlay--visible'), 800);
|
|
} else {
|
|
player.togglePlay();
|
|
counter.lastSide = 'C';
|
|
}
|
|
};
|
|
|
|
playerWrapper.addEventListener('click', handleTap);
|
|
|
|
video.addEventListener('dblclick', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
});
|
|
}
|