修复VideoCard来源数量浮层左上角被裁切,并阻止长按同时弹出操作菜单

This commit is contained in:
mtvpls
2026-07-27 18:58:02 +08:00
parent c4be0e1886
commit 03a5ce4573
2 changed files with 142 additions and 119 deletions
+42 -17
View File
@@ -986,9 +986,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return false; return false;
}} }}
> >
{/* 海报容器 */} {/* 海报容器:不在外层 overflow-hidden,避免裁切来源数量浮层 */}
<div <div
className={`relative overflow-hidden rounded-lg ${ className={`relative rounded-lg ${
origin === 'live' origin === 'live'
? 'ring-1 ring-gray-300/80 dark:ring-gray-600/80' ? 'ring-1 ring-gray-300/80 dark:ring-gray-600/80'
: '' : ''
@@ -1007,11 +1007,15 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return false; return false;
}} }}
> >
{/* 仅图片层做圆角裁剪 */}
<div className='absolute inset-0 overflow-hidden rounded-lg'>
{/* 骨架屏 */} {/* 骨架屏 */}
{!isLoading && !isDirectPlaySource && ( {!isLoading && !isDirectPlaySource && (
<ImagePlaceholder <ImagePlaceholder
aspectRatio={ aspectRatio={
orientation === 'horizontal' ? 'aspect-[3/2]' : 'aspect-[2/3]' orientation === 'horizontal'
? 'aspect-[3/2]'
: 'aspect-[2/3]'
} }
/> />
)} )}
@@ -1120,6 +1124,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return false; return false;
}} }}
/> />
</div>
{/* 播放按钮或上映倒计时 */} {/* 播放按钮或上映倒计时 */}
{isUpcoming && daysUntilRelease !== null ? ( {isUpcoming && daysUntilRelease !== null ? (
@@ -1511,7 +1516,8 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return ( return (
<div <div
className={`absolute bottom-1 right-1 sm:bottom-2 sm:right-2 transition-all duration-300 ease-in-out delay-75 ${ data-button='true'
className={`absolute bottom-1 right-1 z-[55] sm:bottom-2 sm:right-2 transition-all duration-300 ease-in-out delay-75 ${
from === 'search' from === 'search'
? 'opacity-100' ? 'opacity-100'
: 'opacity-0 sm:group-hover:opacity-100' : 'opacity-0 sm:group-hover:opacity-100'
@@ -1523,9 +1529,32 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
WebkitTouchCallout: 'none', WebkitTouchCallout: 'none',
} as React.CSSProperties } as React.CSSProperties
} }
// 阻止冒泡到卡片:避免长按/右键同时弹出操作菜单
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault(); e.preventDefault();
return false; e.stopPropagation();
}}
onTouchStart={(e) => {
e.stopPropagation();
}}
onTouchMove={(e) => {
e.stopPropagation();
}}
onTouchEnd={(e) => {
e.stopPropagation();
}}
onMouseDown={(e) => {
// 右键/中键按下时也不要触发卡片手势
if (e.button !== 0) {
e.stopPropagation();
}
}}
onPointerDown={(e) => {
e.stopPropagation();
}} }}
> >
<div <div
@@ -1547,10 +1576,6 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
WebkitTouchCallout: 'none', WebkitTouchCallout: 'none',
} as React.CSSProperties } as React.CSSProperties
} }
onContextMenu={(e) => {
e.preventDefault();
return false;
}}
> >
{sourceCount} {sourceCount}
</div> </div>
@@ -1590,7 +1615,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
return ( return (
<div <div
className='absolute bottom-full mb-2 opacity-0 invisible group-hover/sources:opacity-100 group-hover/sources:visible transition-all duration-200 ease-out delay-100 pointer-events-none z-50 right-0 sm:right-0 -translate-x-0 sm:translate-x-0' className='absolute bottom-full right-0 z-[60] mb-2 -translate-x-0 opacity-0 invisible pointer-events-none transition-all duration-200 ease-out delay-100 group-hover/sources:opacity-100 group-hover/sources:visible sm:right-0 sm:translate-x-0'
style={ style={
{ {
WebkitUserSelect: 'none', WebkitUserSelect: 'none',
@@ -1604,7 +1629,7 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
}} }}
> >
<div <div
className='bg-gray-800/90 backdrop-blur-sm text-white text-xs sm:text-xs rounded-lg shadow-xl border border-white/10 p-1.5 sm:p-2 min-w-[100px] sm:min-w-[120px] max-w-[140px] sm:max-w-[200px] overflow-hidden' className='relative rounded-lg border border-white/10 bg-gray-800/95 p-1.5 text-xs text-white shadow-xl backdrop-blur-sm sm:p-2 sm:text-xs min-w-[100px] max-w-[140px] sm:min-w-[120px] sm:max-w-[200px]'
style={ style={
{ {
WebkitUserSelect: 'none', WebkitUserSelect: 'none',
@@ -1624,9 +1649,9 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
key={index} key={index}
className='flex items-center gap-1 sm:gap-1.5' className='flex items-center gap-1 sm:gap-1.5'
> >
<div className='w-0.5 h-0.5 sm:w-1 sm:h-1 bg-blue-400 rounded-full flex-shrink-0'></div> <div className='h-0.5 w-0.5 flex-shrink-0 rounded-full bg-blue-400 sm:h-1 sm:w-1'></div>
<span <span
className='truncate text-[10px] sm:text-xs leading-tight' className='truncate text-[10px] leading-tight sm:text-xs'
title={sourceName} title={sourceName}
> >
{sourceName} {sourceName}
@@ -1637,17 +1662,17 @@ const VideoCard = forwardRef<VideoCardHandle, VideoCardProps>(
{/* 显示更多提示 */} {/* 显示更多提示 */}
{hasMore && ( {hasMore && (
<div className='mt-1 sm:mt-2 pt-1 sm:pt-1.5 border-t border-gray-700/50'> <div className='mt-1 border-t border-gray-700/50 pt-1 sm:mt-2 sm:pt-1.5'>
<div className='flex items-center justify-center text-gray-400'> <div className='flex items-center justify-center text-gray-400'>
<span className='text-[10px] sm:text-xs font-medium'> <span className='text-[10px] font-medium sm:text-xs'>
+{remainingCount} +{remainingCount}
</span> </span>
</div> </div>
</div> </div>
)} )}
{/* 小箭头 */} {/* 小箭头:放在内容盒外侧,避免被裁切 */}
<div className='absolute top-full right-2 sm:right-3 w-0 h-0 border-l-[4px] border-r-[4px] border-t-[4px] sm:border-l-[6px] sm:border-r-[6px] sm:border-t-[6px] border-transparent border-t-gray-800/90'></div> <div className='absolute top-full right-2 h-0 w-0 border-l-[4px] border-r-[4px] border-t-[4px] border-transparent border-t-gray-800/95 sm:right-3 sm:border-l-[6px] sm:border-r-[6px] sm:border-t-[6px]'></div>
</div> </div>
</div> </div>
); );
+6 -8
View File
@@ -103,17 +103,15 @@ export const useLongPress = ({
// 触摸事件处理器 // 触摸事件处理器
const onTouchStart = useCallback( const onTouchStart = useCallback(
(e: React.TouchEvent) => { (e: React.TouchEvent) => {
// 检查是否触摸的是按钮或其他交互元素 // 触摸到标记为 data-button 的交互区域(或其子元素)时:
// 不启动卡片长按,避免与控件自身手势(如来源数量浮层)冲突
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
const buttonElement = target.closest('[data-button]'); if (target.closest('[data-button]')) {
return;
}
// 更精确的按钮检测:只有当触摸目标直接是按钮元素或其直接子元素时才认为是按钮
const isDirectButton = target.hasAttribute('data-button');
const isButton = !!buttonElement && isDirectButton;
// 阻止默认的长按行为,但不阻止触摸开始事件
const touch = e.touches[0]; const touch = e.touches[0];
handleStart(touch.clientX, touch.clientY, !!isButton); handleStart(touch.clientX, touch.clientY, false);
}, },
[handleStart] [handleStart]
); );