Add CSS, JS and asset files (without videos)
Dieser Commit ist enthalten in:
403
js/animations.js
Normale Datei
403
js/animations.js
Normale Datei
@ -0,0 +1,403 @@
|
||||
/**
|
||||
* Animation module for IntelSight website
|
||||
* Contains all animation logic and visual effects
|
||||
*/
|
||||
|
||||
// Particle Animation System
|
||||
const ParticleAnimation = {
|
||||
canvas: null,
|
||||
ctx: null,
|
||||
particles: [],
|
||||
|
||||
/**
|
||||
* Initialize particle animation
|
||||
*/
|
||||
init() {
|
||||
this.canvas = document.querySelector(SELECTORS.PARTICLE_CANVAS);
|
||||
if (!this.canvas) return;
|
||||
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.resizeCanvas();
|
||||
this.createParticles();
|
||||
this.animate();
|
||||
|
||||
// Handle window resize
|
||||
window.addEventListener('resize', () => this.resizeCanvas());
|
||||
},
|
||||
|
||||
/**
|
||||
* Resize canvas to window size
|
||||
*/
|
||||
resizeCanvas() {
|
||||
this.canvas.width = window.innerWidth;
|
||||
this.canvas.height = window.innerHeight;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create particle objects
|
||||
*/
|
||||
createParticles() {
|
||||
this.particles = [];
|
||||
for (let i = 0; i < CONFIG.ANIMATION.PARTICLE_COUNT; i++) {
|
||||
this.particles.push(new Particle(this.canvas));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Main animation loop
|
||||
*/
|
||||
animate() {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Update and draw particles
|
||||
this.particles.forEach(particle => {
|
||||
particle.update(this.canvas);
|
||||
particle.draw(this.ctx);
|
||||
});
|
||||
|
||||
// Draw connections between particles
|
||||
this.drawConnections();
|
||||
|
||||
requestAnimationFrame(() => this.animate());
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw connections between nearby particles
|
||||
*/
|
||||
drawConnections() {
|
||||
for (let a = 0; a < this.particles.length; a++) {
|
||||
for (let b = a + 1; b < this.particles.length; b++) {
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(this.particles[a].x - this.particles[b].x, 2) +
|
||||
Math.pow(this.particles[a].y - this.particles[b].y, 2)
|
||||
);
|
||||
|
||||
if (distance < CONFIG.ANIMATION.CONNECTION_DISTANCE) {
|
||||
const opacity = 0.15 * (1 - distance / CONFIG.ANIMATION.CONNECTION_DISTANCE);
|
||||
// Use darker blue for better visibility on light background
|
||||
this.ctx.strokeStyle = `rgba(15, 114, 181, ${opacity})`;
|
||||
this.ctx.lineWidth = 1;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.particles[a].x, this.particles[a].y);
|
||||
this.ctx.lineTo(this.particles[b].x, this.particles[b].y);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Particle class for animation
|
||||
*/
|
||||
class Particle {
|
||||
constructor(canvas) {
|
||||
this.x = Math.random() * canvas.width;
|
||||
this.y = Math.random() * canvas.height;
|
||||
this.size = Math.random() * (CONFIG.ANIMATION.PARTICLE_SIZE_MAX - CONFIG.ANIMATION.PARTICLE_SIZE_MIN) + CONFIG.ANIMATION.PARTICLE_SIZE_MIN;
|
||||
this.speedX = (Math.random() - 0.5) * CONFIG.ANIMATION.PARTICLE_SPEED;
|
||||
this.speedY = (Math.random() - 0.5) * CONFIG.ANIMATION.PARTICLE_SPEED;
|
||||
this.opacity = Math.random() * 0.5 + 0.2;
|
||||
}
|
||||
|
||||
update(canvas) {
|
||||
this.x += this.speedX;
|
||||
this.y += this.speedY;
|
||||
|
||||
// Wrap around screen edges
|
||||
if (this.x > canvas.width) this.x = 0;
|
||||
else if (this.x < 0) this.x = canvas.width;
|
||||
|
||||
if (this.y > canvas.height) this.y = 0;
|
||||
else if (this.y < 0) this.y = canvas.height;
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
// Use darker blue for better visibility on light background
|
||||
ctx.fillStyle = `rgba(15, 114, 181, ${this.opacity * 0.7})`;
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
|
||||
// Counter Animation
|
||||
const CounterAnimation = {
|
||||
/**
|
||||
* Animate all counter elements
|
||||
*/
|
||||
animateAll() {
|
||||
const counters = document.querySelectorAll(SELECTORS.INDICATOR_VALUE);
|
||||
counters.forEach(counter => this.animateCounter(counter));
|
||||
},
|
||||
|
||||
/**
|
||||
* Animate a single counter
|
||||
* @param {HTMLElement} counter - Counter element to animate
|
||||
*/
|
||||
animateCounter(counter) {
|
||||
const target = parseFloat(counter.getAttribute(DATA_ATTRS.TARGET));
|
||||
const increment = target / CONFIG.ANIMATION.COUNTER_SPEED;
|
||||
let current = 0;
|
||||
|
||||
const updateCounter = () => {
|
||||
current += increment;
|
||||
|
||||
if (current < target) {
|
||||
counter.innerText = Math.ceil(current);
|
||||
setTimeout(updateCounter, CONFIG.TIMING.COUNTER_UPDATE_INTERVAL);
|
||||
} else {
|
||||
// Set final value with proper formatting
|
||||
if (target === CONFIG.TRUST_INDICATORS.AVAILABILITY) {
|
||||
counter.innerText = target + '%';
|
||||
} else if (target === CONFIG.TRUST_INDICATORS.AUTHORITIES_COUNT) {
|
||||
counter.innerText = target + '+';
|
||||
} else if (target === CONFIG.TRUST_INDICATORS.SUPPORT_HOURS) {
|
||||
counter.innerText = target + '/7';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateCounter();
|
||||
}
|
||||
};
|
||||
|
||||
// Scroll Animations
|
||||
const ScrollAnimations = {
|
||||
scrollIndicator: null,
|
||||
|
||||
/**
|
||||
* Initialize scroll-based animations
|
||||
*/
|
||||
init() {
|
||||
this.scrollIndicator = document.querySelector(SELECTORS.SCROLL_INDICATOR);
|
||||
this.setupScrollIndicator();
|
||||
this.setupIntersectionObserver();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup scroll indicator behavior
|
||||
*/
|
||||
setupScrollIndicator() {
|
||||
if (!this.scrollIndicator) return;
|
||||
|
||||
// Click to scroll to about section
|
||||
this.scrollIndicator.addEventListener('click', () => {
|
||||
const aboutSection = document.querySelector('#about');
|
||||
if (aboutSection) {
|
||||
aboutSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
|
||||
// Hide/show based on scroll position
|
||||
let scrollTimeout;
|
||||
window.addEventListener('scroll', () => {
|
||||
const hero = document.querySelector(SELECTORS.HERO);
|
||||
|
||||
if (window.scrollY > CONFIG.ANIMATION.SCROLL_THRESHOLD) {
|
||||
if (hero) hero.classList.add(CLASSES.SCROLLED);
|
||||
if (this.scrollIndicator) this.scrollIndicator.style.opacity = '0';
|
||||
} else {
|
||||
if (hero) hero.classList.remove(CLASSES.SCROLLED);
|
||||
if (this.scrollIndicator) this.scrollIndicator.style.opacity = '1';
|
||||
}
|
||||
|
||||
clearTimeout(scrollTimeout);
|
||||
scrollTimeout = setTimeout(() => {
|
||||
if (window.scrollY > CONFIG.ANIMATION.SCROLL_THRESHOLD && this.scrollIndicator) {
|
||||
this.scrollIndicator.style.display = 'none';
|
||||
} else if (this.scrollIndicator) {
|
||||
this.scrollIndicator.style.display = 'flex';
|
||||
}
|
||||
}, CONFIG.TIMING.SCROLL_HIDE_DELAY);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup intersection observer for scroll-triggered animations
|
||||
*/
|
||||
setupIntersectionObserver() {
|
||||
const observerOptions = {
|
||||
threshold: CONFIG.OBSERVER.THRESHOLD,
|
||||
rootMargin: CONFIG.OBSERVER.ROOT_MARGIN
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
// Trust indicators animation
|
||||
if (entry.target.classList.contains('trust-indicators')) {
|
||||
CounterAnimation.animateAll();
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
|
||||
// Timeline animation
|
||||
if (entry.target.classList.contains('timeline')) {
|
||||
const items = entry.target.querySelectorAll('.timeline-item');
|
||||
items.forEach((item, index) => {
|
||||
setTimeout(() => {
|
||||
item.classList.add(CLASSES.VISIBLE);
|
||||
}, index * 300);
|
||||
});
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
|
||||
// Feature nodes animation
|
||||
if (entry.target.classList.contains('feature-nodes')) {
|
||||
const nodes = entry.target.querySelectorAll('.node');
|
||||
nodes.forEach((node, index) => {
|
||||
setTimeout(() => {
|
||||
node.style.opacity = '1';
|
||||
node.style.transform = 'translateY(0)';
|
||||
}, index * 150);
|
||||
});
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
// Observe elements
|
||||
const trustIndicators = document.querySelector(SELECTORS.TRUST_INDICATORS);
|
||||
if (trustIndicators) {
|
||||
trustIndicators.style.opacity = '0';
|
||||
observer.observe(trustIndicators);
|
||||
}
|
||||
|
||||
const timeline = document.querySelector('.timeline');
|
||||
if (timeline) observer.observe(timeline);
|
||||
|
||||
const featureNodes = document.querySelector('.feature-nodes');
|
||||
if (featureNodes) {
|
||||
document.querySelectorAll('.node').forEach(node => {
|
||||
node.style.opacity = '0';
|
||||
node.style.transform = 'translateY(30px)';
|
||||
node.style.transition = 'all 0.6s ease';
|
||||
});
|
||||
observer.observe(featureNodes);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Glitch Effect
|
||||
const GlitchEffect = {
|
||||
/**
|
||||
* Apply glitch effect to element on hover
|
||||
* @param {HTMLElement} element - Element to apply effect to
|
||||
*/
|
||||
apply(element) {
|
||||
if (!element) return;
|
||||
|
||||
let glitchInterval;
|
||||
element.addEventListener('mouseenter', () => {
|
||||
let count = 0;
|
||||
glitchInterval = setInterval(() => {
|
||||
element.style.textShadow = `
|
||||
${Math.random() * 5}px ${Math.random() * 5}px 0 rgba(0, 212, 255, 0.5),
|
||||
${Math.random() * -5}px ${Math.random() * 5}px 0 rgba(255, 0, 128, 0.5)
|
||||
`;
|
||||
count++;
|
||||
if (count > CONFIG.ANIMATION.GLITCH_ITERATIONS) {
|
||||
clearInterval(glitchInterval);
|
||||
element.style.textShadow = 'none';
|
||||
}
|
||||
}, CONFIG.ANIMATION.GLITCH_INTERVAL);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Interactive Elements
|
||||
const InteractiveElements = {
|
||||
/**
|
||||
* Initialize all interactive element animations
|
||||
*/
|
||||
init() {
|
||||
this.setupNodeHoverEffects();
|
||||
this.setupWidgetHoverEffects();
|
||||
this.setupInteractiveIcon();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup hover effects for node elements
|
||||
*/
|
||||
setupNodeHoverEffects() {
|
||||
document.querySelectorAll('.node').forEach(node => {
|
||||
node.addEventListener('mouseenter', function() {
|
||||
const icon = this.querySelector('.node-icon');
|
||||
if (icon) icon.style.transform = 'scale(1.2) rotate(5deg)';
|
||||
});
|
||||
|
||||
node.addEventListener('mouseleave', function() {
|
||||
const icon = this.querySelector('.node-icon');
|
||||
if (icon) icon.style.transform = 'scale(1) rotate(0deg)';
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup hover effects for widget elements
|
||||
*/
|
||||
setupWidgetHoverEffects() {
|
||||
document.querySelectorAll('.widget').forEach(widget => {
|
||||
widget.addEventListener('mouseenter', function() {
|
||||
this.style.boxShadow = '0 5px 20px rgba(0, 212, 255, 0.3)';
|
||||
});
|
||||
|
||||
widget.addEventListener('mouseleave', function() {
|
||||
this.style.boxShadow = 'none';
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup 3D interactive icon effect
|
||||
*/
|
||||
setupInteractiveIcon() {
|
||||
const icon = document.querySelector(SELECTORS.INTERACTIVE_ICON);
|
||||
if (!icon) return;
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
const rect = icon.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
|
||||
const mouseX = (e.clientX - centerX) / 20;
|
||||
const mouseY = (e.clientY - centerY) / 20;
|
||||
|
||||
icon.style.transform = `perspective(1000px) rotateY(${mouseX}deg) rotateX(${-mouseY}deg)`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize all animations
|
||||
const Animations = {
|
||||
/**
|
||||
* Initialize all animation systems
|
||||
*/
|
||||
init() {
|
||||
// Core animations
|
||||
ParticleAnimation.init();
|
||||
ScrollAnimations.init();
|
||||
InteractiveElements.init();
|
||||
|
||||
// Apply glitch effect to main title
|
||||
const mainTitle = document.querySelector('.main-title');
|
||||
if (mainTitle) {
|
||||
GlitchEffect.apply(mainTitle);
|
||||
}
|
||||
|
||||
// Page load animations
|
||||
window.addEventListener('load', () => {
|
||||
document.body.classList.add(CLASSES.LOADED);
|
||||
|
||||
// Fade in hero content
|
||||
setTimeout(() => {
|
||||
const heroContent = document.querySelector(SELECTORS.HERO_CONTENT);
|
||||
if (heroContent) {
|
||||
heroContent.style.opacity = '1';
|
||||
heroContent.style.transform = 'translateY(0)';
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
};
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren