97 lines
3.1 KiB
TypeScript
97 lines
3.1 KiB
TypeScript
import { useState, useEffect, useCallback, RefObject } from 'react';
|
|
|
|
const COLORS = [
|
|
'#F94144', '#F3722C', '#F8961E', '#F9C74F', '#90BE6D',
|
|
'#43AA8B', '#4D908E', '#577590', '#277DA1', '#F94144'
|
|
];
|
|
|
|
function getCursorColor(id: string) {
|
|
let hash = 0;
|
|
for (let i = 0; i < id.length; i++) {
|
|
hash = id.charCodeAt(i) + ((hash << 5) - hash);
|
|
}
|
|
return COLORS[Math.abs(hash) % COLORS.length];
|
|
}
|
|
|
|
export function useCursors(sendMessage: (message: any) => void, lastMessage: any, clientId: string | null, mainRef: RefObject<HTMLElement>) {
|
|
const [normalizedCursors, setNormalizedCursors] = useState<any>({});
|
|
const [cursors, setCursors] = useState<any>({});
|
|
|
|
const handleMouseMove = useCallback((e: MouseEvent) => {
|
|
if (!mainRef.current) return;
|
|
|
|
const mainRect = mainRef.current.getBoundingClientRect();
|
|
const x = e.clientX - mainRect.left;
|
|
const y = e.clientY - mainRect.top;
|
|
|
|
const normalizedX = x / mainRect.width;
|
|
const normalizedY = y / mainRect.height;
|
|
|
|
sendMessage({
|
|
type: 'cursor-move',
|
|
payload: { x: normalizedX, y: normalizedY }
|
|
});
|
|
}, [sendMessage, mainRef]);
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('mousemove', handleMouseMove);
|
|
return () => {
|
|
window.removeEventListener('mousemove', handleMouseMove);
|
|
};
|
|
}, [handleMouseMove]);
|
|
|
|
const updateCursorPositions = useCallback(() => {
|
|
if (!mainRef.current) return;
|
|
const mainRect = mainRef.current.getBoundingClientRect();
|
|
const newCursors = {};
|
|
for (const id in normalizedCursors) {
|
|
const nc = normalizedCursors[id];
|
|
newCursors[id] = {
|
|
...nc,
|
|
x: nc.x * mainRect.width,
|
|
y: nc.y * mainRect.height
|
|
};
|
|
}
|
|
setCursors(newCursors);
|
|
}, [normalizedCursors, mainRef]);
|
|
|
|
|
|
useEffect(() => {
|
|
if (!lastMessage) return;
|
|
|
|
const { type, payload, senderId } = lastMessage;
|
|
|
|
if (type === 'user-update') {
|
|
const remoteUsers = payload.users.filter((user: any) => user.id !== clientId);
|
|
const newCursors = {};
|
|
remoteUsers.forEach((user: any) => {
|
|
const existing = normalizedCursors[user.id];
|
|
newCursors[user.id] = {
|
|
id: user.id,
|
|
color: getCursorColor(user.id),
|
|
x: existing?.x || 0,
|
|
y: existing?.y || 0
|
|
};
|
|
});
|
|
setNormalizedCursors(newCursors);
|
|
} else if (type === 'cursor-move' && senderId !== clientId) {
|
|
setNormalizedCursors(prev => ({
|
|
...prev,
|
|
[senderId]: { ...prev[senderId], ...payload }
|
|
}));
|
|
}
|
|
|
|
}, [lastMessage, clientId]);
|
|
|
|
useEffect(() => {
|
|
updateCursorPositions();
|
|
window.addEventListener('resize', updateCursorPositions);
|
|
return () => {
|
|
window.removeEventListener('resize', updateCursorPositions);
|
|
};
|
|
}, [updateCursorPositions]);
|
|
|
|
|
|
return cursors;
|
|
}
|