Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
212 lines
5.9 KiB
JavaScript
212 lines
5.9 KiB
JavaScript
/**
|
|
* Scroll Animation
|
|
*
|
|
* Handles various scroll-based animations: fade-in, zoom-in, parallax, sticky-fade, sticky-steps
|
|
*/
|
|
|
|
import { Logger } from '../../core/logger.js';
|
|
|
|
/**
|
|
* ScrollAnimation - Individual scroll animation
|
|
*/
|
|
export class ScrollAnimation {
|
|
constructor(element, config = {}) {
|
|
this.element = element;
|
|
this.config = {
|
|
type: config.type || 'fade-in',
|
|
offset: config.offset || 0.85,
|
|
delay: config.delay || 0,
|
|
once: config.once !== false,
|
|
speed: config.speed || 0.5,
|
|
fadeStart: config.fadeStart || 0,
|
|
fadeEnd: config.fadeEnd || 1,
|
|
steps: config.steps || 3,
|
|
...config
|
|
};
|
|
|
|
this.triggered = false;
|
|
this.needsUpdate = true;
|
|
|
|
// Initialize based on type
|
|
this.init();
|
|
}
|
|
|
|
/**
|
|
* Initialize animation
|
|
*/
|
|
init() {
|
|
switch (this.config.type) {
|
|
case 'fade-in':
|
|
case 'zoom-in':
|
|
this.initFadeIn();
|
|
break;
|
|
case 'parallax':
|
|
this.initParallax();
|
|
break;
|
|
case 'sticky-fade':
|
|
this.initStickyFade();
|
|
break;
|
|
case 'sticky-steps':
|
|
this.initStickySteps();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize fade-in animation
|
|
*/
|
|
initFadeIn() {
|
|
this.element.style.opacity = '0';
|
|
this.element.style.transition = `opacity 0.6s ease, transform 0.6s ease`;
|
|
this.element.style.transitionDelay = `${this.config.delay}s`;
|
|
|
|
if (this.config.type === 'zoom-in') {
|
|
this.element.style.transform = 'scale(0.9)';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize parallax animation
|
|
*/
|
|
initParallax() {
|
|
// Parallax doesn't need initial setup
|
|
this.needsUpdate = true;
|
|
}
|
|
|
|
/**
|
|
* Initialize sticky fade animation
|
|
*/
|
|
initStickyFade() {
|
|
this.element.style.position = 'sticky';
|
|
this.element.style.top = '0';
|
|
}
|
|
|
|
/**
|
|
* Initialize sticky steps animation
|
|
*/
|
|
initStickySteps() {
|
|
this.element.style.position = 'sticky';
|
|
this.element.style.top = '0';
|
|
}
|
|
|
|
/**
|
|
* Enter animation (element enters viewport)
|
|
*/
|
|
enter() {
|
|
if (this.triggered && this.config.once) {
|
|
return;
|
|
}
|
|
|
|
this.triggered = true;
|
|
|
|
switch (this.config.type) {
|
|
case 'fade-in':
|
|
this.element.style.opacity = '1';
|
|
this.element.classList.add('visible', 'entered');
|
|
break;
|
|
case 'zoom-in':
|
|
this.element.style.opacity = '1';
|
|
this.element.style.transform = 'scale(1)';
|
|
this.element.classList.add('visible', 'entered');
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exit animation (element exits viewport)
|
|
*/
|
|
exit() {
|
|
if (this.config.once) {
|
|
return;
|
|
}
|
|
|
|
this.triggered = false;
|
|
|
|
switch (this.config.type) {
|
|
case 'fade-in':
|
|
this.element.style.opacity = '0';
|
|
this.element.classList.remove('visible', 'entered');
|
|
break;
|
|
case 'zoom-in':
|
|
this.element.style.opacity = '0';
|
|
this.element.style.transform = 'scale(0.9)';
|
|
this.element.classList.remove('visible', 'entered');
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update animation (for scroll-based animations)
|
|
*/
|
|
update() {
|
|
const rect = this.element.getBoundingClientRect();
|
|
const viewportHeight = window.innerHeight;
|
|
const scrollY = window.scrollY;
|
|
|
|
switch (this.config.type) {
|
|
case 'parallax':
|
|
this.updateParallax(rect, scrollY);
|
|
break;
|
|
case 'sticky-fade':
|
|
this.updateStickyFade(rect, viewportHeight);
|
|
break;
|
|
case 'sticky-steps':
|
|
this.updateStickySteps(rect, viewportHeight, scrollY);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update parallax animation
|
|
*/
|
|
updateParallax(rect, scrollY) {
|
|
const elementTop = rect.top + scrollY;
|
|
const scrolled = scrollY - elementTop;
|
|
const translateY = scrolled * this.config.speed;
|
|
|
|
this.element.style.transform = `translateY(${translateY}px)`;
|
|
}
|
|
|
|
/**
|
|
* Update sticky fade animation
|
|
*/
|
|
updateStickyFade(rect, viewportHeight) {
|
|
const progress = Math.max(0, Math.min(1, (viewportHeight - rect.top) / viewportHeight));
|
|
const opacity = this.config.fadeStart + (this.config.fadeEnd - this.config.fadeStart) * progress;
|
|
|
|
this.element.style.opacity = opacity.toString();
|
|
}
|
|
|
|
/**
|
|
* Update sticky steps animation
|
|
*/
|
|
updateStickySteps(rect, viewportHeight, scrollY) {
|
|
const elementTop = rect.top + scrollY - viewportHeight;
|
|
const scrollProgress = Math.max(0, Math.min(1, scrollY / (rect.height + viewportHeight)));
|
|
const step = Math.floor(scrollProgress * this.config.steps);
|
|
|
|
this.element.setAttribute('data-step', step.toString());
|
|
this.element.classList.remove('step-0', 'step-1', 'step-2', 'step-3', 'step-4', 'step-5');
|
|
this.element.classList.add(`step-${step}`);
|
|
}
|
|
|
|
/**
|
|
* Destroy animation
|
|
*/
|
|
destroy() {
|
|
// Reset styles
|
|
this.element.style.opacity = '';
|
|
this.element.style.transform = '';
|
|
this.element.style.transition = '';
|
|
this.element.style.transitionDelay = '';
|
|
this.element.style.position = '';
|
|
this.element.style.top = '';
|
|
|
|
// Remove classes
|
|
this.element.classList.remove('visible', 'entered');
|
|
|
|
this.triggered = false;
|
|
}
|
|
}
|
|
|