document.addEventListener('DOMContentLoaded', () => { const canvas = document.getElementById('robot-canvas'); if (!canvas) return; const ctx = canvas.getContext('2d'); const dpr = window.devicePixelRatio || 1; canvas.width = 280 * dpr; canvas.height = 320 * dpr; ctx.scale(dpr, dpr); let mouse = { x: 0, y: 0 }; let target = { x: 0, y: 0 }; let blink = 0; let time = 0; let clickCount = 0; let lastClick = 0; let angryUntil = 0; let collapseUntil = 0; let laughUntil = 0; let mouthMode = 'smile'; let mouthUntil = 0; const updateTarget = (clientX, clientY) => { const rect = canvas.getBoundingClientRect(); const cx = rect.left + rect.width / 2; const cy = rect.top + rect.height / 2; target.x = (clientX - cx) / (rect.width / 2); target.y = (clientY - cy) / (rect.height / 2); }; canvas.addEventListener('mousemove', (e) => { updateTarget(e.clientX, e.clientY); }); canvas.addEventListener('mouseleave', () => { target.x = 0; target.y = 0; }); function draw() { time += 0.05; mouse.x += (target.x - mouse.x) * 0.1; mouse.y += (target.y - mouse.y) * 0.1; const w = 280; const h = 320; const cx = w / 2; const cy = h / 2 + 20; ctx.clearRect(0, 0, w, h); const now = Date.now(); if (now < collapseUntil) { canvas.style.opacity = '0'; requestAnimationFrame(draw); return; } canvas.style.opacity = '1'; const isAngry = now < angryUntil; const isLaughing = now < laughUntil; canvas.classList.toggle('robot-canvas-angry', isAngry); ctx.fillStyle = 'rgba(0,0,0,0.1)'; ctx.beginPath(); ctx.ellipse(cx, cy + 90, 40 + (Math.sin(time) * 5), 10, 0, 0, Math.PI * 2); ctx.fill(); const floatY = Math.sin(time) * 8; const bodyX = cx + mouse.x * 5; const bodyY = cy + floatY + 30; let bodyGrad = ctx.createRadialGradient(bodyX - 20, bodyY - 20, 10, bodyX, bodyY, 60); bodyGrad.addColorStop(0, '#ffffff'); bodyGrad.addColorStop(1, '#d1d9e6'); ctx.fillStyle = bodyGrad; ctx.beginPath(); ctx.ellipse(bodyX, bodyY, 50, 45, 0, 0, Math.PI * 2); ctx.fill(); const logoX = bodyX + mouse.x * 10; const logoY = bodyY + mouse.y * 5; ctx.fillStyle = '#25d366'; ctx.beginPath(); ctx.arc(logoX, logoY, 15, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'white'; ctx.beginPath(); ctx.arc(logoX - 3, logoY - 3, 4, 0, Math.PI * 2); ctx.fill(); const headX = cx + mouse.x * 25; const headY = cy + floatY - 50 + (mouse.y * 15); let headGrad = ctx.createRadialGradient(headX - 30, headY - 20, 20, headX, headY, 80); headGrad.addColorStop(0, '#ffffff'); headGrad.addColorStop(1, '#cbd5e1'); ctx.fillStyle = headGrad; ctx.beginPath(); ctx.roundRect(headX - 60, headY - 45, 120, 90, 40); ctx.fill(); const faceX = headX + mouse.x * 10; const faceY = headY + mouse.y * 8; const faceHeight = 60; ctx.fillStyle = '#0f172a'; ctx.beginPath(); ctx.roundRect(faceX - 45, faceY - (faceHeight / 2), 90, faceHeight, 22); ctx.fill(); if (Math.random() > 0.98) blink = 1; if (blink > 0) blink -= 0.2; const eyeY = faceY + (mouse.y * 5) - 2; const eyeColor = isAngry ? '#ff4d4d' : '#25d366'; ctx.fillStyle = eyeColor; ctx.shadowBlur = 10; ctx.shadowColor = eyeColor; if (blink <= 0.2) { ctx.beginPath(); ctx.ellipse(faceX - 22 + (mouse.x * 5), eyeY, 7, 10, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(faceX + 22 + (mouse.x * 5), eyeY, 7, 10, 0, 0, Math.PI * 2); ctx.fill(); } ctx.shadowBlur = 0; if (Date.now() > mouthUntil) mouthMode = 'smile'; ctx.strokeStyle = eyeColor; ctx.lineWidth = 4; const mouthFloat = mouse.y * 6; const baseY = faceY + 14 + mouthFloat; if (mouthMode === 'sad') { ctx.beginPath(); ctx.moveTo(faceX - 18, baseY + 8); ctx.quadraticCurveTo(faceX, baseY - 2, faceX + 18, baseY + 8); ctx.stroke(); } else if (mouthMode === 'wow') { ctx.beginPath(); ctx.arc(faceX, baseY + 4, 6, 0, Math.PI * 2); ctx.stroke(); } else if (mouthMode === 'oh') { ctx.beginPath(); ctx.arc(faceX, baseY + 4, 4, 0, Math.PI * 2); ctx.stroke(); } else { const smileDepth = (isLaughing ? 12 : 8) + Math.abs(mouse.y * 4); ctx.beginPath(); ctx.moveTo(faceX - 18, baseY); ctx.quadraticCurveTo(faceX, baseY + smileDepth, faceX + 18, baseY); ctx.stroke(); } ctx.strokeStyle = '#cbd5e1'; ctx.lineWidth = 6; ctx.beginPath(); ctx.moveTo(headX - 40, headY - 40); ctx.lineTo(headX - 55, headY - 65); ctx.moveTo(headX + 40, headY - 40); ctx.lineTo(headX + 55, headY - 65); ctx.stroke(); requestAnimationFrame(draw); } draw(); canvas.addEventListener('click', () => { const now = Date.now(); if (now - lastClick > 1500) clickCount = 0; clickCount += 1; lastClick = now; const funTexts = ['You tickle me', 'Haha', 'Nice', 'Okay', 'Wow', 'Ah']; const angryTexts = ['Enough!', 'I\'m angry', 'Stop', 'Sad']; const collapseTexts = ['Ouch!', 'I\'m done']; if (clickCount >= 8) { angryUntil = now + 1200; collapseUntil = now + 1200; clickCount = 0; canvas.classList.add('robot-canvas-collapse'); setTimeout(() => { canvas.classList.remove('robot-canvas-collapse'); canvas.classList.remove('robot-canvas-reappear'); void canvas.offsetWidth; canvas.classList.add('robot-canvas-reappear'); emitBubbles(14, 'burst'); setTimeout(() => canvas.classList.remove('robot-canvas-reappear'), 600); }, 1200); emitBubbles(18, 'burst'); mouthMode = 'oh'; mouthUntil = now + 1400; emitPop(collapseTexts[Math.floor(Math.random() * collapseTexts.length)], 1400); return; } if (clickCount >= 5) { angryUntil = now + 1500; mouthMode = 'sad'; mouthUntil = now + 1500; emitBubbles(8, 'angry'); emitPop(angryTexts[Math.floor(Math.random() * angryTexts.length)], 1500); } else { laughUntil = now + 1200; mouthMode = Math.random() > 0.6 ? 'wow' : 'smile'; mouthUntil = now + 1200; emitBubbles(4); emitPop(funTexts[Math.floor(Math.random() * funTexts.length)], 1200); } canvas.classList.remove('robot-canvas-fun'); void canvas.offsetWidth; canvas.classList.add('robot-canvas-fun'); setTimeout(() => canvas.classList.remove('robot-canvas-fun'), 600); }); function emitPop(text, duration) { const card = canvas.closest('.robot-card'); if (!card) return; const pop = document.createElement('span'); pop.className = 'robot-pop'; pop.textContent = text; card.appendChild(pop); setTimeout(() => pop.remove(), duration || 1200); } function emitBubbles(count, tone) { const card = canvas.closest('.robot-card'); if (!card) return; const rect = card.getBoundingClientRect(); const centerX = rect.width / 2; const centerY = rect.height / 2; const radius = Math.min(rect.width, rect.height) * 0.48; for (let i = 0; i < count; i += 1) { const bubble = document.createElement('span'); bubble.className = tone === 'angry' ? 'robot-bubble robot-bubble-angry' : 'robot-bubble'; if (tone === 'burst') { const angle = (Math.PI * 2 * i) / count + Math.random() * 0.2; bubble.style.left = `${centerX + Math.cos(angle) * radius}px`; bubble.style.top = `${centerY + Math.sin(angle) * radius}px`; } else { bubble.style.left = `${40 + Math.random() * 40}%`; bubble.style.top = `${30 + Math.random() * 40}%`; } bubble.style.width = `${8 + Math.random() * 8}px`; bubble.style.height = bubble.style.width; card.appendChild(bubble); setTimeout(() => bubble.remove(), 900); } } });