clean up
This commit is contained in:
@@ -1,595 +0,0 @@
|
||||
/**
|
||||
* Tech-Onepager JavaScript - Interactive Elements and Animations
|
||||
* System being built aesthetic with smooth micro-animations
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// ===== GLOBAL VARIABLES =====
|
||||
let currentTooltip = null;
|
||||
|
||||
// ===== SYSTEM GRAPHIC ANIMATIONS =====
|
||||
const systemGraphic = document.getElementById('systemGraphic');
|
||||
const connections = document.getElementById('connections');
|
||||
const dataPoints = document.getElementById('dataPoints');
|
||||
|
||||
if (systemGraphic && connections) {
|
||||
initializeSystemGraphic();
|
||||
}
|
||||
|
||||
function initializeSystemGraphic() {
|
||||
const nodes = systemGraphic.querySelectorAll('.node');
|
||||
const centralNode = systemGraphic.querySelector('.central-node');
|
||||
|
||||
// Draw connection lines
|
||||
drawConnections();
|
||||
|
||||
// Initialize tooltip system
|
||||
initializeTooltips();
|
||||
|
||||
// Add node interactions
|
||||
nodes.forEach(node => {
|
||||
node.addEventListener('mouseenter', function() {
|
||||
activateConnection(this);
|
||||
});
|
||||
|
||||
node.addEventListener('mouseleave', function() {
|
||||
deactivateConnections();
|
||||
});
|
||||
|
||||
// Special handling for central node
|
||||
if (node.classList.contains('central-node')) {
|
||||
node.addEventListener('click', function() {
|
||||
triggerCentralNode(this);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll-based activation
|
||||
setupScrollActivation();
|
||||
}
|
||||
|
||||
// ===== TOOLTIP SYSTEM =====
|
||||
function initializeTooltips() {
|
||||
const nodes = systemGraphic.querySelectorAll('.node[data-tooltip]');
|
||||
|
||||
nodes.forEach(node => {
|
||||
let hoverTimeout;
|
||||
|
||||
node.addEventListener('mouseenter', function() {
|
||||
const tooltip = this.getAttribute('data-tooltip');
|
||||
if (!tooltip) return;
|
||||
|
||||
// Clear any existing timeout
|
||||
clearTimeout(hoverTimeout);
|
||||
|
||||
// Set 1-second delay before showing tooltip
|
||||
hoverTimeout = setTimeout(() => {
|
||||
showTooltip(this, tooltip);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
node.addEventListener('mouseleave', function() {
|
||||
// Clear the timeout if mouse leaves before 1 second
|
||||
clearTimeout(hoverTimeout);
|
||||
|
||||
// Hide any visible tooltip
|
||||
hideTooltip();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showTooltip(node, text) {
|
||||
// Remove any existing tooltip
|
||||
hideTooltip();
|
||||
|
||||
// Create tooltip element
|
||||
const tooltip = document.createElement('div');
|
||||
tooltip.className = 'node-tooltip';
|
||||
tooltip.textContent = text;
|
||||
|
||||
// Position the tooltip
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
const systemGraphicRect = systemGraphic.getBoundingClientRect();
|
||||
|
||||
// Calculate position relative to system graphic
|
||||
let left = nodeRect.left - systemGraphicRect.left + (nodeRect.width / 2) - 140; // Center horizontally
|
||||
let top = nodeRect.top - systemGraphicRect.top - 60; // Position above node
|
||||
|
||||
// Adjust if tooltip goes outside bounds
|
||||
if (left < 10) left = 10;
|
||||
if (left + 280 > systemGraphicRect.width - 10) left = systemGraphicRect.width - 290;
|
||||
if (top < 10) top = nodeRect.top - systemGraphicRect.top + nodeRect.height + 10; // Show below if not enough space above
|
||||
|
||||
tooltip.style.left = left + 'px';
|
||||
tooltip.style.top = top + 'px';
|
||||
|
||||
// Add to system graphic
|
||||
systemGraphic.appendChild(tooltip);
|
||||
|
||||
// Trigger show animation
|
||||
setTimeout(() => {
|
||||
tooltip.classList.add('show');
|
||||
}, 10);
|
||||
|
||||
currentTooltip = tooltip;
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
if (currentTooltip) {
|
||||
currentTooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
if (currentTooltip && currentTooltip.parentNode) {
|
||||
currentTooltip.parentNode.removeChild(currentTooltip);
|
||||
}
|
||||
currentTooltip = null;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup function
|
||||
function cleanupTooltips() {
|
||||
hideTooltip();
|
||||
}
|
||||
|
||||
// Add cleanup on page unload
|
||||
window.addEventListener('beforeunload', cleanupTooltips);
|
||||
|
||||
function drawConnections() {
|
||||
const centralNode = systemGraphic.querySelector('.central-node');
|
||||
const otherNodes = systemGraphic.querySelectorAll('.node:not(.central-node)');
|
||||
|
||||
const centralRect = centralNode.getBoundingClientRect();
|
||||
const graphicRect = systemGraphic.getBoundingClientRect();
|
||||
|
||||
const centerX = centralRect.left - graphicRect.left + centralRect.width / 2;
|
||||
const centerY = centralRect.top - graphicRect.top + centralRect.height / 2;
|
||||
|
||||
connections.innerHTML = '';
|
||||
|
||||
otherNodes.forEach(node => {
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
const nodeX = nodeRect.left - graphicRect.left + nodeRect.width / 2;
|
||||
const nodeY = nodeRect.top - graphicRect.top + nodeRect.height / 2;
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||||
line.setAttribute('x1', centerX);
|
||||
line.setAttribute('y1', centerY);
|
||||
line.setAttribute('x2', nodeX);
|
||||
line.setAttribute('y2', nodeY);
|
||||
line.setAttribute('class', 'connection-line');
|
||||
line.setAttribute('data-target', node.dataset.node);
|
||||
|
||||
connections.appendChild(line);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function triggerCentralNode(node) {
|
||||
// Add triggered class for growth animation
|
||||
node.classList.add('triggered');
|
||||
|
||||
// Activate all connections
|
||||
const lines = connections.querySelectorAll('.connection-line');
|
||||
lines.forEach((line, index) => {
|
||||
setTimeout(() => {
|
||||
line.classList.add('active');
|
||||
}, index * 100);
|
||||
});
|
||||
|
||||
// Remove triggered class after animation
|
||||
setTimeout(() => {
|
||||
node.classList.remove('triggered');
|
||||
|
||||
// Deactivate connections
|
||||
setTimeout(() => {
|
||||
lines.forEach(line => line.classList.remove('active'));
|
||||
}, 500);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function activateConnection(node) {
|
||||
const targetNode = node.dataset.node;
|
||||
const line = connections.querySelector(`[data-target="${targetNode}"]`);
|
||||
if (line) {
|
||||
line.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function deactivateConnections() {
|
||||
const lines = connections.querySelectorAll('.connection-line');
|
||||
lines.forEach(line => line.classList.remove('active'));
|
||||
}
|
||||
|
||||
function setupScrollActivation() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
const lines = connections.querySelectorAll('.connection-line');
|
||||
lines.forEach((line, index) => {
|
||||
setTimeout(() => {
|
||||
line.classList.add('active');
|
||||
setTimeout(() => {
|
||||
line.classList.remove('active');
|
||||
}, 2000);
|
||||
}, index * 200);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.5 });
|
||||
|
||||
observer.observe(systemGraphic);
|
||||
}
|
||||
|
||||
// ===== PROCESS LINE ANIMATION =====
|
||||
const processLine = document.getElementById('processLine');
|
||||
const processSteps = document.querySelectorAll('.process-step');
|
||||
const processConnectors = document.querySelectorAll('.process-connector');
|
||||
const stepDetails = document.querySelectorAll('.step-detail');
|
||||
|
||||
if (processLine) {
|
||||
setupProcessAnimation();
|
||||
}
|
||||
|
||||
function setupProcessAnimation() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
animateProcessLine();
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
|
||||
observer.observe(processLine);
|
||||
}
|
||||
|
||||
function animateProcessLine() {
|
||||
// Animate connectors sequentially
|
||||
processConnectors.forEach((connector, index) => {
|
||||
setTimeout(() => {
|
||||
connector.classList.add('active');
|
||||
}, 500 + (index * 500));
|
||||
});
|
||||
|
||||
// Activate steps sequentially
|
||||
processSteps.forEach((step, index) => {
|
||||
setTimeout(() => {
|
||||
step.classList.add('active');
|
||||
activateStepDetail(index + 1);
|
||||
}, 200 + (index * 500));
|
||||
});
|
||||
}
|
||||
|
||||
function activateStepDetail(stepNumber) {
|
||||
const detail = document.querySelector(`[data-step-detail="${stepNumber}"]`);
|
||||
if (detail) {
|
||||
setTimeout(() => {
|
||||
detail.classList.add('active');
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Step click interactions
|
||||
processSteps.forEach((step, index) => {
|
||||
step.addEventListener('click', () => {
|
||||
// Reset all steps
|
||||
processSteps.forEach(s => s.classList.remove('active'));
|
||||
processConnectors.forEach(c => c.classList.remove('active'));
|
||||
stepDetails.forEach(d => d.classList.remove('active'));
|
||||
|
||||
// Activate up to clicked step
|
||||
for (let i = 0; i <= index; i++) {
|
||||
processSteps[i].classList.add('active');
|
||||
activateStepDetail(i + 1);
|
||||
|
||||
if (i < processConnectors.length) {
|
||||
processConnectors[i].classList.add('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ===== INTERACTION CARD ANIMATIONS =====
|
||||
const phoneInteraction = document.getElementById('phoneInteraction');
|
||||
const chatInteraction = document.getElementById('chatInteraction');
|
||||
|
||||
if (phoneInteraction) {
|
||||
setupPhoneInteraction();
|
||||
}
|
||||
|
||||
if (chatInteraction) {
|
||||
setupChatInteraction();
|
||||
}
|
||||
|
||||
function setupPhoneInteraction() {
|
||||
const microphone = phoneInteraction.querySelector('.microphone');
|
||||
const pulseRing = phoneInteraction.querySelector('.pulse-ring');
|
||||
const micIcon = phoneInteraction.querySelector('.mic-icon');
|
||||
|
||||
if (microphone && pulseRing && micIcon) {
|
||||
microphone.addEventListener('mouseenter', () => {
|
||||
pulseRing.style.animation = 'pulse 0.8s infinite';
|
||||
micIcon.style.transform = 'scale(1.1)';
|
||||
});
|
||||
|
||||
microphone.addEventListener('mouseleave', () => {
|
||||
pulseRing.style.animation = 'pulse 2s infinite';
|
||||
micIcon.style.transform = 'scale(1)';
|
||||
});
|
||||
|
||||
microphone.addEventListener('click', () => {
|
||||
// Enhanced click animation
|
||||
microphone.style.transform = 'scale(0.9)';
|
||||
micIcon.style.transform = 'scale(0.8)';
|
||||
|
||||
// Create ripple effect
|
||||
const ripple = document.createElement('div');
|
||||
ripple.style.cssText = `
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 50%;
|
||||
animation: rippleEffect 0.6s ease-out;
|
||||
pointer-events: none;
|
||||
`;
|
||||
microphone.appendChild(ripple);
|
||||
|
||||
setTimeout(() => {
|
||||
microphone.style.transform = 'scale(1.15)';
|
||||
micIcon.style.transform = 'scale(1.1)';
|
||||
setTimeout(() => {
|
||||
microphone.style.transform = 'scale(1)';
|
||||
micIcon.style.transform = 'scale(1)';
|
||||
ripple.remove();
|
||||
}, 200);
|
||||
}, 100);
|
||||
|
||||
// Show feedback
|
||||
showInteractionFeedback('KI Telefon wird verbunden...');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupChatInteraction() {
|
||||
const chatWindow = chatInteraction.querySelector('.chat-window');
|
||||
const typingIndicator = chatInteraction.querySelector('.typing-indicator');
|
||||
const chatHeader = chatInteraction.querySelector('.chat-header');
|
||||
|
||||
if (chatWindow && typingIndicator && chatHeader) {
|
||||
chatInteraction.addEventListener('mouseenter', () => {
|
||||
// Enhanced typing animation
|
||||
const spans = typingIndicator.querySelectorAll('span');
|
||||
spans.forEach((span, index) => {
|
||||
span.style.animation = 'typing 1.2s infinite ease-in-out';
|
||||
span.style.animationDelay = `${-0.32 + (index * 0.16)}s`;
|
||||
span.style.background = 'var(--accent-teal)';
|
||||
});
|
||||
|
||||
// Animate chat window
|
||||
chatWindow.style.transform = 'scale(1.02)';
|
||||
chatHeader.style.background = 'linear-gradient(135deg, var(--accent-green), var(--accent-teal))';
|
||||
});
|
||||
|
||||
chatInteraction.addEventListener('mouseleave', () => {
|
||||
const spans = typingIndicator.querySelectorAll('span');
|
||||
spans.forEach(span => {
|
||||
span.style.background = 'var(--primary-mid)';
|
||||
});
|
||||
chatWindow.style.transform = 'scale(1)';
|
||||
chatHeader.style.background = 'linear-gradient(135deg, var(--accent-teal), var(--accent-green))';
|
||||
});
|
||||
|
||||
const chatBtn = chatInteraction.querySelector('.interaction-btn.secondary');
|
||||
if (chatBtn) {
|
||||
chatBtn.addEventListener('click', () => {
|
||||
// Enhanced click animation
|
||||
chatWindow.style.transform = 'scale(0.95)';
|
||||
setTimeout(() => {
|
||||
chatWindow.style.transform = 'scale(1.05)';
|
||||
setTimeout(() => {
|
||||
chatWindow.style.transform = 'scale(1)';
|
||||
}, 200);
|
||||
}, 100);
|
||||
|
||||
showInteractionFeedback('KI Chat wird gestartet...');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showInteractionFeedback(message) {
|
||||
// Create temporary feedback element
|
||||
const feedback = document.createElement('div');
|
||||
feedback.style.cssText = `
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--accent-teal);
|
||||
color: white;
|
||||
padding: 16px 24px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
z-index: 10000;
|
||||
box-shadow: 0 8px 30px rgba(38, 166, 154, 0.4);
|
||||
animation: slideDown 0.3s ease;
|
||||
`;
|
||||
feedback.textContent = message;
|
||||
|
||||
document.body.appendChild(feedback);
|
||||
|
||||
setTimeout(() => {
|
||||
feedback.style.animation = 'slideUp 0.3s ease';
|
||||
setTimeout(() => {
|
||||
feedback.remove();
|
||||
}, 300);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// ===== HERO BUTTON INTERACTIONS =====
|
||||
const kiPhoneBtn = document.getElementById('kiPhoneBtn');
|
||||
const chatBtn = document.getElementById('chatBtn');
|
||||
|
||||
if (kiPhoneBtn) {
|
||||
kiPhoneBtn.addEventListener('click', () => {
|
||||
showInteractionFeedback('KI Telefon wird verbunden...');
|
||||
// Scroll to interaction section
|
||||
document.getElementById('interaction')?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (chatBtn) {
|
||||
chatBtn.addEventListener('click', () => {
|
||||
showInteractionFeedback('KI Chat wird gestartet...');
|
||||
// Scroll to interaction section
|
||||
document.getElementById('interaction')?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ===== SYSTEM CARD HOVER EFFECTS =====
|
||||
const systemCards = document.querySelectorAll('.system-card');
|
||||
|
||||
systemCards.forEach(card => {
|
||||
card.addEventListener('mouseenter', function() {
|
||||
// Add subtle glow effect
|
||||
this.style.boxShadow = '0 25px 50px rgba(38, 166, 154, 0.2)';
|
||||
|
||||
// Animate internal components
|
||||
const flowItems = this.querySelectorAll('.flow-item, .phone-icon, .ki-processor, .crm-output');
|
||||
flowItems.forEach((item, index) => {
|
||||
setTimeout(() => {
|
||||
item.style.transform = 'translateY(-2px)';
|
||||
setTimeout(() => {
|
||||
item.style.transform = 'translateY(0)';
|
||||
}, 200);
|
||||
}, index * 100);
|
||||
});
|
||||
});
|
||||
|
||||
card.addEventListener('mouseleave', function() {
|
||||
this.style.boxShadow = '0 12px 30px rgba(79, 71, 71, 0.1)';
|
||||
});
|
||||
});
|
||||
|
||||
// ===== DATA CARD ANIMATIONS =====
|
||||
const dataCards = document.querySelectorAll('.data-card');
|
||||
|
||||
const dataCardObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry, index) => {
|
||||
if (entry.isIntersecting) {
|
||||
setTimeout(() => {
|
||||
entry.target.style.animation = 'fadeInUp 0.6s ease forwards';
|
||||
}, index * 100);
|
||||
dataCardObserver.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
dataCards.forEach(card => {
|
||||
dataCardObserver.observe(card);
|
||||
});
|
||||
|
||||
// ===== SMOOTH SCROLL FOR INTERNAL LINKS =====
|
||||
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'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ===== UTILITY ANIMATIONS =====
|
||||
// Add CSS animations dynamically
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -20px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rippleEffect {
|
||||
from {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
to {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); opacity: 0; }
|
||||
50% { opacity: 1; }
|
||||
100% { transform: translateX(100%) translateY(100%) rotate(45deg); opacity: 0; }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// ===== PERFORMANCE OPTIMIZATION =====
|
||||
// Throttle scroll events
|
||||
let ticking = false;
|
||||
function requestTick() {
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(updateScrollEffects);
|
||||
ticking = true;
|
||||
setTimeout(() => { ticking = false; }, 16);
|
||||
}
|
||||
}
|
||||
|
||||
function updateScrollEffects() {
|
||||
// Add scroll-based effects here if needed
|
||||
// Parallax, fade-ins, etc.
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', requestTick, { passive: true });
|
||||
|
||||
// ===== INITIALIZATION COMPLETE =====
|
||||
console.log('Tech-Onepager initialized successfully');
|
||||
});
|
||||
Reference in New Issue
Block a user