362 lines
11 KiB
JavaScript
362 lines
11 KiB
JavaScript
// 主页增强功能 - 无彩蛋版本
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
|
||
// 页面加载动画
|
||
document.body.classList.add('page-loading');
|
||
setTimeout(() => {
|
||
document.body.classList.remove('page-loading');
|
||
}, 100);
|
||
|
||
// 数字统计动画
|
||
animateCounters();
|
||
|
||
// 场景分类筛选
|
||
initScenarioFilter();
|
||
|
||
// 滚动动画
|
||
initScrollAnimations();
|
||
|
||
// 轮播图增强
|
||
enhanceCarousel();
|
||
|
||
// 功能卡片交互
|
||
enhanceFeatureCards();
|
||
|
||
console.log('🎨 主页功能已加载完成!');
|
||
});
|
||
|
||
// 数字统计动画
|
||
function animateCounters() {
|
||
const counters = document.querySelectorAll('.stat-number');
|
||
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
const counter = entry.target;
|
||
const target = parseInt(counter.getAttribute('data-count'));
|
||
animateCounter(counter, target);
|
||
observer.unobserve(counter);
|
||
}
|
||
});
|
||
});
|
||
|
||
counters.forEach(counter => observer.observe(counter));
|
||
}
|
||
|
||
function animateCounter(element, target) {
|
||
let count = 0;
|
||
const duration = 2000; // 2秒
|
||
const step = target / (duration / 16); // 60fps
|
||
|
||
const timer = setInterval(() => {
|
||
count += step;
|
||
if (count >= target) {
|
||
count = target;
|
||
clearInterval(timer);
|
||
}
|
||
element.textContent = Math.floor(count);
|
||
}, 16);
|
||
}
|
||
|
||
// 场景分类筛选
|
||
function initScenarioFilter() {
|
||
const navButtons = document.querySelectorAll('.scenario-nav-btn');
|
||
const scenarioCards = document.querySelectorAll('.scenario-card-new');
|
||
|
||
navButtons.forEach(btn => {
|
||
btn.addEventListener('click', function() {
|
||
// 更新激活状态
|
||
navButtons.forEach(b => b.classList.remove('active'));
|
||
this.classList.add('active');
|
||
|
||
// 筛选场景
|
||
const category = this.getAttribute('data-category');
|
||
|
||
scenarioCards.forEach(card => {
|
||
if (category === 'all' || card.getAttribute('data-category') === category) {
|
||
card.style.display = 'block';
|
||
card.style.animation = 'fadeInUp 0.5s ease forwards';
|
||
} else {
|
||
card.style.display = 'none';
|
||
}
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
// 滚动动画
|
||
function initScrollAnimations() {
|
||
const observerOptions = {
|
||
threshold: 0.1,
|
||
rootMargin: '0px 0px -50px 0px'
|
||
};
|
||
|
||
const observer = new IntersectionObserver((entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('animate-in');
|
||
|
||
// 为子元素添加延迟动画
|
||
const children = entry.target.querySelectorAll('.stat-card, .feature-card-advanced, .step-item');
|
||
children.forEach((child, index) => {
|
||
setTimeout(() => {
|
||
child.classList.add('fade-in');
|
||
}, index * 100);
|
||
});
|
||
}
|
||
});
|
||
}, observerOptions);
|
||
|
||
// 观察需要动画的元素
|
||
document.querySelectorAll('.stats-showcase, .learning-steps, .scenarios-grid').forEach(el => {
|
||
observer.observe(el);
|
||
});
|
||
}
|
||
|
||
// 轮播图增强
|
||
function enhanceCarousel() {
|
||
const carousel = document.querySelector('#heroCarousel');
|
||
if (!carousel) return;
|
||
|
||
// 增强指示器点击效果
|
||
document.querySelectorAll('.carousel-indicators button').forEach(indicator => {
|
||
indicator.addEventListener('click', function() {
|
||
this.style.transform = 'scale(1.5)';
|
||
setTimeout(() => {
|
||
this.style.transform = '';
|
||
}, 200);
|
||
});
|
||
});
|
||
|
||
// 控制器悬停效果
|
||
document.querySelectorAll('.kid-control').forEach(control => {
|
||
control.addEventListener('mouseenter', function() {
|
||
this.style.transform = 'scale(1.1)';
|
||
});
|
||
|
||
control.addEventListener('mouseleave', function() {
|
||
this.style.transform = 'scale(1)';
|
||
});
|
||
});
|
||
}
|
||
|
||
// 功能卡片交互增强
|
||
function enhanceFeatureCards() {
|
||
// 语音波浪动画控制
|
||
const waveDemo = document.querySelector('.voice-wave-demo');
|
||
if (waveDemo) {
|
||
const waveCard = waveDemo.closest('.feature-card-advanced');
|
||
|
||
waveCard.addEventListener('mouseenter', function() {
|
||
const bars = waveDemo.querySelectorAll('.wave-bar');
|
||
bars.forEach((bar, index) => {
|
||
bar.style.animationDuration = '0.8s';
|
||
bar.style.animationDelay = (index * 0.1) + 's';
|
||
});
|
||
});
|
||
|
||
waveCard.addEventListener('mouseleave', function() {
|
||
const bars = waveDemo.querySelectorAll('.wave-bar');
|
||
bars.forEach((bar, index) => {
|
||
bar.style.animationDuration = '1.5s';
|
||
bar.style.animationDelay = (index * 0.2) + 's';
|
||
});
|
||
});
|
||
}
|
||
|
||
// 聊天演示动画
|
||
const chatDemo = document.querySelector('.chat-demo');
|
||
if (chatDemo) {
|
||
const aiCard = chatDemo.closest('.feature-card-advanced');
|
||
|
||
aiCard.addEventListener('mouseenter', function() {
|
||
const typingIndicator = chatDemo.querySelector('.typing-indicator');
|
||
if (typingIndicator) {
|
||
typingIndicator.style.animationDuration = '1s';
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 场景预览功能
|
||
window.showPreview = function(scenarioType) {
|
||
const modal = document.getElementById('scenarioPreview');
|
||
const title = document.getElementById('previewTitle');
|
||
const chat = document.getElementById('previewChat');
|
||
|
||
// 预设对话内容
|
||
const dialogues = {
|
||
friend: [
|
||
{ type: 'ai', text: '你好呀,我叫小明!你叫什么名字?' },
|
||
{ type: 'user', text: '你好小明,我叫小红!' },
|
||
{ type: 'ai', text: '很高兴认识你小红!我们可以做朋友吗?' },
|
||
{ type: 'user', text: '当然可以!我也想和你做朋友!' }
|
||
],
|
||
party: [
|
||
{ type: 'ai', text: '小红,下周是我的生日,你愿意来参加我的生日派对吗?' },
|
||
{ type: 'user', text: '哇,真的吗?我很想去!' },
|
||
{ type: 'ai', text: '太好了!派对在下周六下午3点,记得带上好心情哦!' }
|
||
],
|
||
restaurant: [
|
||
{ type: 'ai', text: '欢迎光临!小朋友想吃点什么呢?' },
|
||
{ type: 'user', text: '我想要一份儿童套餐,谢谢!' },
|
||
{ type: 'ai', text: '好的,儿童套餐有汉堡、薯条和果汁,这样可以吗?' }
|
||
],
|
||
shopping: [
|
||
{ type: 'ai', text: '小朋友,你在找什么东西吗?' },
|
||
{ type: 'user', text: '我想买一些水果给妈妈!' },
|
||
{ type: 'ai', text: '真懂事!我们这里有苹果、香蕉和橙子,你想要哪种?' }
|
||
],
|
||
actor: [
|
||
{ type: 'ai', text: '现在你是一只小猫咪,你会怎么介绍自己呢?' },
|
||
{ type: 'user', text: '喵~我是一只可爱的小猫,我喜欢吃鱼和玩毛线球!' },
|
||
{ type: 'ai', text: '表演得太棒了!你还能学其他小动物吗?' }
|
||
],
|
||
story: [
|
||
{ type: 'ai', text: '让我们一起编一个故事吧!从前有一只勇敢的小兔子...' },
|
||
{ type: 'user', text: '这只小兔子住在一个神奇的森林里!' },
|
||
{ type: 'ai', text: '哇!那这个森林里还有什么特别的地方吗?' }
|
||
]
|
||
};
|
||
|
||
const dialogue = dialogues[scenarioType] || dialogues.friend;
|
||
|
||
// 设置标题
|
||
const titles = {
|
||
friend: '👫 和小明交朋友',
|
||
party: '🎂 生日派对邀请',
|
||
restaurant: '🏪 餐厅点餐小达人',
|
||
shopping: '🛍️ 超市购物助手',
|
||
actor: '🎭 小小演员训练营',
|
||
story: '🌈 创意故事大王'
|
||
};
|
||
|
||
title.textContent = titles[scenarioType] || '对话预览';
|
||
|
||
// 清空并重新填充对话
|
||
chat.innerHTML = '';
|
||
|
||
dialogue.forEach((msg, index) => {
|
||
setTimeout(() => {
|
||
const msgDiv = document.createElement('div');
|
||
msgDiv.className = `chat-bubble ${msg.type}-bubble`;
|
||
msgDiv.innerHTML = `
|
||
<span class="avatar">${msg.type === 'ai' ? '🤖' : '😊'}</span>
|
||
<span class="text">${msg.text}</span>
|
||
`;
|
||
chat.appendChild(msgDiv);
|
||
|
||
// 滚动到底部
|
||
chat.scrollTop = chat.scrollHeight;
|
||
}, index * 1000);
|
||
});
|
||
|
||
// 显示弹窗
|
||
modal.style.display = 'flex';
|
||
|
||
// 添加点击外部关闭功能
|
||
modal.addEventListener('click', function(e) {
|
||
if (e.target === modal) {
|
||
closePreview();
|
||
}
|
||
});
|
||
};
|
||
|
||
window.closePreview = function() {
|
||
const modal = document.getElementById('scenarioPreview');
|
||
modal.style.display = 'none';
|
||
};
|
||
|
||
// 按钮点击效果增强
|
||
document.addEventListener('click', function(e) {
|
||
// 为所有按钮添加点击波纹效果
|
||
if (e.target.matches('button, .btn, .btn-start, .btn-preview')) {
|
||
createRipple(e);
|
||
}
|
||
});
|
||
|
||
function createRipple(event) {
|
||
const button = event.target;
|
||
const rect = button.getBoundingClientRect();
|
||
const size = Math.max(rect.width, rect.height);
|
||
const x = event.clientX - rect.left - size / 2;
|
||
const y = event.clientY - rect.top - size / 2;
|
||
|
||
const ripple = document.createElement('span');
|
||
ripple.style.cssText = `
|
||
position: absolute;
|
||
width: ${size}px;
|
||
height: ${size}px;
|
||
left: ${x}px;
|
||
top: ${y}px;
|
||
background: rgba(255, 255, 255, 0.6);
|
||
border-radius: 50%;
|
||
transform: scale(0);
|
||
animation: ripple 0.6s linear;
|
||
pointer-events: none;
|
||
z-index: 1000;
|
||
`;
|
||
|
||
// 确保按钮有相对定位
|
||
if (getComputedStyle(button).position === 'static') {
|
||
button.style.position = 'relative';
|
||
}
|
||
|
||
button.appendChild(ripple);
|
||
|
||
setTimeout(() => {
|
||
ripple.remove();
|
||
}, 600);
|
||
}
|
||
|
||
// 添加CSS动画
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
@keyframes ripple {
|
||
to {
|
||
transform: scale(4);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.fade-in {
|
||
animation: fadeInUp 0.6s ease forwards;
|
||
}
|
||
|
||
.animate-in {
|
||
animation: fadeInUp 0.8s ease forwards;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
// 性能优化:节流函数
|
||
function throttle(func, wait) {
|
||
let timeout;
|
||
return function executedFunction(...args) {
|
||
const later = () => {
|
||
clearTimeout(timeout);
|
||
func(...args);
|
||
};
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(later, wait);
|
||
};
|
||
}
|
||
|
||
// 滚动性能优化
|
||
const handleScroll = throttle(() => {
|
||
// 滚动相关的性能敏感操作
|
||
}, 100);
|
||
|
||
window.addEventListener('scroll', handleScroll);
|