/** * Utility functions for hand detection and drawing */ /** * Draw hand landmarks on canvas * @param {CanvasRenderingContext2D} ctx - Canvas context * @param {Array} landmarks - Hand landmarks from MediaPipe * @param {HTMLCanvasElement} canvas - Canvas element * @param {boolean} isMobile - Whether the device is mobile */ export const drawLandmarks = (ctx, landmarks, canvas, isMobile) => { // Draw connections const connections = [ // Thumb [0, 1], [1, 2], [2, 3], [3, 4], // Index finger [0, 5], [5, 6], [6, 7], [7, 8], // Middle finger [9, 10], [10, 11], [11, 12], [5, 9], // Ring finger [9, 13], [13, 14], [14, 15], [15, 16], // Pinky [13, 17], [17, 18], [18, 19], [19, 20], // Palm [0, 17] ]; // Use the requested colors ctx.lineWidth = isMobile ? 2 : 3; // Thinner lines on mobile ctx.strokeStyle = '#217BFE'; // Primary hand color for (const [start, end] of connections) { ctx.beginPath(); ctx.moveTo(landmarks[start].x * canvas.width, landmarks[start].y * canvas.height); ctx.lineTo(landmarks[end].x * canvas.width, landmarks[end].y * canvas.height); ctx.stroke(); } // Draw landmarks with secondary color ctx.fillStyle = '#AC87EB'; landmarks.forEach(landmark => { ctx.beginPath(); ctx.arc(landmark.x * canvas.width, landmark.y * canvas.height, isMobile ? 3 : 4, 0, 2 * Math.PI); ctx.fill(); }); // Highlight thumb and index finger const thumbTip = landmarks[4]; const indexTip = landmarks[8]; ctx.fillStyle = '#217BFE'; [thumbTip, indexTip].forEach(tip => { ctx.beginPath(); ctx.arc(tip.x * canvas.width, tip.y * canvas.height, isMobile ? 4 : 6, 0, 2 * Math.PI); ctx.fill(); }); }; /** * Check if hand is open (thumb away from index finger) * @param {Array} landmarks - Hand landmarks from MediaPipe * @returns {Object} - Object containing isOpen and other hand information */ export const analyzeHandGesture = (landmarks) => { const thumbTip = landmarks[4]; // Thumb tip const indexTip = landmarks[8]; // Index finger tip const wrist = landmarks[0]; // Wrist // Determine if it's a left or right hand // If thumb is to the left of the wrist, it's likely a right hand const isLeftHand = !(thumbTip.x < wrist.x); // Calculate distance between thumb and index finger const distance = Math.sqrt( Math.pow(thumbTip.x - indexTip.x, 2) + Math.pow(thumbTip.y - indexTip.y, 2) + Math.pow(thumbTip.z - indexTip.z, 2) ); // Hand is open when distance is greater than threshold const isOpen = distance > 0.1; return { isOpen, isLeftHand, thumbPosition: { x: thumbTip.x, y: thumbTip.y } }; }; /** * Create animation keyframes for hand detection animations * @returns {string} - CSS keyframes string */ export const getAnimationKeyframes = () => { return ` @keyframes thinking-blink { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.7; transform: scale(0.95); } } @keyframes spring-out { 0% { transform: scale(0) translateY(0); opacity: 0; } 20% { transform: scale(0.3) translateY(-5px); opacity: 0.7; } 40% { transform: scale(1.5) translateY(-30px); } 60% { transform: scale(0.8) translateY(15px); } 75% { transform: scale(1.2) translateY(-10px); } 90% { transform: scale(0.95) translateY(5px); } 100% { transform: scale(1) translateY(0); opacity: 1; } } @keyframes wiggle { 0% { transform: rotate(0deg); } 15% { transform: rotate(-15deg); } 30% { transform: rotate(12deg); } 45% { transform: rotate(-8deg); } 60% { transform: rotate(5deg); } 75% { transform: rotate(-2deg); } 100% { transform: rotate(0deg); } } /* Combined animation that handles both scale and rotation */ @keyframes spring-wiggle { 0% { transform: scale(0) rotate(0deg) translateY(0); opacity: 0; } 15% { transform: scale(0.2) rotate(-5deg) translateY(-5px); opacity: 0.5; } 30% { transform: scale(1.5) rotate(12deg) translateY(-30px); opacity: 1; } 45% { transform: scale(0.8) rotate(-8deg) translateY(15px); } 60% { transform: scale(1.2) rotate(5deg) translateY(-10px); } 75% { transform: scale(0.95) rotate(-2deg) translateY(5px); } 90% { transform: scale(1.05) rotate(1deg) translateY(-2px); } 100% { transform: scale(1) rotate(0deg) translateY(0); } } /* Add new animations for particles and popping */ @keyframes float-particle { 0% { transform: translate(0, 0) rotate(0deg); opacity: 1; } 100% { transform: translate(var(--tx), var(--ty)) rotate(var(--r)); opacity: 0; } } @keyframes pop-out { 0% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.2); } 100% { transform: scale(0); opacity: 0; } } `; };