Refactor player controls and progress bar layout

Moved native controls and progress bar positioning logic to PlayerConfig for better configurability. Separated control bar styling from progress bar layout, allowing independent application of styles. Added a spacer to the control bar to improve button alignment when the progress bar is repositioned. Cleaned up legacy code and improved maintainability.
This commit is contained in:
Yiannis Christodoulou 2025-10-09 15:42:48 +03:00
parent 9ada2cb8c9
commit 9c4aeb3134
3 changed files with 146 additions and 110 deletions

View File

@ -78,7 +78,7 @@ class AutoplayToggleButton extends Button {
// Add appropriate icon class based on state // Add appropriate icon class based on state
if (this.isAutoplayEnabled) { if (this.isAutoplayEnabled) {
// this.iconSpan.classList.add('vjs-icon-spinner'); // this.iconSpan.classList.add('vjs-icon-spinner');
this.iconSpan.innerHTML = ` this.iconSpan.innerHTML = `12321321
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 300 300"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 300 300">
<path d="M0 0 C5.28 0.66 10.56 1.32 16 2 C11.67407494 30.83950041 -0.70166324 55.71110206 -24 74 C-47.86506837 91.08769673 -76.02581328 98.52206834 -105.125 93.8125 C-135.12151624 88.48114449 -157.27092449 72.37747882 -175 48 C-175.33 57.57 -175.66 67.14 -176 77 C-181.28 77 -186.56 77 -192 77 C-192 56.54 -192 36.08 -192 15 C-171.54 15 -151.08 15 -130 15 C-130 20.28 -130 25.56 -130 31 C-147.325 31.495 -147.325 31.495 -165 32 C-159.82225386 40.13645822 -155.56278318 46.32892007 -149 53 C-148.23945313 53.7734375 -147.47890625 54.546875 -146.6953125 55.34375 C-129.22175893 72.07252916 -106.1048424 78.80708624 -82.37109375 78.31640625 C-58.0970353 77.28060908 -37.04807338 65.00089922 -20.75390625 47.6015625 C-9.130597 33.96173371 -3.40740768 17.34680275 0 0 Z " fill="#FFFFFF " transform="translate(216,137)"/> <path d="M0 0 C5.28 0.66 10.56 1.32 16 2 C11.67407494 30.83950041 -0.70166324 55.71110206 -24 74 C-47.86506837 91.08769673 -76.02581328 98.52206834 -105.125 93.8125 C-135.12151624 88.48114449 -157.27092449 72.37747882 -175 48 C-175.33 57.57 -175.66 67.14 -176 77 C-181.28 77 -186.56 77 -192 77 C-192 56.54 -192 36.08 -192 15 C-171.54 15 -151.08 15 -130 15 C-130 20.28 -130 25.56 -130 31 C-147.325 31.495 -147.325 31.495 -165 32 C-159.82225386 40.13645822 -155.56278318 46.32892007 -149 53 C-148.23945313 53.7734375 -147.47890625 54.546875 -146.6953125 55.34375 C-129.22175893 72.07252916 -106.1048424 78.80708624 -82.37109375 78.31640625 C-58.0970353 77.28060908 -37.04807338 65.00089922 -20.75390625 47.6015625 C-9.130597 33.96173371 -3.40740768 17.34680275 0 0 Z " fill="#FFFFFF " transform="translate(216,137)"/>
<path d="M0 0 C4.65174076 0.93034815 8.20079246 2.396823 12.3605957 4.51000977 C13.08309006 4.8710379 13.80558441 5.23206604 14.54997253 5.60403442 C16.92813231 6.79415607 19.30193243 7.99271217 21.67578125 9.19140625 C23.32747078 10.02004975 24.97942673 10.84816241 26.63163757 11.67576599 C30.97273819 13.85203468 35.31018622 16.03548755 39.64691162 18.22045898 C44.07557427 20.45015317 48.5076553 22.67303021 52.93945312 24.89648438 C61.62966021 29.25765972 70.31602362 33.62643276 79 38 C79 38.66 79 39.32 79 40 C69.14617359 44.96162844 59.28913947 49.9168183 49.42792797 54.86375427 C44.84935432 57.16087773 40.27192652 59.46022616 35.69702148 61.76464844 C31.28411887 63.98736649 26.86833299 66.20425375 22.45046425 68.41708374 C20.76327244 69.26345678 19.07714036 70.11194566 17.39208031 70.96255493 C15.03651482 72.15118441 12.67733497 73.33231761 10.31713867 74.51171875 C9.61726837 74.86704681 8.91739807 75.22237488 8.19631958 75.58847046 C5.2698443 77.04233211 3.31399908 78 0 78 C0 52.26 0 26.52 0 0 Z " fill="#FFFFFF" transform="translate(101,89)"/> <path d="M0 0 C4.65174076 0.93034815 8.20079246 2.396823 12.3605957 4.51000977 C13.08309006 4.8710379 13.80558441 5.23206604 14.54997253 5.60403442 C16.92813231 6.79415607 19.30193243 7.99271217 21.67578125 9.19140625 C23.32747078 10.02004975 24.97942673 10.84816241 26.63163757 11.67576599 C30.97273819 13.85203468 35.31018622 16.03548755 39.64691162 18.22045898 C44.07557427 20.45015317 48.5076553 22.67303021 52.93945312 24.89648438 C61.62966021 29.25765972 70.31602362 33.62643276 79 38 C79 38.66 79 39.32 79 40 C69.14617359 44.96162844 59.28913947 49.9168183 49.42792797 54.86375427 C44.84935432 57.16087773 40.27192652 59.46022616 35.69702148 61.76464844 C31.28411887 63.98736649 26.86833299 66.20425375 22.45046425 68.41708374 C20.76327244 69.26345678 19.07714036 70.11194566 17.39208031 70.96255493 C15.03651482 72.15118441 12.67733497 73.33231761 10.31713867 74.51171875 C9.61726837 74.86704681 8.91739807 75.22237488 8.19631958 75.58847046 C5.2698443 77.04233211 3.31399908 78 0 78 C0 52.26 0 26.52 0 0 Z " fill="#FFFFFF" transform="translate(101,89)"/>
@ -89,7 +89,7 @@ class AutoplayToggleButton extends Button {
</svg>`; </svg>`;
} else { } else {
// this.iconSpan.classList.add('vjs-icon-play-circle'); // this.iconSpan.classList.add('vjs-icon-play-circle');
this.iconSpan.innerHTML = ` this.iconSpan.innerHTML = `1232132
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 300 300"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 300 300">
<path d="M0 0 C5.28 0.66 10.56 1.32 16 2 C11.67407494 30.83950041 -0.70166324 55.71110206 -24 74 C-47.86506837 91.08769673 -76.02581328 98.52206834 -105.125 93.8125 C-135.12151624 88.48114449 -157.27092449 72.37747882 -175 48 C-175.33 57.57 -175.66 67.14 -176 77 C-181.28 77 -186.56 77 -192 77 C-192 56.54 -192 36.08 -192 15 C-171.54 15 -151.08 15 -130 15 C-130 20.28 -130 25.56 -130 31 C-147.325 31.495 -147.325 31.495 -165 32 C-159.82225386 40.13645822 -155.56278318 46.32892007 -149 53 C-148.23945313 53.7734375 -147.47890625 54.546875 -146.6953125 55.34375 C-129.22175893 72.07252916 -106.1048424 78.80708624 -82.37109375 78.31640625 C-58.0970353 77.28060908 -37.04807338 65.00089922 -20.75390625 47.6015625 C-9.130597 33.96173371 -3.40740768 17.34680275 0 0 Z " fill="#b5bac4 " transform="translate(216,137)"/> <path d="M0 0 C5.28 0.66 10.56 1.32 16 2 C11.67407494 30.83950041 -0.70166324 55.71110206 -24 74 C-47.86506837 91.08769673 -76.02581328 98.52206834 -105.125 93.8125 C-135.12151624 88.48114449 -157.27092449 72.37747882 -175 48 C-175.33 57.57 -175.66 67.14 -176 77 C-181.28 77 -186.56 77 -192 77 C-192 56.54 -192 36.08 -192 15 C-171.54 15 -151.08 15 -130 15 C-130 20.28 -130 25.56 -130 31 C-147.325 31.495 -147.325 31.495 -165 32 C-159.82225386 40.13645822 -155.56278318 46.32892007 -149 53 C-148.23945313 53.7734375 -147.47890625 54.546875 -146.6953125 55.34375 C-129.22175893 72.07252916 -106.1048424 78.80708624 -82.37109375 78.31640625 C-58.0970353 77.28060908 -37.04807338 65.00089922 -20.75390625 47.6015625 C-9.130597 33.96173371 -3.40740768 17.34680275 0 0 Z " fill="#b5bac4 " transform="translate(216,137)"/>
<path d="M0 0 C4.65174076 0.93034815 8.20079246 2.396823 12.3605957 4.51000977 C13.08309006 4.8710379 13.80558441 5.23206604 14.54997253 5.60403442 C16.92813231 6.79415607 19.30193243 7.99271217 21.67578125 9.19140625 C23.32747078 10.02004975 24.97942673 10.84816241 26.63163757 11.67576599 C30.97273819 13.85203468 35.31018622 16.03548755 39.64691162 18.22045898 C44.07557427 20.45015317 48.5076553 22.67303021 52.93945312 24.89648438 C61.62966021 29.25765972 70.31602362 33.62643276 79 38 C79 38.66 79 39.32 79 40 C69.14617359 44.96162844 59.28913947 49.9168183 49.42792797 54.86375427 C44.84935432 57.16087773 40.27192652 59.46022616 35.69702148 61.76464844 C31.28411887 63.98736649 26.86833299 66.20425375 22.45046425 68.41708374 C20.76327244 69.26345678 19.07714036 70.11194566 17.39208031 70.96255493 C15.03651482 72.15118441 12.67733497 73.33231761 10.31713867 74.51171875 C9.61726837 74.86704681 8.91739807 75.22237488 8.19631958 75.58847046 C5.2698443 77.04233211 3.31399908 78 0 78 C0 52.26 0 26.52 0 0 Z " fill="#b5bac4" transform="translate(101,89)"/> <path d="M0 0 C4.65174076 0.93034815 8.20079246 2.396823 12.3605957 4.51000977 C13.08309006 4.8710379 13.80558441 5.23206604 14.54997253 5.60403442 C16.92813231 6.79415607 19.30193243 7.99271217 21.67578125 9.19140625 C23.32747078 10.02004975 24.97942673 10.84816241 26.63163757 11.67576599 C30.97273819 13.85203468 35.31018622 16.03548755 39.64691162 18.22045898 C44.07557427 20.45015317 48.5076553 22.67303021 52.93945312 24.89648438 C61.62966021 29.25765972 70.31602362 33.62643276 79 38 C79 38.66 79 39.32 79 40 C69.14617359 44.96162844 59.28913947 49.9168183 49.42792797 54.86375427 C44.84935432 57.16087773 40.27192652 59.46022616 35.69702148 61.76464844 C31.28411887 63.98736649 26.86833299 66.20425375 22.45046425 68.41708374 C20.76327244 69.26345678 19.07714036 70.11194566 17.39208031 70.96255493 C15.03651482 72.15118441 12.67733497 73.33231761 10.31713867 74.51171875 C9.61726837 74.86704681 8.91739807 75.22237488 8.19631958 75.58847046 C5.2698443 77.04233211 3.31399908 78 0 78 C0 52.26 0 26.52 0 0 Z " fill="#b5bac4" transform="translate(101,89)"/>

View File

@ -1831,10 +1831,6 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
return; return;
} }
const useNative = false; // /iPad|iPhone|iPod|Android/.test(navigator.userAgent);
console.log('useNative', useNative);
console.log('navigator.userAgent', navigator.userAgent);
//const timer = setTimeout(() => { //const timer = setTimeout(() => {
// Double-check that we still don't have a player and element exists // Double-check that we still don't have a player and element exists
if (!playerRef.current && videoRef.current && !videoRef.current.player) { if (!playerRef.current && videoRef.current && !videoRef.current.player) {
@ -1942,7 +1938,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
}, },
// Force native controls for touch devices // Force native controls for touch devices
nativeControlsForTouch: useNative, //true, nativeControlsForTouch: PlayerConfig.nativeControlsForTouch,
// Ensures consistent autoplay behavior across browsers (prevents unexpected blocking or auto-play issues) // Ensures consistent autoplay behavior across browsers (prevents unexpected blocking or auto-play issues)
normalizeAutoplay: true, normalizeAutoplay: true,
@ -2093,7 +2089,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
// ===== HTML5 TECH OPTIONS ===== // ===== HTML5 TECH OPTIONS =====
html5: { html5: {
// Force native controls for touch devices // Force native controls for touch devices
nativeControlsForTouch: useNative, //true, nativeControlsForTouch: PlayerConfig.nativeControlsForTouch,
// Use native audio tracks instead of emulated - disabled for consistency // Use native audio tracks instead of emulated - disabled for consistency
nativeAudioTracks: true, nativeAudioTracks: true,
@ -2502,57 +2498,13 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
const progressControl = controlBar.getChild('progressControl'); const progressControl = controlBar.getChild('progressControl');
const seekBar = progressControl?.getChild('seekBar'); const seekBar = progressControl?.getChild('seekBar');
// BEGIN: Move progress bar below control bar (native touch style) // BEGIN: Apply control bar styling from config (always applied)
setTimeout(() => { setTimeout(() => {
const controlBar = playerRef.current.getChild('controlBar'); const controlBarEl = controlBar?.el();
const progressControl = controlBar?.getChild('progressControl'); if (controlBarEl) {
if (progressControl && progressControl.el() && controlBar && controlBar.el()) {
const progressEl = progressControl.el();
const controlBarEl = controlBar.el();
controlBarEl.style.gap = 0;
// Remove progress control from control bar
controlBar.removeChild(progressControl);
// Create a wrapper div that will hold both progress and control bar
const wrapper = document.createElement('div');
wrapper.className = 'vjs-controls-wrapper';
wrapper.style.position = 'absolute';
wrapper.style.bottom = '0';
wrapper.style.left = '0';
wrapper.style.right = '0';
wrapper.style.width = '100%';
// Insert wrapper before control bar
controlBarEl.parentNode.insertBefore(wrapper, controlBarEl);
// Position elements based on config
if (PlayerConfig.progressBar.position === 'top') {
// Progress bar above control bar
wrapper.appendChild(progressEl);
wrapper.appendChild(controlBarEl);
} else {
// Progress bar below control bar (default/native style)
wrapper.appendChild(controlBarEl);
wrapper.appendChild(progressEl);
}
// Style the progress control using config values
progressEl.style.position = 'relative';
progressEl.style.width = '100%';
progressEl.style.height = '15px';
progressEl.style.margin = '8px 0'; // Add top and bottom margin
progressEl.style.padding = '5px 10px'; // Add left and right padding/gap
progressEl.style.display = 'block';
progressEl.style.transition = 'opacity 0.3s, visibility 0.3s'; // Smooth transition
progressEl.style.boxSizing = 'border-box'; // Ensure padding doesn't increase width
// Style control bar using config values // Style control bar using config values
controlBarEl.style.position = 'relative';
controlBarEl.style.width = '100%';
controlBarEl.style.height = `${PlayerConfig.controlBar.height}em`; controlBarEl.style.height = `${PlayerConfig.controlBar.height}em`;
controlBarEl.style.fontSize = `${PlayerConfig.controlBar.fontSize}em`; controlBarEl.style.fontSize = `${PlayerConfig.controlBar.fontSize}px`;
controlBarEl.style.backgroundColor = PlayerConfig.controlBar.backgroundColor; controlBarEl.style.backgroundColor = PlayerConfig.controlBar.backgroundColor;
// Apply same line height to time-related controls // Apply same line height to time-related controls
@ -2560,60 +2512,113 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
timeControls.forEach((timeControl) => { timeControls.forEach((timeControl) => {
timeControl.style.lineHeight = `${PlayerConfig.controlBar.height}em`; timeControl.style.lineHeight = `${PlayerConfig.controlBar.height}em`;
}); });
// Style the progress holder and bars with config colors
const progressHolder = progressEl.querySelector('.vjs-progress-holder');
if (progressHolder) {
progressHolder.style.height = '100%';
progressHolder.style.margin = '0';
progressHolder.style.backgroundColor = PlayerConfig.progressBar.trackColor;
}
// Style the play progress bar (the filled part)
const playProgress = progressEl.querySelector('.vjs-play-progress');
if (playProgress) {
playProgress.style.backgroundColor = PlayerConfig.progressBar.color;
}
// Style the load progress bar (buffered part)
const loadProgress = progressEl.querySelector('.vjs-load-progress');
if (loadProgress) {
loadProgress.style.backgroundColor = PlayerConfig.progressBar.bufferColor;
}
// Store reference for cleanup
customComponents.current.movedProgressControl = progressControl;
customComponents.current.controlsWrapper = wrapper;
// Hide/show progress bar with control bar based on user activity
const syncProgressVisibility = () => {
const isControlBarVisible =
controlBar.hasClass('vjs-visible') ||
!playerRef.current.hasClass('vjs-user-inactive');
if (isControlBarVisible) {
progressEl.style.opacity = '1';
progressEl.style.visibility = 'visible';
} else {
progressEl.style.opacity = '0';
progressEl.style.visibility = 'hidden';
}
};
// Listen to user activity events
playerRef.current.on('useractive', syncProgressVisibility);
playerRef.current.on('userinactive', syncProgressVisibility);
// Initial sync
syncProgressVisibility();
// Store cleanup function
customComponents.current.cleanupProgressVisibility = () => {
playerRef.current.off('useractive', syncProgressVisibility);
playerRef.current.off('userinactive', syncProgressVisibility);
};
} }
}, 100); }, 100);
// END: Apply control bar styling from config
// BEGIN: Move progress bar below control bar (native touch style)
if (PlayerConfig.progressBar.position === 'bottom' || PlayerConfig.progressBar.position === 'top') {
setTimeout(() => {
if (progressControl && progressControl.el() && controlBar && controlBar.el()) {
const progressEl = progressControl.el();
const controlBarEl = controlBar.el();
controlBarEl.style.gap = 0;
// Remove progress control from control bar
controlBar.removeChild(progressControl);
// Create a wrapper div that will hold both progress and control bar
const wrapper = document.createElement('div');
wrapper.className = 'vjs-controls-wrapper';
wrapper.style.position = 'absolute';
wrapper.style.bottom = '0';
wrapper.style.left = '0';
wrapper.style.right = '0';
wrapper.style.width = '100%';
// Insert wrapper before control bar
controlBarEl.parentNode.insertBefore(wrapper, controlBarEl);
// Position elements based on config
if (PlayerConfig.progressBar.position === 'top') {
// Progress bar above control bar
wrapper.appendChild(progressEl);
wrapper.appendChild(controlBarEl);
} else {
// Progress bar below control bar (default/native style)
wrapper.appendChild(controlBarEl);
wrapper.appendChild(progressEl);
}
// Style the progress control using config values
progressEl.style.position = 'relative';
progressEl.style.width = '100%';
progressEl.style.height = '15px';
progressEl.style.margin = '8px 0'; // Add top and bottom margin
progressEl.style.padding = '5px 10px'; // Add left and right padding/gap
progressEl.style.display = 'block';
progressEl.style.transition = 'opacity 0.3s, visibility 0.3s'; // Smooth transition
progressEl.style.boxSizing = 'border-box'; // Ensure padding doesn't increase width
// Style control bar positioning
controlBarEl.style.position = 'relative';
controlBarEl.style.width = '100%';
// Style the progress holder and bars with config colors
const progressHolder = progressEl.querySelector('.vjs-progress-holder');
if (progressHolder) {
progressHolder.style.height = '100%';
progressHolder.style.margin = '0';
progressHolder.style.backgroundColor = PlayerConfig.progressBar.trackColor;
}
// Style the play progress bar (the filled part)
const playProgress = progressEl.querySelector('.vjs-play-progress');
if (playProgress) {
playProgress.style.backgroundColor = PlayerConfig.progressBar.color;
}
// Style the load progress bar (buffered part)
const loadProgress = progressEl.querySelector('.vjs-load-progress');
if (loadProgress) {
loadProgress.style.backgroundColor = PlayerConfig.progressBar.bufferColor;
}
// Store reference for cleanup
customComponents.current.movedProgressControl = progressControl;
customComponents.current.controlsWrapper = wrapper;
// Hide/show progress bar with control bar based on user activity
const syncProgressVisibility = () => {
const isControlBarVisible =
controlBar.hasClass('vjs-visible') ||
!playerRef.current.hasClass('vjs-user-inactive');
if (isControlBarVisible) {
progressEl.style.opacity = '1';
progressEl.style.visibility = 'visible';
} else {
progressEl.style.opacity = '0';
progressEl.style.visibility = 'hidden';
}
};
// Listen to user activity events
playerRef.current.on('useractive', syncProgressVisibility);
playerRef.current.on('userinactive', syncProgressVisibility);
// Initial sync
syncProgressVisibility();
// Store cleanup function
customComponents.current.cleanupProgressVisibility = () => {
playerRef.current.off('useractive', syncProgressVisibility);
playerRef.current.off('userinactive', syncProgressVisibility);
};
}
}, 100);
}
// END: Move progress bar below control bar // END: Move progress bar below control bar
// Debug: Check if progress control exists and is visible on touch devices // Debug: Check if progress control exists and is visible on touch devices
@ -2791,6 +2796,35 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
customComponents.current.customRemainingTime = customRemainingTime; customComponents.current.customRemainingTime = customRemainingTime;
// END: Implement custom time display component // END: Implement custom time display component
// BEGIN: Add spacer to push right-side buttons to the right
if (
controlBar &&
customRemainingTime &&
customRemainingTime.el() &&
(PlayerConfig.progressBar.position === 'top' || PlayerConfig.progressBar.position === 'bottom')
) {
// Create spacer element
const spacer = document.createElement('div');
spacer.className = 'vjs-spacer-control vjs-control';
spacer.style.flex = '1';
spacer.style.minWidth = '1px';
// Insert spacer right after the time display
const controlBarEl = controlBar.el();
const timeDisplayEl = customRemainingTime.el();
const nextSibling = timeDisplayEl.nextSibling;
if (nextSibling) {
controlBarEl.insertBefore(spacer, nextSibling);
} else {
controlBarEl.appendChild(spacer);
}
// Store reference for cleanup
customComponents.current.spacer = spacer;
}
// END: Add spacer
// BEGIN: Wrap volume panel in custom div container // BEGIN: Wrap volume panel in custom div container
/* setTimeout(() => { /* setTimeout(() => {
const volumePanel = controlBar.getChild('volumePanel'); const volumePanel = controlBar.getChild('volumePanel');

View File

@ -4,12 +4,14 @@
*/ */
const PlayerConfig = { const PlayerConfig = {
nativeControlsForTouch: true,
// Progress bar configuration // Progress bar configuration
progressBar: { progressBar: {
// Position: 'top' or 'bottom' // Position: 'top' or 'bottom'
// 'top' - progress bar above control bar // 'top' - progress bar above control bar
// 'bottom' - progress bar below control bar (default/native style) // 'bottom' - progress bar below control bar (default/native style)
position: 'top', position: 'default',
// Progress bar color (hex, rgb, or CSS color name) // Progress bar color (hex, rgb, or CSS color name)
color: '#019932', color: '#019932',
@ -30,7 +32,7 @@ const PlayerConfig = {
height: 3, height: 3,
// Font size in em units // Font size in em units
fontSize: 1.5, fontSize: 26,
}, },
}; };