mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-07 16:08:54 -05:00
fix: Make seekbar more touch-friendly on Android
This commit is contained in:
parent
57616c6b81
commit
b04ad2344c
@ -23,6 +23,37 @@ html {
|
|||||||
visibility: hidden !important;
|
visibility: hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Simple fix: Move seekbar up by 10px on touch devices */
|
||||||
|
.video-js .vjs-progress-control {
|
||||||
|
bottom: 56px !important; /* Move up 10px from original 46px */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make seekbar more touch-friendly on Android */
|
||||||
|
.video-js .vjs-progress-holder {
|
||||||
|
touch-action: pan-x !important;
|
||||||
|
-webkit-touch-callout: none !important;
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js .vjs-seek-bar {
|
||||||
|
touch-action: pan-x !important;
|
||||||
|
-webkit-touch-callout: none !important;
|
||||||
|
-webkit-user-select: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prevent big play button from interfering with seekbar on touch devices */
|
||||||
|
.video-js .vjs-big-play-button {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
z-index: 1 !important; /* Lower than seekbar */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-js .vjs-progress-control {
|
||||||
|
z-index: 10 !important; /* Higher than big play button */
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Exception: Allow intentional touch-activated tooltips */
|
/* Exception: Allow intentional touch-activated tooltips */
|
||||||
.video-js .vjs-autoplay-toggle.touch-active::after {
|
.video-js .vjs-autoplay-toggle.touch-active::after {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
@ -744,6 +775,7 @@ html {
|
|||||||
.vjs-slider-horizontal {
|
.vjs-slider-horizontal {
|
||||||
top: -5px;
|
top: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-js .vjs-spacer-control {
|
.video-js .vjs-spacer-control {
|
||||||
flex: 1 !important;
|
flex: 1 !important;
|
||||||
min-width: 1px !important;
|
min-width: 1px !important;
|
||||||
@ -1364,56 +1396,6 @@ button.vjs-button > .vjs-icon-placeholder:before {
|
|||||||
width: auto;
|
width: auto;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable all tooltips on touch devices */
|
|
||||||
.video-js .vjs-control:hover::after,
|
|
||||||
.video-js .vjs-control:focus::after,
|
|
||||||
.video-js .vjs-control:active::after {
|
|
||||||
display: none !important;
|
|
||||||
opacity: 0 !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-js .vjs-play-control:hover::after,
|
|
||||||
.video-js .vjs-mute-control:hover::after,
|
|
||||||
.video-js .vjs-volume-panel:hover::after,
|
|
||||||
.video-js .vjs-fullscreen-control:hover::after,
|
|
||||||
.video-js .vjs-picture-in-picture-control:hover::after,
|
|
||||||
.video-js .vjs-settings-control:hover::after,
|
|
||||||
.video-js .vjs-chapters-control:hover::after,
|
|
||||||
.video-js .vjs-autoplay-toggle:hover::after,
|
|
||||||
.video-js .vjs-next-video-control:hover::after,
|
|
||||||
.video-js .vjs-remaining-time:hover::after {
|
|
||||||
display: none !important;
|
|
||||||
opacity: 0 !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable Video.js native control text tooltips on touch devices */
|
|
||||||
.video-js button.vjs-button:hover span.vjs-control-text {
|
|
||||||
opacity: 0 !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable chapter marker tooltips on touch devices */
|
|
||||||
.vjs-chapter-marker:hover .vjs-chapter-marker-tooltip {
|
|
||||||
opacity: 0 !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Disable chapter floating tooltips on touch devices */
|
|
||||||
.vjs-chapter-floating-tooltip,
|
|
||||||
.vjs-sprite-preview-tooltip {
|
|
||||||
display: none !important;
|
|
||||||
opacity: 0 !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exception: Allow touch-activated autoplay tooltip on touch devices */
|
|
||||||
.video-js .vjs-autoplay-toggle.touch-active::after {
|
|
||||||
opacity: 1 !important;
|
|
||||||
visibility: visible !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
@media (min-width: 1200px) {
|
||||||
@ -1535,6 +1517,11 @@ button.vjs-button > .vjs-icon-placeholder:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
|
/* Move seekbar up by 10px on mobile to prevent accidental button touches */
|
||||||
|
.video-js .vjs-progress-control {
|
||||||
|
bottom: 56px !important; /* Move up 10px from original 46px */
|
||||||
|
}
|
||||||
|
|
||||||
.vjs-related-vdeo-item:nth-child(n + 5) {
|
.vjs-related-vdeo-item:nth-child(n + 5) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -1773,6 +1760,11 @@ button.vjs-button > .vjs-icon-placeholder:before {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
/* Move seekbar up by 10px on small mobile to prevent accidental button touches */
|
||||||
|
.video-js .vjs-progress-control {
|
||||||
|
bottom: 56px !important; /* Move up 10px from original 46px */
|
||||||
|
}
|
||||||
|
|
||||||
.video-container {
|
.video-container {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1047,7 +1047,7 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// other
|
// other
|
||||||
useRoundedCorners: false,
|
useRoundedCorners: true,
|
||||||
isPlayList: true,
|
isPlayList: true,
|
||||||
previewSprite: {
|
previewSprite: {
|
||||||
url: 'https://deic.mediacms.io/media/original/thumbnails/user/thorkild/2ca18fadeef8475eae513c12cc0830d3.19990812hd_1920_1080_30fps.mp4sprites.jpg',
|
url: 'https://deic.mediacms.io/media/original/thumbnails/user/thorkild/2ca18fadeef8475eae513c12cc0830d3.19990812hd_1920_1080_30fps.mp4sprites.jpg',
|
||||||
@ -1998,14 +1998,27 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
|
|||||||
touchStartTime = Date.now();
|
touchStartTime = Date.now();
|
||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
touchStartPos = { x: touch.clientX, y: touch.clientY };
|
touchStartPos = { x: touch.clientX, y: touch.clientY };
|
||||||
|
|
||||||
|
// Check if touch is in seekbar area
|
||||||
|
const progressControl = playerRef.current
|
||||||
|
.getChild('controlBar')
|
||||||
|
?.getChild('progressControl');
|
||||||
|
if (progressControl && progressControl.el()) {
|
||||||
|
const progressRect = progressControl.el().getBoundingClientRect();
|
||||||
|
const isInSeekbarArea =
|
||||||
|
touch.clientY >= progressRect.top && touch.clientY <= progressRect.bottom;
|
||||||
|
if (isInSeekbarArea) {
|
||||||
|
playerRef.current.seekbarTouching = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTouchEnd = (e) => {
|
const handleTouchEnd = (e) => {
|
||||||
const touchEndTime = Date.now();
|
const touchEndTime = Date.now();
|
||||||
const touchDuration = touchEndTime - touchStartTime;
|
const touchDuration = touchEndTime - touchStartTime;
|
||||||
|
|
||||||
// Only handle if it's a quick tap
|
// Only handle if it's a quick tap and we're not touching the seekbar
|
||||||
if (touchDuration < 500) {
|
if (touchDuration < 500 && !playerRef.current.seekbarTouching) {
|
||||||
const touch = e.changedTouches[0];
|
const touch = e.changedTouches[0];
|
||||||
const touchEndPos = { x: touch.clientX, y: touch.clientY };
|
const touchEndPos = { x: touch.clientX, y: touch.clientY };
|
||||||
const distance = Math.sqrt(
|
const distance = Math.sqrt(
|
||||||
@ -2025,6 +2038,13 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always clear seekbar touching flag at the end
|
||||||
|
setTimeout(() => {
|
||||||
|
if (playerRef.current) {
|
||||||
|
playerRef.current.seekbarTouching = false;
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
};
|
};
|
||||||
|
|
||||||
videoEl.addEventListener('touchstart', handleTouchStart, { passive: true });
|
videoEl.addEventListener('touchstart', handleTouchStart, { passive: true });
|
||||||
@ -2506,6 +2526,100 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
|
|||||||
|
|
||||||
// Store components reference for potential cleanup
|
// Store components reference for potential cleanup
|
||||||
|
|
||||||
|
// BEGIN: Fix Android seekbar touch functionality
|
||||||
|
if (isTouchDevice) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const progressControl = playerRef.current
|
||||||
|
.getChild('controlBar')
|
||||||
|
?.getChild('progressControl');
|
||||||
|
const seekBar = progressControl?.getChild('seekBar');
|
||||||
|
|
||||||
|
if (seekBar && seekBar.el()) {
|
||||||
|
const seekBarEl = seekBar.el();
|
||||||
|
const progressHolder = seekBarEl.querySelector('.vjs-progress-holder');
|
||||||
|
|
||||||
|
if (progressHolder) {
|
||||||
|
let isDragging = false;
|
||||||
|
|
||||||
|
const handleTouchStart = (e) => {
|
||||||
|
isDragging = true;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Prevent event from reaching video element
|
||||||
|
playerRef.current.userActive(true);
|
||||||
|
|
||||||
|
// Mark that we're interacting with seekbar to prevent play/pause
|
||||||
|
playerRef.current.seekbarTouching = true;
|
||||||
|
|
||||||
|
// Temporarily disable big play button
|
||||||
|
const bigPlayButton = playerRef.current.getChild('bigPlayButton');
|
||||||
|
if (bigPlayButton && bigPlayButton.el()) {
|
||||||
|
bigPlayButton.el().style.pointerEvents = 'none';
|
||||||
|
bigPlayButton.el().style.touchAction = 'none';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchMove = (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Prevent event from reaching video element
|
||||||
|
|
||||||
|
const touch = e.touches[0];
|
||||||
|
const rect = progressHolder.getBoundingClientRect();
|
||||||
|
const percentage = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(1, (touch.clientX - rect.left) / rect.width)
|
||||||
|
);
|
||||||
|
const duration = playerRef.current.duration();
|
||||||
|
|
||||||
|
if (duration && !isNaN(duration)) {
|
||||||
|
const newTime = percentage * duration;
|
||||||
|
playerRef.current.currentTime(newTime);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTouchEnd = (e) => {
|
||||||
|
isDragging = false;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation(); // Prevent event from reaching video element
|
||||||
|
|
||||||
|
// Re-enable big play button
|
||||||
|
const bigPlayButton = playerRef.current.getChild('bigPlayButton');
|
||||||
|
if (bigPlayButton && bigPlayButton.el()) {
|
||||||
|
setTimeout(() => {
|
||||||
|
bigPlayButton.el().style.pointerEvents = '';
|
||||||
|
bigPlayButton.el().style.touchAction = '';
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the seekbar touching flag after a longer delay to prevent conflicts
|
||||||
|
setTimeout(() => {
|
||||||
|
if (playerRef.current) {
|
||||||
|
playerRef.current.seekbarTouching = false;
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add touch event listeners specifically for Android
|
||||||
|
progressHolder.addEventListener('touchstart', handleTouchStart, {
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
progressHolder.addEventListener('touchmove', handleTouchMove, {
|
||||||
|
passive: false,
|
||||||
|
});
|
||||||
|
progressHolder.addEventListener('touchend', handleTouchEnd, { passive: false });
|
||||||
|
|
||||||
|
// Store cleanup function
|
||||||
|
customComponents.current.cleanupSeekbarTouch = () => {
|
||||||
|
progressHolder.removeEventListener('touchstart', handleTouchStart);
|
||||||
|
progressHolder.removeEventListener('touchmove', handleTouchMove);
|
||||||
|
progressHolder.removeEventListener('touchend', handleTouchEnd);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
// END: Fix Android seekbar touch functionality
|
||||||
|
|
||||||
// BEGIN: Add comprehensive keyboard event handling
|
// BEGIN: Add comprehensive keyboard event handling
|
||||||
const handleAllKeyboardEvents = (event) => {
|
const handleAllKeyboardEvents = (event) => {
|
||||||
// Only handle if no input elements are focused
|
// Only handle if no input elements are focused
|
||||||
@ -2805,6 +2919,11 @@ function VideoJSPlayer({ videoId = 'default-video' }) {
|
|||||||
customComponents.current.cleanupArrowKeyHandler();
|
customComponents.current.cleanupArrowKeyHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up seekbar touch handlers if they exist
|
||||||
|
if (customComponents.current && customComponents.current.cleanupSeekbarTouch) {
|
||||||
|
customComponents.current.cleanupSeekbarTouch();
|
||||||
|
}
|
||||||
|
|
||||||
if (playerRef.current && !playerRef.current.isDisposed()) {
|
if (playerRef.current && !playerRef.current.isDisposed()) {
|
||||||
playerRef.current.dispose();
|
playerRef.current.dispose();
|
||||||
playerRef.current = null;
|
playerRef.current = null;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user