feat(chess): hover affordance on draggable pieces
Pieces you can actually pick up (your color, your turn, game ongoing) now lift slightly on mouseover — spring scale to 1.08 with a deeper drop shadow — so you see at a glance which piece you are about to grab. Opponent pieces and your pieces on the opponent\`s turn stay static, so the hover never advertises an action you cannot take. The effect is suppressed while actively dragging; the larger drag scale already owns the visual treatment there.
This commit is contained in:
parent
ae87772277
commit
598370fe2e
1 changed files with 30 additions and 3 deletions
|
|
@ -110,6 +110,11 @@ export function Piece({
|
|||
const staleStashTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const mountedRef = useRef(true);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
// Hover state — tracked only when the piece is draggable (i.e. it's
|
||||
// this player's turn AND this is one of their pieces). Feeds a subtle
|
||||
// scale-up + brighter shadow so you can see at a glance which piece
|
||||
// you're about to pick up.
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const detachDragOver = () => {
|
||||
if (dragOverHandlerRef.current) {
|
||||
|
|
@ -274,14 +279,28 @@ export function Piece({
|
|||
? 'cursor-grab active:cursor-grabbing'
|
||||
: 'cursor-default';
|
||||
|
||||
// Only apply hover affordance when the piece can actually be picked
|
||||
// up. On opponent pieces (or your pieces on the opponent's turn) the
|
||||
// hover is a no-op so we don't advertise an action the player can't
|
||||
// take. We also suppress it while actively dragging — the scale-up
|
||||
// there is owned by the drag treatment.
|
||||
const showHover = isHovered && isDraggable && !isDragging;
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<div
|
||||
draggable={isDraggable}
|
||||
data-piece={`${color}-${type}`}
|
||||
data-piece-id={pieceId}
|
||||
data-hovered={showHover ? 'true' : 'false'}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
// Mouse hover toggles are gated to draggable pieces inside
|
||||
// `showHover` so we don't need to branch the handlers; keeping
|
||||
// them unconditional means the state is correct if `isDraggable`
|
||||
// flips mid-hover (e.g. a move completes while hovering).
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
className={`flex items-center justify-center w-full h-full select-none ${cursorClass}`}
|
||||
>
|
||||
<motion.div
|
||||
|
|
@ -289,8 +308,14 @@ export function Piece({
|
|||
className={`flex items-center justify-center w-full h-full ${isDragging ? 'pointer-events-none' : ''}`}
|
||||
style={{ x, y, rotate }}
|
||||
animate={{
|
||||
scale: isDragging ? 1.15 : 1,
|
||||
zIndex: isDragging ? 50 : 0,
|
||||
// Scale priorities (highest wins):
|
||||
// isDragging -> 1.15 (big lift, matches drag juice)
|
||||
// showHover -> 1.08 (subtle pickup affordance)
|
||||
// otherwise -> 1.00
|
||||
// zIndex bumps on hover too so the enlarged piece overlaps
|
||||
// neighbours cleanly without clipping.
|
||||
scale: isDragging ? 1.15 : showHover ? 1.08 : 1,
|
||||
zIndex: isDragging ? 50 : showHover ? 10 : 0,
|
||||
}}
|
||||
transition={{
|
||||
scale: { type: 'spring', stiffness: 500, damping: 30 },
|
||||
|
|
@ -303,7 +328,9 @@ export function Piece({
|
|||
className={`w-[85%] h-[85%] pointer-events-none transition-[filter] duration-200 ${
|
||||
isDragging
|
||||
? 'drop-shadow-[0_12px_16px_rgba(0,0,0,0.45)]'
|
||||
: 'drop-shadow-[0_4px_4px_rgba(0,0,0,0.3)]'
|
||||
: showHover
|
||||
? 'drop-shadow-[0_8px_10px_rgba(0,0,0,0.4)]'
|
||||
: 'drop-shadow-[0_4px_4px_rgba(0,0,0,0.3)]'
|
||||
}`}
|
||||
draggable={false}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue