// Login Form Handling document.addEventListener('DOMContentLoaded', function() { const loginForm = document.getElementById('loginForm'); const loginButton = document.getElementById('loginButton'); const loadingOverlay = document.getElementById('loadingOverlay'); const successAnimation = document.getElementById('successAnimation'); // Add ripple effect to button loginButton.addEventListener('click', function(e) { if (loginForm.checkValidity()) { createRipple(e); } }); // Form submission handler loginForm.addEventListener('submit', function(e) { e.preventDefault(); if (loginForm.checkValidity()) { // Show loading animation loadingOverlay.classList.remove('hidden'); // Simulate API call setTimeout(() => { loadingOverlay.classList.add('hidden'); successAnimation.classList.remove('hidden'); // Simulate successful login and redirect setTimeout(() => { successAnimation.classList.add('hidden'); // In a real application, you would redirect to the dashboard console.log('登入成功!正在跳轉到儀表板...'); // window.location.href = '/dashboard'; }, 1500); }, 2000); } }); // Input field animations const inputs = document.querySelectorAll('input'); inputs.forEach(input => { input.addEventListener('focus', function() { this.parentElement.classList.add('input-focused'); }); input.addEventListener('blur', function() { this.parentElement.classList.remove('input-focused'); }); }); // Add keyboard navigation document.addEventListener('keydown', function(e) { if (e.key === 'Enter' && loginForm.checkValidity()) { loginForm.dispatchEvent(new Event('submit')); } }); }); // Ripple Effect Function function createRipple(event) { const button = event.currentTarget; const circle = document.createElement('span'); const diameter = Math.max(button.clientWidth, button.clientHeight); const radius = diameter / 2; circle.style.width = circle.style.height = `${diameter}px`; circle.style.left = `${event.clientX - button.getBoundingClientRect().left - radius}px`; circle.style.top = `${event.clientY - button.getBoundingClientRect().top - radius}px`; circle.classList.add('ripple'); const ripple = button.getElementsByClassName('ripple')[0]; if (ripple) { ripple.remove(); } button.appendChild(circle); } // Add ripple styles dynamically const style = document.createElement('style'); style.textContent = ` .ripple { position: absolute; border-radius: 50%; background: rgba(255, 255, 255, 0.6); transform: scale(0); animation: ripple-animation 0.6s linear; } @keyframes ripple-animation { to { transform: scale(4); opacity: 0; } } .input-focused .input-highlight { width: 100%; } `; document.head.appendChild(style); // Form validation enhancements function validateForm() { const username = document.getElementById('username'); const password = document.getElementById('password'); let isValid = true; if (!username.value.trim()) { showError(username, '用戶名是必填項'); isValid = false; } if (!password.value.trim()) { showError(password, '密碼是必填項'); isValid = false; } return isValid; } function showError(input, message) { const parent = input.parentElement; const errorDiv = document.createElement('div'); errorDiv.className = 'text-red-300 text-xs mt-1'; errorDiv.textContent = message; // Remove existing error const existingError = parent.querySelector('.text-red-300'); if (existingError) { existingError.remove(); } parent.appendChild(errorDiv); // Add shake animation input.classList.add('animate-shake'); setTimeout(() => { input.classList.remove('animate-shake'); }, 500); } // Add shake animation for errors const shakeStyle = document.createElement('style'); shakeStyle.textContent = ` @keyframes shake { 0%, 100% { transform: translateX(0); } 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } 20%, 40%, 60%, 80% { transform: translateX(5px); } } .animate-shake { animation: shake 0.5s ease-in-out; } `; document.head.appendChild(shakeStyle);