Files
2026-04-02 00:59:42 +08:00

399 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 个人作品展示网站 - 主JavaScript文件
// 文件位置: portfolio/js/main.js
// 创建时间: 2026-03-31
document.addEventListener('DOMContentLoaded', function() {
// 初始化所有功能
initNavigation();
initSmoothScroll();
initContactForm();
initBackToTop();
initSkillAnimations();
initProjectCards();
console.log('个人作品展示网站已加载完成!');
});
// 导航功能
function initNavigation() {
const menuToggle = document.querySelector('.menu-toggle');
const navLinks = document.querySelector('.nav-links');
const navItems = document.querySelectorAll('.nav-link');
// 菜单切换
if (menuToggle) {
menuToggle.addEventListener('click', function() {
navLinks.classList.toggle('active');
menuToggle.classList.toggle('active');
});
}
// 导航项点击
navItems.forEach(item => {
item.addEventListener('click', function(e) {
// 关闭移动端菜单
if (window.innerWidth <= 1023) {
navLinks.classList.remove('active');
menuToggle.classList.remove('active');
}
// 更新活动状态
navItems.forEach(nav => nav.classList.remove('active'));
this.classList.add('active');
});
});
// 滚动时更新活动导航项
window.addEventListener('scroll', updateActiveNav);
}
// 平滑滚动
function initSmoothScroll() {
const links = document.querySelectorAll('a[href^="#"]');
links.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
const headerHeight = document.querySelector('.navbar').offsetHeight;
const targetPosition = targetElement.offsetTop - headerHeight;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
}
// 联系表单处理
function initContactForm() {
const contactForm = document.getElementById('contactForm');
if (contactForm) {
contactForm.addEventListener('submit', function(e) {
e.preventDefault();
// 获取表单数据
const formData = new FormData(this);
const data = Object.fromEntries(formData);
// 简单验证
if (!validateForm(data)) {
return;
}
// 显示加载状态
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.textContent = '发送中...';
submitBtn.disabled = true;
// 模拟API调用
setTimeout(() => {
// 在实际应用中,这里会发送到服务器
console.log('表单数据:', data);
// 显示成功消息
showFormMessage('消息发送成功!我会尽快回复您。', 'success');
// 重置表单
contactForm.reset();
// 恢复按钮状态
submitBtn.textContent = originalText;
submitBtn.disabled = false;
}, 1500);
});
}
// 表单验证
function validateForm(data) {
const errors = [];
if (!data.name || data.name.trim().length < 2) {
errors.push('请输入有效的姓名至少2个字符');
}
if (!data.email || !isValidEmail(data.email)) {
errors.push('请输入有效的邮箱地址');
}
if (!data.subject || data.subject.trim().length < 3) {
errors.push('请输入主题至少3个字符');
}
if (!data.message || data.message.trim().length < 10) {
errors.push('请输入消息内容至少10个字符');
}
if (errors.length > 0) {
showFormMessage(errors.join('<br>'), 'error');
return false;
}
return true;
}
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function showFormMessage(message, type) {
// 移除旧的消息
const oldMessage = document.querySelector('.form-message');
if (oldMessage) {
oldMessage.remove();
}
// 创建新消息
const messageDiv = document.createElement('div');
messageDiv.className = `form-message ${type}`;
messageDiv.innerHTML = message;
// 添加到表单前
const form = document.getElementById('contactForm');
form.parentNode.insertBefore(messageDiv, form);
// 5秒后自动移除
setTimeout(() => {
messageDiv.remove();
}, 5000);
}
}
// 返回顶部按钮
function initBackToTop() {
const backToTopBtn = document.getElementById('backToTop');
if (backToTopBtn) {
// 显示/隐藏按钮
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
backToTopBtn.style.display = 'block';
setTimeout(() => {
backToTopBtn.style.opacity = '1';
}, 10);
} else {
backToTopBtn.style.opacity = '0';
setTimeout(() => {
backToTopBtn.style.display = 'none';
}, 300);
}
});
// 点击返回顶部
backToTopBtn.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
}
// 技能条动画
function initSkillAnimations() {
const skillBars = document.querySelectorAll('.skill-level');
// 创建Intersection Observer来检测技能部分是否在视口中
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 当技能部分进入视口时,触发动画
skillBars.forEach(bar => {
const width = bar.style.width;
bar.style.width = '0';
setTimeout(() => {
bar.style.width = width;
}, 100);
});
// 停止观察,避免重复触发
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.5 // 当50%的元素可见时触发
});
// 观察技能部分
const skillsSection = document.getElementById('skills');
if (skillsSection) {
observer.observe(skillsSection);
}
}
// 项目卡片交互
function initProjectCards() {
const projectCards = document.querySelectorAll('.project-card');
projectCards.forEach(card => {
// 点击卡片查看详情
card.addEventListener('click', function(e) {
// 防止点击链接时触发卡片点击
if (e.target.tagName === 'A') return;
// 在实际应用中,这里会打开项目详情模态框
console.log('查看项目详情:', this.querySelector('h3').textContent);
});
// 键盘导航支持
card.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
console.log('查看项目详情:', this.querySelector('h3').textContent);
}
});
// 设置卡片可聚焦
card.setAttribute('tabindex', '0');
});
}
// 更新活动导航项
function updateActiveNav() {
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav-link');
const scrollPosition = window.pageYOffset + 100;
sections.forEach(section => {
const sectionTop = section.offsetTop;
const sectionHeight = section.offsetHeight;
const sectionId = section.getAttribute('id');
if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) {
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${sectionId}`) {
link.classList.add('active');
}
});
}
});
}
// 性能优化:延迟加载图片
function initLazyLoading() {
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
}
// 添加CSS样式到页面
function addDynamicStyles() {
const styles = `
.form-message {
padding: 12px 16px;
margin-bottom: 20px;
border-radius: 8px;
font-weight: 500;
}
.form-message.success {
background-color: rgba(76, 175, 80, 0.1);
color: #4caf50;
border: 1px solid #4caf50;
}
.form-message.error {
background-color: rgba(244, 67, 54, 0.1);
color: #f44336;
border: 1px solid #f44336;
}
.back-to-top {
display: none;
opacity: 0;
transition: opacity 0.3s ease;
background-color: var(--color-highlight);
color: white;
border: none;
border-radius: 50%;
cursor: pointer;
font-size: 1.2rem;
z-index: 1000;
}
.back-to-top:hover {
background-color: #ff2e4f;
}
.project-card {
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.project-card:focus {
outline: 2px solid var(--color-highlight);
outline-offset: 2px;
}
img[data-src] {
opacity: 0;
transition: opacity 0.3s ease;
}
img.loaded {
opacity: 1;
}
`;
const styleSheet = document.createElement('style');
styleSheet.textContent = styles;
document.head.appendChild(styleSheet);
}
// 初始化动态样式
addDynamicStyles();
// 性能监控
if (typeof PerformanceObserver !== 'undefined') {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`${entry.name}: ${entry.duration}ms`);
}
});
observer.observe({ entryTypes: ['measure'] });
}
// 错误处理
window.addEventListener('error', function(e) {
console.error('JavaScript错误:', e.message, 'at', e.filename, ':', e.lineno);
// 在实际应用中,这里可以发送错误到监控服务
// sendErrorToMonitoring(e);
});
// PWA支持基础
if ('serviceWorker' in navigator && window.location.protocol === 'https:') {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('ServiceWorker 注册成功:', registration.scope);
}).catch(function(error) {
console.log('ServiceWorker 注册失败:', error);
});
});
}