502 lines
15 KiB
JavaScript
502 lines
15 KiB
JavaScript
// 文旅主题动画和交互效果
|
|
|
|
// 初始化函数
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
initMountainBackground();
|
|
initCloudAnimation();
|
|
initBambooLeaves();
|
|
initScrollReveal();
|
|
initMapNavigation();
|
|
initJourneyTimeline();
|
|
initInkTransition();
|
|
initProjectCards();
|
|
initParallaxEffect();
|
|
initChineseScrollEffect();
|
|
});
|
|
|
|
// 创建动态山水背景
|
|
function initMountainBackground() {
|
|
const body = document.body;
|
|
|
|
// 创建山水画背景容器
|
|
const mountainBg = document.createElement('div');
|
|
mountainBg.className = 'mountain-bg';
|
|
|
|
// 创建三层山峦
|
|
const layers = ['back', 'mid', 'front'];
|
|
layers.forEach(layer => {
|
|
const mountainLayer = document.createElement('div');
|
|
mountainLayer.className = `mountain-layer ${layer}`;
|
|
mountainBg.appendChild(mountainLayer);
|
|
});
|
|
|
|
body.insertBefore(mountainBg, body.firstChild);
|
|
}
|
|
|
|
// 云雾飘动效果
|
|
function initCloudAnimation() {
|
|
const cloudContainer = document.createElement('div');
|
|
cloudContainer.className = 'cloud-container';
|
|
|
|
// 创建多个云朵
|
|
for (let i = 0; i < 3; i++) {
|
|
const cloud = document.createElement('div');
|
|
cloud.className = 'cloud';
|
|
cloud.style.left = `${Math.random() * -200}px`;
|
|
cloudContainer.appendChild(cloud);
|
|
}
|
|
|
|
document.body.appendChild(cloudContainer);
|
|
}
|
|
|
|
// 竹叶飘落效果
|
|
function initBambooLeaves() {
|
|
const leavesContainer = document.createElement('div');
|
|
leavesContainer.className = 'bamboo-leaves';
|
|
|
|
// 创建飘落的竹叶
|
|
function createLeaf() {
|
|
const leaf = document.createElement('div');
|
|
leaf.className = 'leaf';
|
|
leaf.style.left = `${Math.random() * window.innerWidth}px`;
|
|
leaf.style.animationDelay = `${Math.random() * 15}s`;
|
|
leaf.style.animationDuration = `${15 + Math.random() * 10}s`;
|
|
leavesContainer.appendChild(leaf);
|
|
|
|
// 动画结束后移除叶子
|
|
setTimeout(() => {
|
|
leaf.remove();
|
|
}, 25000);
|
|
}
|
|
|
|
// 定期创建新叶子
|
|
setInterval(createLeaf, 3000);
|
|
|
|
// 初始创建一些叶子
|
|
for (let i = 0; i < 5; i++) {
|
|
setTimeout(createLeaf, i * 1000);
|
|
}
|
|
|
|
document.body.appendChild(leavesContainer);
|
|
}
|
|
|
|
// 卷轴展开效果
|
|
function initScrollReveal() {
|
|
const scrollElements = document.querySelectorAll('.scroll-reveal');
|
|
|
|
const elementInView = (el, dividend = 1) => {
|
|
const elementTop = el.getBoundingClientRect().top;
|
|
return (
|
|
elementTop <=
|
|
(window.innerHeight || document.documentElement.clientHeight) / dividend
|
|
);
|
|
};
|
|
|
|
const displayScrollElement = (element) => {
|
|
element.classList.add('active');
|
|
};
|
|
|
|
const handleScrollAnimation = () => {
|
|
scrollElements.forEach((el) => {
|
|
if (elementInView(el, 1.25)) {
|
|
displayScrollElement(el);
|
|
}
|
|
});
|
|
};
|
|
|
|
window.addEventListener('scroll', () => {
|
|
handleScrollAnimation();
|
|
});
|
|
|
|
// 初始检查
|
|
handleScrollAnimation();
|
|
}
|
|
|
|
// 地图导航交互
|
|
function initMapNavigation() {
|
|
const mapNav = document.querySelector('.map-navigation');
|
|
if (!mapNav) return;
|
|
|
|
// 项目位置数据
|
|
const projects = [
|
|
{ id: 1, name: '智慧景区管理系统', x: '20%', y: '40%' },
|
|
{ id: 2, name: '文化遗产数字化', x: '40%', y: '60%' },
|
|
{ id: 3, name: '旅游路线规划平台', x: '60%', y: '30%' },
|
|
{ id: 4, name: '民宿预订系统', x: '80%', y: '50%' }
|
|
];
|
|
|
|
projects.forEach(project => {
|
|
const point = document.createElement('div');
|
|
point.className = 'map-point';
|
|
point.style.left = project.x;
|
|
point.style.top = project.y;
|
|
point.dataset.project = project.id;
|
|
|
|
const label = document.createElement('span');
|
|
label.className = 'map-point-label';
|
|
label.textContent = project.name;
|
|
point.appendChild(label);
|
|
|
|
point.addEventListener('click', () => {
|
|
showProjectDetail(project.id);
|
|
});
|
|
|
|
mapNav.appendChild(point);
|
|
});
|
|
|
|
// 连接线动画
|
|
createPathAnimation(mapNav, projects);
|
|
}
|
|
|
|
// 创建路径动画
|
|
function createPathAnimation(container, points) {
|
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
svg.style.position = 'absolute';
|
|
svg.style.width = '100%';
|
|
svg.style.height = '100%';
|
|
svg.style.pointerEvents = 'none';
|
|
|
|
// 创建路径
|
|
for (let i = 0; i < points.length - 1; i++) {
|
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
const x1 = parseFloat(points[i].x);
|
|
const y1 = parseFloat(points[i].y);
|
|
const x2 = parseFloat(points[i + 1].x);
|
|
const y2 = parseFloat(points[i + 1].y);
|
|
|
|
const d = `M ${x1} ${y1} Q ${(x1 + x2) / 2} ${y1} ${x2} ${y2}`;
|
|
path.setAttribute('d', d);
|
|
path.setAttribute('stroke', '#4a90a4');
|
|
path.setAttribute('stroke-width', '2');
|
|
path.setAttribute('fill', 'none');
|
|
path.setAttribute('stroke-dasharray', '5,5');
|
|
path.style.animation = 'dashAnimation 2s linear infinite';
|
|
|
|
svg.appendChild(path);
|
|
}
|
|
|
|
container.appendChild(svg);
|
|
}
|
|
|
|
// 旅程时间轴
|
|
function initJourneyTimeline() {
|
|
const timeline = document.querySelector('.journey-timeline');
|
|
if (!timeline) return;
|
|
|
|
const stops = timeline.querySelectorAll('.journey-stop');
|
|
|
|
const observerOptions = {
|
|
threshold: 0.3,
|
|
rootMargin: '0px 0px -100px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('fade-in', 'visible');
|
|
|
|
// 添加连接线动画
|
|
const marker = entry.target.querySelector('.stop-marker');
|
|
if (marker) {
|
|
marker.style.animation = 'pulse 2s infinite';
|
|
}
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
stops.forEach(stop => {
|
|
observer.observe(stop);
|
|
});
|
|
}
|
|
|
|
// 水墨晕染效果
|
|
function initInkTransition() {
|
|
const inkElements = document.querySelectorAll('.ink-transition');
|
|
|
|
inkElements.forEach(element => {
|
|
element.addEventListener('mouseenter', function(e) {
|
|
const rect = this.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
|
|
const ripple = document.createElement('div');
|
|
ripple.style.position = 'absolute';
|
|
ripple.style.left = `${x}px`;
|
|
ripple.style.top = `${y}px`;
|
|
ripple.style.width = '0';
|
|
ripple.style.height = '0';
|
|
ripple.style.background = 'radial-gradient(circle, rgba(45, 80, 22, 0.3) 0%, transparent 70%)';
|
|
ripple.style.borderRadius = '50%';
|
|
ripple.style.transform = 'translate(-50%, -50%)';
|
|
ripple.style.transition = 'width 0.6s ease, height 0.6s ease, opacity 0.6s ease';
|
|
ripple.style.pointerEvents = 'none';
|
|
|
|
this.appendChild(ripple);
|
|
|
|
setTimeout(() => {
|
|
ripple.style.width = '400px';
|
|
ripple.style.height = '400px';
|
|
ripple.style.opacity = '0';
|
|
}, 10);
|
|
|
|
setTimeout(() => {
|
|
ripple.remove();
|
|
}, 600);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 项目卡片交互
|
|
function initProjectCards() {
|
|
const cards = document.querySelectorAll('.travel-project-card');
|
|
|
|
cards.forEach(card => {
|
|
// 鼠标跟随效果
|
|
card.addEventListener('mousemove', function(e) {
|
|
const rect = this.getBoundingClientRect();
|
|
const x = e.clientX - rect.left;
|
|
const y = e.clientY - rect.top;
|
|
|
|
const centerX = rect.width / 2;
|
|
const centerY = rect.height / 2;
|
|
|
|
const angleX = (y - centerY) / 10;
|
|
const angleY = (centerX - x) / 10;
|
|
|
|
this.style.transform = `perspective(1000px) rotateX(${angleX}deg) rotateY(${angleY}deg) translateZ(10px)`;
|
|
});
|
|
|
|
card.addEventListener('mouseleave', function() {
|
|
this.style.transform = 'perspective(1000px) rotateX(0) rotateY(0) translateZ(0)';
|
|
});
|
|
|
|
// 点击展开详情
|
|
card.addEventListener('click', function() {
|
|
showProjectModal(this.dataset.projectId);
|
|
});
|
|
});
|
|
}
|
|
|
|
// 视差滚动效果
|
|
function initParallaxEffect() {
|
|
const parallaxElements = document.querySelectorAll('[data-parallax]');
|
|
|
|
window.addEventListener('scroll', () => {
|
|
const scrolled = window.pageYOffset;
|
|
|
|
parallaxElements.forEach(element => {
|
|
const speed = element.dataset.parallax || 0.5;
|
|
const yPos = -(scrolled * speed);
|
|
element.style.transform = `translateY(${yPos}px)`;
|
|
});
|
|
});
|
|
}
|
|
|
|
// 中国画卷轴效果
|
|
function initChineseScrollEffect() {
|
|
// 创建卷轴边框
|
|
const sections = document.querySelectorAll('.content-section');
|
|
|
|
sections.forEach(section => {
|
|
// 添加卷轴装饰
|
|
const scrollTop = document.createElement('div');
|
|
scrollTop.className = 'scroll-decoration-top';
|
|
scrollTop.innerHTML = `
|
|
<svg width="100%" height="30" viewBox="0 0 200 30">
|
|
<path d="M0,15 Q50,5 100,15 T200,15" stroke="#c0392b" stroke-width="2" fill="none"/>
|
|
<circle cx="10" cy="15" r="5" fill="#c0392b"/>
|
|
<circle cx="190" cy="15" r="5" fill="#c0392b"/>
|
|
</svg>
|
|
`;
|
|
|
|
const scrollBottom = document.createElement('div');
|
|
scrollBottom.className = 'scroll-decoration-bottom';
|
|
scrollBottom.innerHTML = scrollTop.innerHTML;
|
|
|
|
section.insertBefore(scrollTop, section.firstChild);
|
|
section.appendChild(scrollBottom);
|
|
});
|
|
}
|
|
|
|
// 显示项目详情模态框
|
|
function showProjectModal(projectId) {
|
|
// 创建模态框
|
|
const modal = document.createElement('div');
|
|
modal.className = 'project-modal';
|
|
modal.innerHTML = `
|
|
<div class="modal-content pattern-border">
|
|
<span class="close-modal">×</span>
|
|
<h2 class="title-decoration">项目详情</h2>
|
|
<div class="modal-body">
|
|
<p>项目 ${projectId} 的详细信息...</p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(modal);
|
|
|
|
// 关闭模态框
|
|
modal.querySelector('.close-modal').addEventListener('click', () => {
|
|
modal.remove();
|
|
});
|
|
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) {
|
|
modal.remove();
|
|
}
|
|
});
|
|
|
|
// 显示动画
|
|
setTimeout(() => {
|
|
modal.classList.add('show');
|
|
}, 10);
|
|
}
|
|
|
|
// 显示项目详情(地图点击)
|
|
function showProjectDetail(projectId) {
|
|
const targetSection = document.querySelector(`#project-${projectId}`);
|
|
if (targetSection) {
|
|
targetSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
targetSection.classList.add('highlight');
|
|
setTimeout(() => {
|
|
targetSection.classList.remove('highlight');
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
// 添加加载完成后的入场动画
|
|
window.addEventListener('load', () => {
|
|
document.body.classList.add('loaded');
|
|
|
|
// 依次显示页面元素
|
|
const animatedElements = document.querySelectorAll('.animate-on-load');
|
|
animatedElements.forEach((element, index) => {
|
|
setTimeout(() => {
|
|
element.classList.add('animated');
|
|
}, index * 100);
|
|
});
|
|
});
|
|
|
|
// 平滑滚动到锚点
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// 导航栏滚动效果
|
|
window.addEventListener('scroll', () => {
|
|
const navbar = document.querySelector('.navbar');
|
|
if (navbar) {
|
|
if (window.scrollY > 50) {
|
|
navbar.classList.add('scrolled');
|
|
} else {
|
|
navbar.classList.remove('scrolled');
|
|
}
|
|
}
|
|
});
|
|
|
|
// 添加 CSS 动画关键帧(如果尚未存在)
|
|
if (!document.querySelector('#travel-animations-styles')) {
|
|
const style = document.createElement('style');
|
|
style.id = 'travel-animations-styles';
|
|
style.textContent = `
|
|
@keyframes dashAnimation {
|
|
0% { stroke-dashoffset: 0; }
|
|
100% { stroke-dashoffset: -10; }
|
|
}
|
|
|
|
.project-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
z-index: 10000;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.project-modal.show {
|
|
opacity: 1;
|
|
}
|
|
|
|
.modal-content {
|
|
background: white;
|
|
padding: 40px;
|
|
border-radius: 15px;
|
|
max-width: 600px;
|
|
width: 90%;
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
transform: scale(0.8);
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.project-modal.show .modal-content {
|
|
transform: scale(1);
|
|
}
|
|
|
|
.close-modal {
|
|
position: absolute;
|
|
top: 20px;
|
|
right: 30px;
|
|
font-size: 30px;
|
|
cursor: pointer;
|
|
color: #c0392b;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.close-modal:hover {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
.highlight {
|
|
animation: highlightPulse 2s ease;
|
|
}
|
|
|
|
@keyframes highlightPulse {
|
|
0%, 100% { background: transparent; }
|
|
50% { background: rgba(230, 126, 34, 0.1); }
|
|
}
|
|
|
|
.scroll-decoration-top,
|
|
.scroll-decoration-bottom {
|
|
width: 100%;
|
|
height: 30px;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.scroll-decoration-bottom {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
body.loaded .loader {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.animate-on-load {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: all 0.6s ease;
|
|
}
|
|
|
|
.animate-on-load.animated {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
} |