Fix control visibility and stabilize HLS loading
This commit is contained in:
@@ -14,6 +14,9 @@
|
||||
|
||||
.controls-layer.hidden.playing {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.controls-layer.fullscreen.hidden.playing {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,60 +28,116 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
subtitles = [],
|
||||
audioTracks = [],
|
||||
}) => {
|
||||
const { videoState, uiState, togglePlay, toggleFullscreen, showControls, hideControls } = usePlayerContext()
|
||||
const [mouseMoving, setMouseMoving] = useState(false)
|
||||
const { videoState, uiState, togglePlay, toggleFullscreen, showControls, hideControls } =
|
||||
usePlayerContext()
|
||||
const [isPointerOver, setIsPointerOver] = useState(false)
|
||||
const [lastInteraction, setLastInteraction] = useState<number>(0)
|
||||
const hideTimeoutRef = useRef<number>()
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const lastClickTimeRef = useRef<number>(0)
|
||||
const isMenuOpen =
|
||||
uiState.settingsOpen ||
|
||||
uiState.volumeControlOpen ||
|
||||
uiState.qualityMenuOpen ||
|
||||
uiState.subtitleMenuOpen
|
||||
const autoHideEnabled = videoState.fullscreen && videoState.playing && !isMenuOpen
|
||||
|
||||
// Auto-hide controls after inactivity
|
||||
const clearHideTimeout = useCallback(() => {
|
||||
if (hideTimeoutRef.current) {
|
||||
window.clearTimeout(hideTimeoutRef.current)
|
||||
hideTimeoutRef.current = undefined
|
||||
}
|
||||
}, [])
|
||||
|
||||
const scheduleHide = useCallback(() => {
|
||||
if (!autoHideEnabled) {
|
||||
clearHideTimeout()
|
||||
return
|
||||
}
|
||||
|
||||
clearHideTimeout()
|
||||
hideTimeoutRef.current = window.setTimeout(() => {
|
||||
hideControls()
|
||||
}, 3000)
|
||||
}, [autoHideEnabled, clearHideTimeout, hideControls])
|
||||
|
||||
// Keep controls visible when not playing or when any menu is open
|
||||
useEffect(() => {
|
||||
if (videoState.playing) {
|
||||
// Show controls on mouse movement
|
||||
if (mouseMoving) {
|
||||
if (!videoState.playing || isMenuOpen) {
|
||||
clearHideTimeout()
|
||||
showControls()
|
||||
}
|
||||
}, [videoState.playing, isMenuOpen, showControls, clearHideTimeout])
|
||||
|
||||
// Manage controls visibility when leaving fullscreen
|
||||
useEffect(() => {
|
||||
if (!videoState.fullscreen) {
|
||||
clearHideTimeout()
|
||||
if (!videoState.playing || isPointerOver) {
|
||||
showControls()
|
||||
}
|
||||
}
|
||||
}, [videoState.fullscreen, videoState.playing, isPointerOver, showControls, clearHideTimeout])
|
||||
|
||||
// Clear existing timeout
|
||||
if (hideTimeoutRef.current) {
|
||||
window.clearTimeout(hideTimeoutRef.current)
|
||||
}
|
||||
|
||||
// Hide controls after inactivity (3 seconds in all modes)
|
||||
if (mouseMoving) {
|
||||
const hideDelay = 3000
|
||||
hideTimeoutRef.current = window.setTimeout(() => {
|
||||
hideControls()
|
||||
setMouseMoving(false)
|
||||
}, hideDelay)
|
||||
}
|
||||
} else {
|
||||
// Always show controls when paused
|
||||
showControls()
|
||||
// Re-schedule auto hide when interaction changes
|
||||
useEffect(() => {
|
||||
if (autoHideEnabled && lastInteraction > 0) {
|
||||
scheduleHide()
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (hideTimeoutRef.current) {
|
||||
window.clearTimeout(hideTimeoutRef.current)
|
||||
if (autoHideEnabled) {
|
||||
clearHideTimeout()
|
||||
}
|
||||
}
|
||||
}, [mouseMoving, videoState.playing, videoState.fullscreen, showControls, hideControls])
|
||||
}, [autoHideEnabled, lastInteraction, scheduleHide, clearHideTimeout])
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
setIsPointerOver(true)
|
||||
showControls()
|
||||
if (autoHideEnabled) {
|
||||
setLastInteraction(Date.now())
|
||||
}
|
||||
}, [autoHideEnabled, showControls])
|
||||
|
||||
// Handle mouse movement
|
||||
const handleMouseMove = useCallback(() => {
|
||||
if (!mouseMoving) {
|
||||
setMouseMoving(true)
|
||||
setIsPointerOver(true)
|
||||
showControls()
|
||||
if (autoHideEnabled) {
|
||||
setLastInteraction(Date.now())
|
||||
}
|
||||
}, [mouseMoving])
|
||||
}, [autoHideEnabled, showControls])
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
// Only hide controls on mouse leave when in fullscreen mode
|
||||
// When player is small, controls should stay visible
|
||||
setMouseMoving(false)
|
||||
if (videoState.playing && videoState.fullscreen) {
|
||||
setIsPointerOver(false)
|
||||
clearHideTimeout()
|
||||
if (videoState.fullscreen) {
|
||||
if (videoState.playing) {
|
||||
hideControls()
|
||||
} else {
|
||||
showControls()
|
||||
}
|
||||
} else if (videoState.playing) {
|
||||
hideControls()
|
||||
}
|
||||
}, [videoState.playing, videoState.fullscreen, hideControls])
|
||||
}, [clearHideTimeout, videoState.fullscreen, videoState.playing, hideControls, showControls])
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
clearHideTimeout()
|
||||
}
|
||||
}, [clearHideTimeout])
|
||||
|
||||
const previousAutoHide = useRef(autoHideEnabled)
|
||||
useEffect(() => {
|
||||
if (autoHideEnabled && !previousAutoHide.current) {
|
||||
showControls()
|
||||
setLastInteraction(Date.now())
|
||||
}
|
||||
|
||||
previousAutoHide.current = autoHideEnabled
|
||||
}, [autoHideEnabled, showControls])
|
||||
|
||||
// Keyboard shortcuts
|
||||
useKeyboardShortcuts(keyboardShortcuts)
|
||||
@@ -132,12 +188,13 @@ export const ControlsLayer: React.FC<ControlsLayerProps> = ({
|
||||
|
||||
const controlsClassName = `controls-layer ${uiState.controlsVisible ? 'visible' : 'hidden'} ${
|
||||
videoState.playing ? 'playing' : 'paused'
|
||||
}`
|
||||
} ${videoState.fullscreen ? 'fullscreen' : 'windowed'}`
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={controlsClassName}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect, useState, useCallback } from 'react'
|
||||
import { PlayerProvider } from '../contexts/PlayerContext'
|
||||
import { VideoElement } from './VideoElement'
|
||||
import { ControlsLayer } from './ControlsLayer'
|
||||
|
||||
Reference in New Issue
Block a user