From 03a5ce45732ee92282cf48db2cb49feb264c915e Mon Sep 17 00:00:00 2001 From: mtvpls <247332661+mtvpls@users.noreply.github.com> Date: Mon, 27 Jul 2026 18:58:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DVideoCard=E6=9D=A5=E6=BA=90?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E6=B5=AE=E5=B1=82=E5=B7=A6=E4=B8=8A=E8=A7=92?= =?UTF-8?q?=E8=A2=AB=E8=A3=81=E5=88=87=EF=BC=8C=E5=B9=B6=E9=98=BB=E6=AD=A2?= =?UTF-8?q?=E9=95=BF=E6=8C=89=E5=90=8C=E6=97=B6=E5=BC=B9=E5=87=BA=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/VideoCard.tsx | 247 +++++++++++++++++++---------------- src/hooks/useLongPress.ts | 14 +- 2 files changed, 142 insertions(+), 119 deletions(-) diff --git a/src/components/VideoCard.tsx b/src/components/VideoCard.tsx index 4b39677..d98637e 100644 --- a/src/components/VideoCard.tsx +++ b/src/components/VideoCard.tsx @@ -986,9 +986,9 @@ const VideoCard = forwardRef( return false; }} > - {/* 海报容器 */} + {/* 海报容器:不在外层 overflow-hidden,避免裁切来源数量浮层 */}
( return false; }} > - {/* 骨架屏 */} - {!isLoading && !isDirectPlaySource && ( - - )} - {isDirectPlaySource ? ( -
- -
- ) : isNetdiskSource(actualSource) && - !actualPoster && - displayPoster === netdiskPosterPlaceholder ? ( -
- -
- ) : ( - {actualTitle} { - setIsLoading(true); - }} - onClick={(e) => { - e.stopPropagation(); - setShowImageViewer(true); - }} - onError={(e) => { - const img = e.currentTarget as HTMLImageElement; - const doubanFallbackPoster = - getDoubanImageFallbackUrl(actualPoster); - if ( - doubanFallbackPoster && - tryApplyDoubanImageFallback(img, actualPoster) - ) { - setDisplayPoster(doubanFallbackPoster); - return; + {/* 仅图片层做圆角裁剪 */} +
+ {/* 骨架屏 */} + {!isLoading && !isDirectPlaySource && ( + + )} + {isDirectPlaySource ? ( +
+ +
+ ) : isNetdiskSource(actualSource) && + !actualPoster && + displayPoster === netdiskPosterPlaceholder ? ( +
+ +
+ ) : ( + {actualTitle} { + setIsLoading(true); + }} + onClick={(e) => { + e.stopPropagation(); + setShowImageViewer(true); + }} + onError={(e) => { + const img = e.currentTarget as HTMLImageElement; + const doubanFallbackPoster = + getDoubanImageFallbackUrl(actualPoster); + if ( + doubanFallbackPoster && + tryApplyDoubanImageFallback(img, actualPoster) + ) { + setDisplayPoster(doubanFallbackPoster); + return; + } - if ( - clearBangumiImageFallbackCacheIfFailed(img, actualPoster) - ) { - setDisplayPoster(processedPoster); - return; - } + const bangumiFallbackPoster = + getBangumiImageFallbackUrl(actualPoster); + if ( + bangumiFallbackPoster && + tryApplyBangumiImageFallback(img, actualPoster) + ) { + setDisplayPoster(bangumiFallbackPoster); + return; + } - // 图片加载失败时的重试机制 - if (!img.dataset.retried) { - img.dataset.retried = 'true'; - setTimeout(() => { + if ( + clearBangumiImageFallbackCacheIfFailed(img, actualPoster) + ) { setDisplayPoster(processedPoster); - img.src = processedPoster; - }, 2000); + return; + } + + // 图片加载失败时的重试机制 + if (!img.dataset.retried) { + img.dataset.retried = 'true'; + setTimeout(() => { + setDisplayPoster(processedPoster); + img.src = processedPoster; + }, 2000); + } + }} + style={ + { + // 禁用图片的默认长按效果 + WebkitUserSelect: 'none', + userSelect: 'none', + WebkitTouchCallout: 'none', + pointerEvents: 'auto', // 改为auto以响应点击事件 + cursor: 'pointer', // 添加指针样式 + } as React.CSSProperties } - }} + onContextMenu={(e) => { + e.preventDefault(); + return false; + }} + onDragStart={(e) => { + e.preventDefault(); + return false; + }} + /> + )} + + {/* 悬浮遮罩 */} +
{ e.preventDefault(); return false; }} - onDragStart={(e) => { - e.preventDefault(); - return false; - }} /> - )} - - {/* 悬浮遮罩 */} -
{ - e.preventDefault(); - return false; - }} - /> +
{/* 播放按钮或上映倒计时 */} {isUpcoming && daysUntilRelease !== null ? ( @@ -1511,7 +1516,8 @@ const VideoCard = forwardRef( return (
( WebkitTouchCallout: 'none', } as React.CSSProperties } + // 阻止冒泡到卡片:避免长按/右键同时弹出操作菜单 + onClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + }} onContextMenu={(e) => { 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(); }} >
( WebkitTouchCallout: 'none', } as React.CSSProperties } - onContextMenu={(e) => { - e.preventDefault(); - return false; - }} > {sourceCount}
@@ -1590,7 +1615,7 @@ const VideoCard = forwardRef( return (
( }} >
( key={index} className='flex items-center gap-1 sm:gap-1.5' > -
+
{sourceName} @@ -1637,17 +1662,17 @@ const VideoCard = forwardRef( {/* 显示更多提示 */} {hasMore && ( -
+
- + +{remainingCount} 播放源
)} - {/* 小箭头 */} -
+ {/* 小箭头:放在内容盒外侧,避免被裁切 */} +
); diff --git a/src/hooks/useLongPress.ts b/src/hooks/useLongPress.ts index 84e10a2..58bd144 100644 --- a/src/hooks/useLongPress.ts +++ b/src/hooks/useLongPress.ts @@ -103,17 +103,15 @@ export const useLongPress = ({ // 触摸事件处理器 const onTouchStart = useCallback( (e: React.TouchEvent) => { - // 检查是否触摸的是按钮或其他交互元素 + // 触摸到标记为 data-button 的交互区域(或其子元素)时: + // 不启动卡片长按,避免与控件自身手势(如来源数量浮层)冲突 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]; - handleStart(touch.clientX, touch.clientY, !!isButton); + handleStart(touch.clientX, touch.clientY, false); }, [handleStart] );