suanan's picture
繁體中文介面
e018666 verified
// 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);