export interface SizeType {width: numberheight: number}export interface MousePosition {x: number;y: number;}
Source: Showcase Item
Types
Wrappers
Define a custom wrapper component for our styles
namespace ShowcaseItemWrapper {export type Props = (& HTMLAttributes<HTMLDivElement>& ShowcaseWrapperStylesProps);}const ShowcaseItemWrapper = forwardRef<HTMLDivElement, ShowcaseItemWrapper.Props>(function ShowcaseItemWrapper({className,which,active,...props}, reference) {return (<div{...props}ref={reference}className={[className, showcaseWrapperStyles({which,active,})].filter(Boolean).join(' ')}/>);},);
Showcase Component
This is where we will tap into our topmost wrapper to track,mouse movement and relay this information to our CSS,to achieve our tilt effect
export namespace ShowcaseItem {export type Props = ImgHTMLAttributes<HTMLImageElement>;}export function ShowcaseItem({ className, ...props }: ShowcaseItem.Props) {// Define our position stateconst [position, setPosition] = useState<MousePosition>();// Handle mouse movements within our wrapper (both enter and move)// We'll be tracking the position of our cursor relative to our item// in the [0, 1] range with respects to the item's size// (eg. item is 300x200px, cursor is at 100x20,// our position will be 0.33x0.1)const onEnterMove = useCallback<MouseEventHandler<HTMLDivElement>>(({clientX,clientY,currentTarget: element,}) => {const {top,left,width,height,} = element.getBoundingClientRect();const [x, y] = [clientX - left, clientY - top];setPosition({x: x / width,y: y / height,});},[setPosition],);const onTouchEnterMove = useCallback<TouchEventHandler<HTMLDivElement>>(({touches,currentTarget: element,}) => {const { clientX, clientY } = touches.item(0);const {top,left,width,height,} = element.getBoundingClientRect();const [x, y] = [clientX - left, clientY - top];setPosition({x: x / width,y: y / height,});},[setPosition],);// Handle the loss of interaction (mouse moves outside our component)const onLeave = useCallback(() => {setPosition(undefined);}, [setPosition]);const isActive = !!position;return (// Outer wrapper will handle scaling and interactions// It will also relay our position to our CSS<ShowcaseItemWrapperonMouseEnter={onEnterMove}onMouseMove ={onEnterMove}onMouseLeave={onLeave}onTouchStart={onTouchEnterMove}onTouchMove={onTouchEnterMove}onTouchCancel={onLeave}onTouchEnd={onLeave}which="outer"active={isActive}style={isActive? assignInlineVars({[xPosition]: `${position.x}`,[yPosition]: `${position.y}`,}): {}}>{(// Inner wrapper will handle the tilting effect<ShowcaseItemWrapperwhich='inner'active={isActive}>{(// Finally, render the image itself<img {...props} className={[className, showcaseImageStyles].filter(Boolean).join(' ')} />)}</ShowcaseItemWrapper>)}</ShowcaseItemWrapper>);}