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 staleStashTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
const mountedRef = useRef(true);
|
const mountedRef = useRef(true);
|
||||||
const [isDragging, setIsDragging] = useState(false);
|
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 = () => {
|
const detachDragOver = () => {
|
||||||
if (dragOverHandlerRef.current) {
|
if (dragOverHandlerRef.current) {
|
||||||
|
|
@ -274,14 +279,28 @@ export function Piece({
|
||||||
? 'cursor-grab active:cursor-grabbing'
|
? 'cursor-grab active:cursor-grabbing'
|
||||||
: 'cursor-default';
|
: '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 (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<div
|
<div
|
||||||
draggable={isDraggable}
|
draggable={isDraggable}
|
||||||
data-piece={`${color}-${type}`}
|
data-piece={`${color}-${type}`}
|
||||||
data-piece-id={pieceId}
|
data-piece-id={pieceId}
|
||||||
|
data-hovered={showHover ? 'true' : 'false'}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
onDragEnd={handleDragEnd}
|
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}`}
|
className={`flex items-center justify-center w-full h-full select-none ${cursorClass}`}
|
||||||
>
|
>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -289,8 +308,14 @@ export function Piece({
|
||||||
className={`flex items-center justify-center w-full h-full ${isDragging ? 'pointer-events-none' : ''}`}
|
className={`flex items-center justify-center w-full h-full ${isDragging ? 'pointer-events-none' : ''}`}
|
||||||
style={{ x, y, rotate }}
|
style={{ x, y, rotate }}
|
||||||
animate={{
|
animate={{
|
||||||
scale: isDragging ? 1.15 : 1,
|
// Scale priorities (highest wins):
|
||||||
zIndex: isDragging ? 50 : 0,
|
// 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={{
|
transition={{
|
||||||
scale: { type: 'spring', stiffness: 500, damping: 30 },
|
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 ${
|
className={`w-[85%] h-[85%] pointer-events-none transition-[filter] duration-200 ${
|
||||||
isDragging
|
isDragging
|
||||||
? 'drop-shadow-[0_12px_16px_rgba(0,0,0,0.45)]'
|
? '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}
|
draggable={false}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue