public pages content

This commit is contained in:
2025-06-24 09:32:50 +02:00
parent 10560a01fb
commit fed00ff2a0
21 changed files with 2897 additions and 1010 deletions

View File

@@ -0,0 +1,138 @@
<!-- Animated Numbers Component -->
<div class="stats-section">
<div class="container">
<div class="row text-center">
{% for stat in stats %}
<div class="col-md-{{ 12 // stats|length }}">
<div class="stat-item">
<span class="stat-number" data-value="{{ stat.value }}" data-suffix="{{ stat.suffix }}">{{ stat.display }}</span>
<div class="stat-label">{{ stat.label }}</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<style>
.stats-section {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
color: white;
padding: 80px 0;
position: relative;
overflow: hidden;
}
.stats-section::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="50" cy="50" r="1" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.stat-item {
text-align: center;
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.stat-number {
font-size: 3rem;
font-weight: 700;
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 100%;
}
.stat-label {
font-size: 1.1rem;
opacity: 0.9;
text-align: center;
}
</style>
<script>
// Function to animate number counting
function animateNumber(element, endValue, suffix = '', duration = 2000) {
const start = performance.now();
const startValue = 0;
const difference = endValue - startValue;
function updateNumber(currentTime) {
const elapsed = currentTime - start;
const progress = Math.min(elapsed / duration, 1);
// Easing function for smooth animation
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
const currentValue = startValue + (difference * easeOutQuart);
// Format the number based on the suffix
let displayValue;
if (suffix === '%') {
displayValue = currentValue.toFixed(1) + suffix;
} else if (suffix === '-bit') {
displayValue = Math.round(currentValue) + suffix;
} else if (suffix === '/7') {
displayValue = Math.round(currentValue) + suffix;
} else if (suffix === '+') {
displayValue = Math.round(currentValue) + suffix;
} else if (suffix === 'K+') {
displayValue = Math.round(currentValue) + suffix;
} else {
displayValue = Math.round(currentValue) + (suffix || '');
}
element.textContent = displayValue;
if (progress < 1) {
requestAnimationFrame(updateNumber);
} else {
// Ensure the final value is correct
element.textContent = element.getAttribute('data-value') + (suffix || '');
}
}
requestAnimationFrame(updateNumber);
}
// Initialize animated numbers when component is loaded
document.addEventListener('DOMContentLoaded', function() {
const statsObserver = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const statNumbers = entry.target.querySelectorAll('.stat-number');
statNumbers.forEach((stat, index) => {
setTimeout(() => {
const value = parseFloat(stat.getAttribute('data-value'));
const suffix = stat.getAttribute('data-suffix') || '';
if (!isNaN(value)) {
animateNumber(stat, value, suffix, 2000);
}
}, index * 300); // Stagger the animations
});
// Only trigger once
statsObserver.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
// Observe the stats section
const statsSection = document.querySelector('.stats-section');
if (statsSection) {
statsObserver.observe(statsSection);
}
});
</script>