// ═══════════════════════════════════════════════════════════════════════ // Mouse Basic Activity - Logic // ═══════════════════════════════════════════════════════════════════════ // ── postMessage helpers ────────────────────────────────────────────── const channelToken = new URLSearchParams(window.location.hash.slice(1)).get('channelToken'); function notify(type, payload = {}) { window.parent.postMessage({ type, token: channelToken, ...payload }, '*'); } // ── DOM Elements ───────────────────────────────────────────────────── const arena = document.getElementById('arena'); const instrEl = document.getElementById('instruction'); const hintEl = document.getElementById('hint'); const coverWrap = document.getElementById('coverageWrap'); const coverBar = document.getElementById('coverageBar'); const progLabel = document.getElementById('progressLabel'); const banner = document.getElementById('successBanner'); const successMsg = document.getElementById('successMsg'); // ── State ──────────────────────────────────────────────────────────── const TOTAL_STEPS = 3; let currentStep = 0; let started = false; const steps = [ { instruction: 'Passo 1 de 3: Mova o mouse pela área abaixo', hint: 'Explore toda a área movendo o mouse. Preencha pelo menos 60% dela.', setup: setupMoveStep, }, { instruction: 'Passo 2 de 3: Clique no botão', hint: 'Mova o cursor até o botão e clique uma vez.', setup: setupClickStep, }, { instruction: 'Passo 3 de 3: Dê um duplo clique no botão', hint: 'Dois cliques rápidos sobre o botão!', setup: setupDblClickStep, }, ]; // ── Rendering ──────────────────────────────────────────────────────── function renderStep(step) { const s = steps[step]; instrEl.textContent = s.instruction; hintEl.textContent = s.hint; clearArena(); s.setup(); } function clearArena() { arena.innerHTML = ''; arena.style.cursor = 'default'; } function nextStep(feedbackMsg) { if (!started) { notify('started', { step: currentStep + 1 }); started = true; } notify('running', { step: currentStep + 1, message: feedbackMsg }); currentStep++; setTimeout(() => { if (currentStep >= TOTAL_STEPS) { finishActivity(); } else { renderStep(currentStep); } }, 1200); } function finishActivity() { instrEl.textContent = 'Você completou todos os passos!'; hintEl.textContent = ''; clearArena(); arena.classList.add('hidden'); coverWrap.classList.add('hidden'); successMsg.textContent = 'Agora você sabe como mover e clicar com o mouse. Excelente trabalho!'; banner.classList.remove('hidden'); // Reinitialize icons in success banner lucide.createIcons(); notify('success', { score: 100 }); notify('completed', { score: 100 }); } // ═══════════════════════════════════════════════════════════════════════ // STEP 1: Move Mouse & Track Coverage // ═══════════════════════════════════════════════════════════════════════ function setupMoveStep() { coverWrap.classList.remove('hidden'); arena.style.cursor = 'crosshair'; const GRID_SIZE = 32; const cols = Math.ceil(arena.clientWidth / GRID_SIZE); const rows = Math.ceil(arena.clientHeight / GRID_SIZE); const totalCells = cols * rows; const visitedCells = new Set(); arena.addEventListener('mousemove', handleMouseMove); function handleMouseMove(e) { const rect = arena.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Track visited cells const col = Math.floor(x / GRID_SIZE); const row = Math.floor(y / GRID_SIZE); const cellId = `${col},${row}`; visitedCells.add(cellId); const coverage = (visitedCells.size / totalCells) * 100; coverBar.style.width = `${coverage}%`; progLabel.textContent = `${Math.floor(coverage)}%`; // Create cursor trail const trail = document.createElement('div'); trail.className = 'absolute w-8 h-8 rounded-full bg-red-400/30 pointer-events-none'; trail.style.left = `${x}px`; trail.style.top = `${y}px`; trail.style.transform = 'translate(-50%, -50%)'; arena.appendChild(trail); setTimeout(() => trail.remove(), 800); // Success condition if (coverage >= 60) { arena.removeEventListener('mousemove', handleMouseMove); nextStep('Ótimo! Você explorou a área com sucesso!'); } } } // ═══════════════════════════════════════════════════════════════════════ // STEP 2: Single Click // ═══════════════════════════════════════════════════════════════════════ function setupClickStep() { arena.style.cursor = 'default'; placeTarget(false); } // ═══════════════════════════════════════════════════════════════════════ // STEP 3: Double Click // ═══════════════════════════════════════════════════════════════════════ function setupDblClickStep() { arena.style.cursor = 'default'; placeTarget(true); } function placeTarget(isDblClick) { const SIZE = 140; const maxX = arena.clientWidth - SIZE - 20; const maxY = arena.clientHeight - SIZE - 20; const x = Math.floor(Math.random() * maxX) + 10; const y = Math.floor(Math.random() * maxY) + 10; const button = document.createElement('button'); button.className = 'absolute bg-green-500 hover:bg-green-600 active:scale-95 text-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-200 flex items-center justify-center gap-3 text-2xl font-bold'; button.style.width = `${SIZE}px`; button.style.height = `${SIZE}px`; button.style.left = `${x}px`; button.style.top = `${y}px`; // Add Lucide icon const icon = document.createElement('i'); icon.setAttribute('data-lucide', isDblClick ? 'pointer' : 'hand'); icon.className = 'w-16 h-16'; button.appendChild(icon); arena.appendChild(button); // Initialize the icon lucide.createIcons(); if (isDblClick) { button.addEventListener('dblclick', () => { button.remove(); nextStep('Incrível! Você aprendeu o duplo clique!'); }); // Mobile fallback: two taps let tapCount = 0; let tapTimer; button.addEventListener('click', (e) => { tapCount++; if (tapCount === 1) { tapTimer = setTimeout(() => { tapCount = 0; }, 400); } else if (tapCount === 2) { clearTimeout(tapTimer); button.remove(); nextStep('Incrível! Você aprendeu o duplo clique!'); } e.stopPropagation(); }); } else { button.addEventListener('click', () => { button.remove(); nextStep('Perfeito! Você clicou no alvo!'); }); } } // ── Initialize ─────────────────────────────────────────────────────── renderStep(0);