Add CSS, JS and asset files (without videos)
Dieser Commit ist enthalten in:
303
js/animations-enhanced.js
Normale Datei
303
js/animations-enhanced.js
Normale Datei
@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Enhanced Animations and Interactions
|
||||
* Premium effects for modern web experience
|
||||
*/
|
||||
|
||||
const EnhancedAnimations = {
|
||||
init() {
|
||||
this.initScrollAnimations();
|
||||
this.initParallaxEffects();
|
||||
this.initMagneticButtons();
|
||||
this.initTextAnimations();
|
||||
this.initCardTilt();
|
||||
this.initSmoothScroll();
|
||||
this.initCursorEffects();
|
||||
this.initNumberCounters();
|
||||
this.initRevealOnScroll();
|
||||
this.initNavbarEffects();
|
||||
},
|
||||
|
||||
// Smooth scroll with easing
|
||||
initSmoothScroll() {
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
const offset = 100;
|
||||
const targetPosition = target.offsetTop - offset;
|
||||
window.scrollTo({
|
||||
top: targetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Parallax scrolling effects
|
||||
initParallaxEffects() {
|
||||
const parallaxElements = document.querySelectorAll('.parallax');
|
||||
let ticking = false;
|
||||
|
||||
function updateParallax() {
|
||||
const scrolled = window.pageYOffset;
|
||||
|
||||
parallaxElements.forEach(element => {
|
||||
const speed = element.dataset.speed || 0.5;
|
||||
const yPos = -(scrolled * speed);
|
||||
element.style.transform = `translateY(${yPos}px)`;
|
||||
});
|
||||
|
||||
ticking = false;
|
||||
}
|
||||
|
||||
function requestTick() {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(updateParallax);
|
||||
ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', requestTick);
|
||||
},
|
||||
|
||||
// Magnetic button effects
|
||||
initMagneticButtons() {
|
||||
const magneticButtons = document.querySelectorAll('.primary-button, .secondary-button, .cta-button');
|
||||
|
||||
magneticButtons.forEach(button => {
|
||||
button.addEventListener('mousemove', (e) => {
|
||||
const rect = button.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left - rect.width / 2;
|
||||
const y = e.clientY - rect.top - rect.height / 2;
|
||||
|
||||
button.style.transform = `translate(${x * 0.2}px, ${y * 0.2}px) scale(1.05)`;
|
||||
});
|
||||
|
||||
button.addEventListener('mouseleave', () => {
|
||||
button.style.transform = '';
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Advanced text animations
|
||||
initTextAnimations() {
|
||||
// Typewriter effect for hero title - DISABLED to prevent duplication
|
||||
// The title already has CSS animations applied
|
||||
|
||||
/* Commented out to fix duplication issue
|
||||
const heroTitle = document.querySelector('.main-title');
|
||||
if (heroTitle && !heroTitle.dataset.animated) {
|
||||
heroTitle.dataset.animated = 'true';
|
||||
const text = heroTitle.textContent;
|
||||
heroTitle.textContent = '';
|
||||
heroTitle.style.opacity = '1';
|
||||
|
||||
let index = 0;
|
||||
const typeWriter = () => {
|
||||
if (index < text.length) {
|
||||
heroTitle.textContent += text.charAt(index);
|
||||
index++;
|
||||
setTimeout(typeWriter, 50);
|
||||
}
|
||||
};
|
||||
|
||||
// Start typewriter after a short delay
|
||||
setTimeout(typeWriter, 500);
|
||||
}
|
||||
*/
|
||||
|
||||
// Word-by-word reveal for hero text
|
||||
const heroText = document.querySelector('.hero-text');
|
||||
if (heroText) {
|
||||
const words = heroText.textContent.split(' ');
|
||||
heroText.innerHTML = words.map(word =>
|
||||
`<span class="word-reveal" style="opacity: 0; display: inline-block; transform: translateY(20px); transition: all 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);">${word}</span>`
|
||||
).join(' ');
|
||||
|
||||
const wordSpans = heroText.querySelectorAll('.word-reveal');
|
||||
wordSpans.forEach((word, index) => {
|
||||
setTimeout(() => {
|
||||
word.style.opacity = '1';
|
||||
word.style.transform = 'translateY(0)';
|
||||
}, 1000 + index * 100);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 3D card tilt effect
|
||||
initCardTilt() {
|
||||
const cards = document.querySelectorAll('.tool-card, .value-card, .why-card');
|
||||
|
||||
cards.forEach(card => {
|
||||
card.addEventListener('mousemove', (e) => {
|
||||
const rect = card.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
const centerX = rect.width / 2;
|
||||
const centerY = rect.height / 2;
|
||||
|
||||
const rotateX = (y - centerY) / 10;
|
||||
const rotateY = (centerX - x) / 10;
|
||||
|
||||
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.02)`;
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', () => {
|
||||
card.style.transform = '';
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Custom cursor effects - DISABLED
|
||||
initCursorEffects() {
|
||||
// Cursor removed as requested
|
||||
return;
|
||||
},
|
||||
|
||||
// Animated number counters
|
||||
initNumberCounters() {
|
||||
const counters = document.querySelectorAll('.indicator-value');
|
||||
const animationDuration = 2000;
|
||||
let hasAnimated = false;
|
||||
|
||||
const animateCounters = () => {
|
||||
if (hasAnimated) return;
|
||||
|
||||
counters.forEach(counter => {
|
||||
const target = parseFloat(counter.dataset.target);
|
||||
const start = 0;
|
||||
const increment = target / (animationDuration / 16);
|
||||
let current = start;
|
||||
|
||||
const updateCounter = () => {
|
||||
current += increment;
|
||||
if (current < target) {
|
||||
counter.textContent = Math.floor(current);
|
||||
requestAnimationFrame(updateCounter);
|
||||
} else {
|
||||
counter.textContent = target % 1 === 0 ? target : target.toFixed(1);
|
||||
}
|
||||
};
|
||||
|
||||
updateCounter();
|
||||
});
|
||||
|
||||
hasAnimated = true;
|
||||
};
|
||||
|
||||
// Trigger on scroll into view
|
||||
const observerOptions = {
|
||||
threshold: 0.5
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
animateCounters();
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
const indicatorsSection = document.querySelector('.trust-indicators');
|
||||
if (indicatorsSection) {
|
||||
observer.observe(indicatorsSection);
|
||||
}
|
||||
},
|
||||
|
||||
// Reveal elements on scroll
|
||||
initRevealOnScroll() {
|
||||
const revealElements = document.querySelectorAll('.about-panel, .tool-card, .value-card, .why-card, .competency-item');
|
||||
|
||||
revealElements.forEach((element, index) => {
|
||||
element.style.opacity = '0';
|
||||
element.style.transform = 'translateY(50px)';
|
||||
element.style.transition = 'all 0.8s cubic-bezier(0.4, 0, 0.2, 1)';
|
||||
});
|
||||
|
||||
const revealOnScroll = () => {
|
||||
const windowHeight = window.innerHeight;
|
||||
|
||||
revealElements.forEach((element, index) => {
|
||||
const elementTop = element.getBoundingClientRect().top;
|
||||
const elementVisible = 100;
|
||||
|
||||
if (elementTop < windowHeight - elementVisible) {
|
||||
setTimeout(() => {
|
||||
element.style.opacity = '1';
|
||||
element.style.transform = 'translateY(0)';
|
||||
}, index * 50);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', revealOnScroll);
|
||||
revealOnScroll(); // Check on initial load
|
||||
},
|
||||
|
||||
// Scroll-based animations
|
||||
initScrollAnimations() {
|
||||
let lastScrollY = window.scrollY;
|
||||
let ticking = false;
|
||||
|
||||
function updateScrollAnimations() {
|
||||
const scrollY = window.scrollY;
|
||||
const scrollDirection = scrollY > lastScrollY ? 'down' : 'up';
|
||||
|
||||
// Hero parallax
|
||||
const hero = document.querySelector('.hero-content');
|
||||
if (hero) {
|
||||
hero.style.transform = `translateY(${scrollY * 0.5}px)`;
|
||||
hero.style.opacity = 1 - (scrollY / 800);
|
||||
}
|
||||
|
||||
// Video parallax
|
||||
const heroVideos = document.querySelector('.hero-video-container');
|
||||
if (heroVideos) {
|
||||
heroVideos.style.transform = `translateY(${scrollY * 0.3}px) scale(${1 + scrollY * 0.0003})`;
|
||||
}
|
||||
|
||||
lastScrollY = scrollY;
|
||||
ticking = false;
|
||||
}
|
||||
|
||||
function requestTick() {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(updateScrollAnimations);
|
||||
ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', requestTick);
|
||||
},
|
||||
|
||||
// Enhanced navbar effects
|
||||
initNavbarEffects() {
|
||||
const navbar = document.querySelector('.navbar');
|
||||
let lastScrollY = window.scrollY;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
if (scrollY > 50) {
|
||||
navbar.classList.add('scrolled');
|
||||
} else {
|
||||
navbar.classList.remove('scrolled');
|
||||
}
|
||||
|
||||
// Keep navbar always visible
|
||||
navbar.style.transform = 'translateY(0)';
|
||||
|
||||
lastScrollY = scrollY;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => EnhancedAnimations.init());
|
||||
} else {
|
||||
EnhancedAnimations.init();
|
||||
}
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren