- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
244 lines
8.0 KiB
JavaScript
244 lines
8.0 KiB
JavaScript
// modules/canvas-animations/ScrollEffects.js
|
|
import { CanvasManager } from './CanvasManager.js';
|
|
import { useEvent } from '../../core/useEvent.js';
|
|
import { Logger } from '../../core/logger.js';
|
|
|
|
/**
|
|
* Scroll-based Canvas Effects - Parallax and scroll animations
|
|
*/
|
|
export const ScrollEffects = {
|
|
|
|
/**
|
|
* Initialize parallax canvas effect
|
|
*/
|
|
initParallax(canvas, config) {
|
|
const manager = new CanvasManager(canvas);
|
|
const elements = this.createParallaxElements(canvas, config);
|
|
|
|
let ticking = false;
|
|
|
|
const updateParallax = () => {
|
|
if (!ticking) {
|
|
requestAnimationFrame(() => {
|
|
this.renderParallax(manager, elements, config);
|
|
ticking = false;
|
|
});
|
|
ticking = true;
|
|
}
|
|
};
|
|
|
|
// Listen to scroll events
|
|
useEvent(window, 'scroll', updateParallax, 'scroll-parallax');
|
|
useEvent(window, 'resize', updateParallax, 'scroll-parallax');
|
|
|
|
// Initial render
|
|
updateParallax();
|
|
|
|
Logger.info('[ScrollEffects] Parallax initialized with', elements.length, 'elements');
|
|
},
|
|
|
|
/**
|
|
* Create parallax elements based on config
|
|
*/
|
|
createParallaxElements(canvas, config) {
|
|
const elements = [];
|
|
const layerCount = config.layers || 3;
|
|
const elementCount = config.elements || 20;
|
|
|
|
for (let i = 0; i < elementCount; i++) {
|
|
elements.push({
|
|
x: Math.random() * canvas.clientWidth,
|
|
y: Math.random() * canvas.clientHeight * 2, // Allow elements outside viewport
|
|
size: Math.random() * 20 + 5,
|
|
layer: Math.floor(Math.random() * layerCount),
|
|
speed: 0.1 + (Math.random() * 0.5), // Different parallax speeds
|
|
opacity: Math.random() * 0.7 + 0.3,
|
|
color: this.getLayerColor(Math.floor(Math.random() * layerCount), config)
|
|
});
|
|
}
|
|
|
|
return elements;
|
|
},
|
|
|
|
/**
|
|
* Get color for parallax layer
|
|
*/
|
|
getLayerColor(layer, config) {
|
|
const colors = config.colors || [
|
|
'rgba(100, 150, 255, 0.6)', // Front layer - more opaque
|
|
'rgba(150, 100, 255, 0.4)', // Middle layer
|
|
'rgba(200, 100, 150, 0.2)' // Back layer - more transparent
|
|
];
|
|
return colors[layer] || colors[0];
|
|
},
|
|
|
|
/**
|
|
* Render parallax effect
|
|
*/
|
|
renderParallax(manager, elements, config) {
|
|
manager.clear();
|
|
|
|
const scrollY = window.pageYOffset;
|
|
const canvasRect = manager.canvas.getBoundingClientRect();
|
|
const canvasTop = canvasRect.top + scrollY;
|
|
|
|
// Calculate relative scroll position
|
|
const relativeScroll = scrollY - canvasTop;
|
|
const scrollProgress = relativeScroll / window.innerHeight;
|
|
|
|
elements.forEach(element => {
|
|
// Apply parallax offset based on layer and scroll
|
|
const parallaxOffset = scrollProgress * element.speed * 100;
|
|
const y = element.y - parallaxOffset;
|
|
|
|
// Only render elements that are potentially visible
|
|
if (y > -element.size && y < manager.canvas.clientHeight + element.size) {
|
|
manager.ctx.save();
|
|
manager.ctx.globalAlpha = element.opacity;
|
|
manager.ctx.fillStyle = element.color;
|
|
manager.ctx.beginPath();
|
|
manager.ctx.arc(element.x, y, element.size, 0, Math.PI * 2);
|
|
manager.ctx.fill();
|
|
manager.ctx.restore();
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Initialize scroll-based animations
|
|
*/
|
|
initScrollAnimation(canvas, config) {
|
|
const manager = new CanvasManager(canvas);
|
|
const animationType = config.animation || 'wave';
|
|
|
|
let ticking = false;
|
|
|
|
const updateAnimation = () => {
|
|
if (!ticking) {
|
|
requestAnimationFrame(() => {
|
|
this.renderScrollAnimation(manager, animationType, config);
|
|
ticking = false;
|
|
});
|
|
ticking = true;
|
|
}
|
|
};
|
|
|
|
useEvent(window, 'scroll', updateAnimation, 'scroll-animation');
|
|
useEvent(window, 'resize', updateAnimation, 'scroll-animation');
|
|
|
|
// Initial render
|
|
updateAnimation();
|
|
|
|
Logger.info('[ScrollEffects] Scroll animation initialized:', animationType);
|
|
},
|
|
|
|
/**
|
|
* Render scroll-based animations
|
|
*/
|
|
renderScrollAnimation(manager, animationType, config) {
|
|
manager.clear();
|
|
|
|
const scrollY = window.pageYOffset;
|
|
const canvasRect = manager.canvas.getBoundingClientRect();
|
|
const canvasTop = canvasRect.top + scrollY;
|
|
const relativeScroll = scrollY - canvasTop;
|
|
const scrollProgress = Math.max(0, Math.min(1, relativeScroll / window.innerHeight));
|
|
|
|
switch (animationType) {
|
|
case 'wave':
|
|
this.renderWaveAnimation(manager, scrollProgress, config);
|
|
break;
|
|
case 'progress':
|
|
this.renderProgressAnimation(manager, scrollProgress, config);
|
|
break;
|
|
case 'morph':
|
|
this.renderMorphAnimation(manager, scrollProgress, config);
|
|
break;
|
|
default:
|
|
this.renderWaveAnimation(manager, scrollProgress, config);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Render wave animation based on scroll
|
|
*/
|
|
renderWaveAnimation(manager, progress, config) {
|
|
const { ctx } = manager;
|
|
const { width, height } = manager.getSize();
|
|
|
|
ctx.strokeStyle = config.color || 'rgba(100, 150, 255, 0.8)';
|
|
ctx.lineWidth = config.lineWidth || 3;
|
|
|
|
ctx.beginPath();
|
|
|
|
const amplitude = (config.amplitude || 50) * progress;
|
|
const frequency = config.frequency || 0.02;
|
|
const phase = progress * Math.PI * 2;
|
|
|
|
for (let x = 0; x <= width; x += 2) {
|
|
const y = height / 2 + Math.sin(x * frequency + phase) * amplitude;
|
|
|
|
if (x === 0) {
|
|
ctx.moveTo(x, y);
|
|
} else {
|
|
ctx.lineTo(x, y);
|
|
}
|
|
}
|
|
|
|
ctx.stroke();
|
|
},
|
|
|
|
/**
|
|
* Render progress bar animation
|
|
*/
|
|
renderProgressAnimation(manager, progress, config) {
|
|
const { ctx } = manager;
|
|
const { width, height } = manager.getSize();
|
|
|
|
const barHeight = config.barHeight || 10;
|
|
const y = height / 2 - barHeight / 2;
|
|
|
|
// Background
|
|
ctx.fillStyle = config.backgroundColor || 'rgba(255, 255, 255, 0.2)';
|
|
ctx.fillRect(0, y, width, barHeight);
|
|
|
|
// Progress
|
|
ctx.fillStyle = config.color || 'rgba(100, 150, 255, 0.8)';
|
|
ctx.fillRect(0, y, width * progress, barHeight);
|
|
},
|
|
|
|
/**
|
|
* Render morphing shapes
|
|
*/
|
|
renderMorphAnimation(manager, progress, config) {
|
|
const { ctx } = manager;
|
|
const { width, height } = manager.getSize();
|
|
|
|
ctx.fillStyle = config.color || 'rgba(100, 150, 255, 0.6)';
|
|
|
|
const centerX = width / 2;
|
|
const centerY = height / 2;
|
|
const maxRadius = Math.min(width, height) / 3;
|
|
|
|
ctx.beginPath();
|
|
|
|
const points = config.points || 6;
|
|
for (let i = 0; i <= points; i++) {
|
|
const angle = (i / points) * Math.PI * 2;
|
|
const radiusVariation = Math.sin(progress * Math.PI * 4 + angle * 3) * 0.3 + 1;
|
|
const radius = maxRadius * progress * radiusVariation;
|
|
|
|
const x = centerX + Math.cos(angle) * radius;
|
|
const y = centerY + Math.sin(angle) * radius;
|
|
|
|
if (i === 0) {
|
|
ctx.moveTo(x, y);
|
|
} else {
|
|
ctx.lineTo(x, y);
|
|
}
|
|
}
|
|
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
}; |