|
|
@@ -18,7 +18,9 @@ const CustomVideoControls = {
|
|
|
fullscreenBtn: null,
|
|
|
|
|
|
controlsTimeout: null,
|
|
|
+ mouseTimeout: null,
|
|
|
container: null,
|
|
|
+ isMouseOverContainer: false,
|
|
|
|
|
|
/**
|
|
|
* Initialize the custom video controls
|
|
|
@@ -59,6 +61,12 @@ const CustomVideoControls = {
|
|
|
|
|
|
// Set up mouse movement detection for auto-hiding controls
|
|
|
this.setupAutoHide();
|
|
|
+
|
|
|
+ // Set up mouse inactivity detection
|
|
|
+ this.setupMouseInactivityDetection();
|
|
|
+
|
|
|
+ // Set up focus handling for top controls
|
|
|
+ this.setupTopControlsFocusHandling();
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
@@ -78,22 +86,44 @@ const CustomVideoControls = {
|
|
|
*/
|
|
|
setupEventListeners() {
|
|
|
// Play/Pause button
|
|
|
- this.playPauseBtn.addEventListener('click', () => this.togglePlayPause());
|
|
|
+ this.playPauseBtn.addEventListener('click', () => {
|
|
|
+ this.togglePlayPause();
|
|
|
+ this.playPauseBtn.blur();
|
|
|
+ });
|
|
|
|
|
|
// Seek slider
|
|
|
- this.seekSlider.addEventListener('input', () => this.seek());
|
|
|
+ this.seekSlider.addEventListener('input', () => {
|
|
|
+ this.seek();
|
|
|
+ });
|
|
|
+ this.seekSlider.addEventListener('change', () => {
|
|
|
+ this.seekSlider.blur();
|
|
|
+ });
|
|
|
|
|
|
// Progress bar click to seek
|
|
|
- this.progressBar.addEventListener('click', (e) => this.progressBarSeek(e));
|
|
|
+ this.progressBar.addEventListener('click', (e) => {
|
|
|
+ this.progressBarSeek(e);
|
|
|
+ e.target.blur();
|
|
|
+ });
|
|
|
|
|
|
// Mute button
|
|
|
- this.muteBtn.addEventListener('click', () => this.toggleMute());
|
|
|
+ this.muteBtn.addEventListener('click', () => {
|
|
|
+ this.toggleMute();
|
|
|
+ this.muteBtn.blur();
|
|
|
+ });
|
|
|
|
|
|
// Volume slider
|
|
|
- this.volumeSlider.addEventListener('input', () => this.updateVolume());
|
|
|
+ this.volumeSlider.addEventListener('input', () => {
|
|
|
+ this.updateVolume();
|
|
|
+ });
|
|
|
+ this.volumeSlider.addEventListener('change', () => {
|
|
|
+ this.volumeSlider.blur();
|
|
|
+ });
|
|
|
|
|
|
// Fullscreen button
|
|
|
- this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
|
|
|
+ this.fullscreenBtn.addEventListener('click', () => {
|
|
|
+ this.toggleFullscreen();
|
|
|
+ this.fullscreenBtn.blur();
|
|
|
+ });
|
|
|
|
|
|
// Video click to toggle play/pause
|
|
|
this.video.addEventListener('click', (e) => {
|
|
|
@@ -110,16 +140,20 @@ const CustomVideoControls = {
|
|
|
this.video.addEventListener('play', () => {
|
|
|
this.updatePlayPauseButton();
|
|
|
this.hideControlsWithTimeout(); // Auto-hide controls when playing
|
|
|
+ this.hideCursorWithTimeout(); // Auto-hide cursor when playing
|
|
|
});
|
|
|
this.video.addEventListener('pause', () => {
|
|
|
this.updatePlayPauseButton();
|
|
|
this.showControls(); // Show controls when paused
|
|
|
+ this.showCursor(); // Show cursor when paused
|
|
|
+ this.resetMouseTimeout(); // Clear cursor timeout when paused
|
|
|
});
|
|
|
this.video.addEventListener('seeking', () => {
|
|
|
this.showControls(); // Show controls when seeking
|
|
|
});
|
|
|
this.video.addEventListener('seeked', () => {
|
|
|
this.hideControlsWithTimeout(); // Auto-hide after seeking
|
|
|
+ this.hideCursorWithTimeout(); // Auto-hide cursor after seeking
|
|
|
});
|
|
|
|
|
|
// Keyboard controls
|
|
|
@@ -134,9 +168,9 @@ const CustomVideoControls = {
|
|
|
|
|
|
// Add mouse movement detection for the entire container
|
|
|
if (this.container) {
|
|
|
- this.container.addEventListener('mousemove', () => this.showControls());
|
|
|
- this.container.addEventListener('mouseleave', () => this.hideControls());
|
|
|
- this.container.addEventListener('mouseenter', () => this.showControls());
|
|
|
+ this.container.addEventListener('mousemove', () => this.handleMouseMove());
|
|
|
+ this.container.addEventListener('mouseleave', () => this.handleMouseLeave());
|
|
|
+ this.container.addEventListener('mouseenter', () => this.handleMouseEnter());
|
|
|
}
|
|
|
|
|
|
// Touch events for mobile support
|
|
|
@@ -352,9 +386,10 @@ const CustomVideoControls = {
|
|
|
// Always show controls when entering/exiting fullscreen
|
|
|
this.showControls();
|
|
|
|
|
|
- // Auto-hide controls after a delay in fullscreen mode if video is playing
|
|
|
+ // Auto-hide controls and cursor after a delay in fullscreen mode if video is playing
|
|
|
if (isFullscreen && !this.video.paused) {
|
|
|
this.hideControlsWithTimeout();
|
|
|
+ this.hideCursorWithTimeout();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -412,10 +447,14 @@ const CustomVideoControls = {
|
|
|
if (this.topControls) {
|
|
|
this.topControls.classList.add('show');
|
|
|
}
|
|
|
+ this.showCursor();
|
|
|
+
|
|
|
// Only reset timeout if video is playing (to allow auto-hide when idle)
|
|
|
if (!this.video.paused) {
|
|
|
this.resetControlsTimeout();
|
|
|
this.hideControlsWithTimeout();
|
|
|
+ this.resetMouseTimeout();
|
|
|
+ this.hideCursorWithTimeout();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
@@ -428,9 +467,75 @@ const CustomVideoControls = {
|
|
|
if (this.topControls) {
|
|
|
this.topControls.classList.remove('show');
|
|
|
}
|
|
|
+ this.hideCursor();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Show the cursor
|
|
|
+ */
|
|
|
+ showCursor() {
|
|
|
+ if (this.container) {
|
|
|
+ this.container.style.cursor = 'auto';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Hide the cursor
|
|
|
+ */
|
|
|
+ hideCursor() {
|
|
|
+ if (this.container && !this.video.paused) {
|
|
|
+ this.container.style.cursor = 'none';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Hide cursor with a timeout
|
|
|
+ */
|
|
|
+ hideCursorWithTimeout() {
|
|
|
+ this.resetMouseTimeout();
|
|
|
+ this.mouseTimeout = setTimeout(() => {
|
|
|
+ this.hideCursor();
|
|
|
+ }, 3000); // 3 seconds
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Reset the mouse timeout
|
|
|
+ */
|
|
|
+ resetMouseTimeout() {
|
|
|
+ if (this.mouseTimeout) {
|
|
|
+ clearTimeout(this.mouseTimeout);
|
|
|
+ this.mouseTimeout = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handle mouse movement inside the container
|
|
|
+ */
|
|
|
+ handleMouseMove() {
|
|
|
+ this.showControls();
|
|
|
+ if (!this.video.paused) {
|
|
|
+ this.resetMouseTimeout();
|
|
|
+ this.hideCursorWithTimeout();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Handle mouse entering the container
|
|
|
+ */
|
|
|
+ handleMouseEnter() {
|
|
|
+ this.isMouseOverContainer = true;
|
|
|
+ this.showControls();
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Handle mouse leaving the container
|
|
|
+ */
|
|
|
+ handleMouseLeave() {
|
|
|
+ this.isMouseOverContainer = false;
|
|
|
+ this.hideControls();
|
|
|
+ },
|
|
|
+
|
|
|
/**
|
|
|
* Hide controls with a timeout
|
|
|
*/
|
|
|
@@ -459,6 +564,19 @@ const CustomVideoControls = {
|
|
|
// so we don't need to add them again here to prevent duplicate execution
|
|
|
},
|
|
|
|
|
|
+ /**
|
|
|
+ * Set up mouse inactivity detection
|
|
|
+ */
|
|
|
+ setupMouseInactivityDetection() {
|
|
|
+ // Initial state - show cursor
|
|
|
+ this.showCursor();
|
|
|
+
|
|
|
+ // Set up initial timeout if video is playing
|
|
|
+ if (!this.video.paused) {
|
|
|
+ this.hideCursorWithTimeout();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
/**
|
|
|
* Handle window resize events
|
|
|
*/
|
|
|
@@ -471,9 +589,46 @@ const CustomVideoControls = {
|
|
|
this.resizeTimeout = setTimeout(() => {
|
|
|
if (!this.video.paused) {
|
|
|
this.hideControlsWithTimeout();
|
|
|
+ this.hideCursorWithTimeout();
|
|
|
}
|
|
|
}, 1000);
|
|
|
},
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set up focus handling for top controls
|
|
|
+ */
|
|
|
+ setupTopControlsFocusHandling() {
|
|
|
+ // Get top control buttons and select
|
|
|
+ const infoBtn = document.querySelector('button[onclick="showInfo()"]');
|
|
|
+ const shareBtn = document.querySelector('button[onclick="shareVideo()"]');
|
|
|
+ const downloadBtn = document.querySelector('button[onclick="downloadVideo()"]');
|
|
|
+ const qualitySelector = document.getElementById('quality-selector');
|
|
|
+
|
|
|
+ // Add blur() calls to top control buttons
|
|
|
+ if (infoBtn) {
|
|
|
+ infoBtn.addEventListener('click', () => {
|
|
|
+ infoBtn.blur();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (shareBtn) {
|
|
|
+ shareBtn.addEventListener('click', () => {
|
|
|
+ shareBtn.blur();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (downloadBtn) {
|
|
|
+ downloadBtn.addEventListener('click', () => {
|
|
|
+ downloadBtn.blur();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (qualitySelector) {
|
|
|
+ qualitySelector.addEventListener('change', () => {
|
|
|
+ qualitySelector.blur();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
// @license-end
|