Spaces:
Running
Running
| // 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); |