Commit abf327ab by luoqi

fix(web): hover Portal 防裁切 + 桌宠套绳距离上限

HoverCardContent 包 Portal + collisionPadding,优先级浮层逃出侧栏 overflow:hidden
不再被裁。桌宠套绳手势加距离上限(宠物鼠标 ≤ 半屏高),超距不触发。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 4d4339ab
...@@ -241,13 +241,18 @@ export function usePetLocomotion( ...@@ -241,13 +241,18 @@ export function usePetLocomotion(
/** 套绳荡飞:绳子套在锚点(默认 = 当前鼠标位置),钟摆荡 1.5 个来回后松手甩飞。 */ /** 套绳荡飞:绳子套在锚点(默认 = 当前鼠标位置),钟摆荡 1.5 个来回后松手甩飞。 */
/** 套绳就绪:鼠标在视窗内、且离宠物足够远(锚点可在任意方位 —— 上/下/侧都行)。 */ /** 套绳就绪:鼠标在视窗内、且离宠物足够远(锚点可在任意方位 —— 上/下/侧都行)。 */
const swingReady = () => const swingReady = () => {
const d = Math.hypot(c.cur.x - (c.x + size / 2), c.cur.y - (c.y + size / 2));
return (
c.cur.t > 0 && c.cur.t > 0 &&
c.cur.x >= 0 && c.cur.x >= 0 &&
c.cur.x <= window.innerWidth && c.cur.x <= window.innerWidth &&
c.cur.y >= 0 && c.cur.y >= 0 &&
c.cur.y <= window.innerHeight && c.cur.y <= window.innerHeight &&
Math.hypot(c.cur.x - (c.x + size / 2), c.cur.y - (c.y + size / 2)) > 110; d > 110 &&
d <= window.innerHeight / 2 // 距离上限:宠物↔鼠标 ≤ 半屏高,过远不套绳
);
};
const startSwing = (anchor?: { x: number; y: number }) => { const startSwing = (anchor?: { x: number; y: number }) => {
const pcx = c.x + size / 2; const pcx = c.x + size / 2;
...@@ -348,11 +353,14 @@ export function usePetLocomotion( ...@@ -348,11 +353,14 @@ export function usePetLocomotion(
cr.minY = Math.min(cr.minY, e.clientY); cr.minY = Math.min(cr.minY, e.clientY);
cr.maxY = Math.max(cr.maxY, e.clientY); cr.maxY = Math.max(cr.maxY, e.clientY);
const diag = Math.hypot(cr.maxX - cr.minX, cr.maxY - cr.minY); const diag = Math.hypot(cr.maxX - cr.minX, cr.maxY - cr.minY);
// 宠物↔鼠标(画圈处)距离上限:半个屏幕高度,过远不触发套绳
const petMouseDist = Math.hypot(e.clientX - (c.x + size / 2), e.clientY - (c.y + size / 2));
if ( if (
Math.abs(cr.accum) > 5.8 && // ≈ 330° Math.abs(cr.accum) > 5.8 && // ≈ 330°
cr.len > 140 && cr.len > 140 &&
diag > 30 && diag > 30 &&
diag < 650 && diag < 650 &&
petMouseDist <= window.innerHeight / 2 &&
now - cr.lastAt > 6_000 && now - cr.lastAt > 6_000 &&
c.motion === 'rest' && c.motion === 'rest' &&
!getBusy() !getBusy()
......
...@@ -13,10 +13,13 @@ const HoverCardContent = React.forwardRef< ...@@ -13,10 +13,13 @@ const HoverCardContent = React.forwardRef<
React.ElementRef<typeof HoverCardPrimitive.Content>, React.ElementRef<typeof HoverCardPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content> React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( >(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
// ⭐ Portal:渲染到 body,逃出祖先 overflow:hidden 裁切(优先级/画像 hover 浮层被侧栏切掉的根因)
<HoverCardPrimitive.Portal>
<HoverCardPrimitive.Content <HoverCardPrimitive.Content
ref={ref} ref={ref}
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
collisionPadding={8}
className={cn( className={cn(
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none', 'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none',
'data-[state=open]:animate-in data-[state=closed]:animate-out', 'data-[state=open]:animate-in data-[state=closed]:animate-out',
...@@ -28,6 +31,7 @@ const HoverCardContent = React.forwardRef< ...@@ -28,6 +31,7 @@ const HoverCardContent = React.forwardRef<
)} )}
{...props} {...props}
/> />
</HoverCardPrimitive.Portal>
)); ));
HoverCardContent.displayName = HoverCardPrimitive.Content.displayName; HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment