hypnagaga_old/src/assets/scripts/bundle/gsap-shortcode-init.js
2026-01-08 01:35:35 -05:00

165 lines
5 KiB
JavaScript

/**
Initialize GSAP on pages with Turbo frames */
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
import { shouldAnimate, punchIn, punchOut } from './gsap-animations.js';
// Register GSAP plugins
gsap.registerPlugin(ScrollTrigger);
// Register GSDevTools in development only (esbuild inlines ELEVENTY_ENV at build time)
if (process.env.ELEVENTY_ENV === 'development') {
try {
// Dynamic import wrapped in try-catch; GSDevTools package must be installed
import('gsap/GSDevTools').then(module => {
gsap.registerPlugin(module.default);
}).catch(() => {
// Silently continue if GSDevTools is not available
});
} catch (e) {
// Fallback for any import errors
}
}
// Store active contexts for cleanup
const activeContexts = new Map();
/**
* Initialize GSAP animations for all containers
*/
function initGsapAnimations() {
// Check if reduced motion is preferred
if (!shouldAnimate()) {
// Skip animations if user prefers reduced motion
console.log('GSAP animations disabled: prefers-reduced-motion');
return;
}
// Find all GSAP animation containers
const containers = document.querySelectorAll('[data-gsap-scroll-anim]');
containers.forEach(container => {
try {
// Parse configuration from data attribute
const config = JSON.parse(container.dataset.gsapScrollAnim);
// Extract configuration with defaults
const {
animationType,
scrollStart = 'top 80%',
scrollEnd = 'bottom 20%',
scrub = true,
pin = true,
markers = false
} = config;
// Find all animated items within container
const items = container.querySelectorAll('.gsap-item');
if (items.length === 0) return;
// Create GSAP context for this container
const ctx = gsap.context(() => {
let timeline;
if (animationType === 'punchIn' || animationType === 'punchOut') {
const targetElements = container.querySelectorAll('.gsap-image');
const anim = animationType === 'punchIn'
? punchIn(targetElements, config)
: punchOut(targetElements, config);
// Create timeline with ScrollTrigger
timeline = gsap.timeline({
scrollTrigger: {
trigger: container,
start: scrollStart,
end: scrollEnd,
scrub: scrub ? 1 : false,
markers: markers,
pin: pin,
toggleActions: scrub ? undefined : 'play reverse play reverse',
onEnter: () => container.dataset.gsapActive = 'true',
onLeave: () => container.dataset.gsapActive = 'false',
onEnterBack: () => container.dataset.gsapActive = 'true',
onLeaveBack: () => container.dataset.gsapActive = 'false'
}
});
// Apply animation
if (anim.from && anim.to) {
timeline.fromTo(targetElements, anim.from, anim.to);
} else if (anim.from) {
timeline.from(targetElements, anim.from);
} else if (anim.to) {
timeline.to(targetElements, anim.to);
}
} else {
console.warn('Unsupported animation type. Only zoomIn/zoomOut are available.', config);
return;
}
}, container);
// Store context for cleanup
activeContexts.set(container, ctx);
} catch (error) {
console.error('Error initializing GSAP animation:', error, container);
}
});
console.log(`Initialized ${activeContexts.size} GSAP scroll animations`);
}
/**
* Cleanup all active animations
*/
function cleanupGsapAnimations() {
activeContexts.forEach((ctx, container) => {
ctx.revert(); // Reverts all GSAP changes made in this context
container.removeAttribute('data-gsap-active');
});
activeContexts.clear();
// Kill all ScrollTriggers
ScrollTrigger.getAll().forEach(trigger => trigger.kill());
}
/**
* Refresh ScrollTrigger calculations
* Useful when content height changes
*/
function refreshScrollTrigger() {
ScrollTrigger.refresh();
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', initGsapAnimations);
// Turbo Drive compatibility
if (window.Turbo) {
// Clean up before navigation
document.addEventListener('turbo:before-render', cleanupGsapAnimations);
// Re-initialize after navigation
document.addEventListener('turbo:render', initGsapAnimations);
document.addEventListener('turbo:load', () => {
// Slight delay to ensure DOM is ready
setTimeout(initGsapAnimations, 100);
});
// Refresh on Turbo frame load
document.addEventListener('turbo:frame-load', refreshScrollTrigger);
}
// Handle window resize
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
refreshScrollTrigger();
}, 250);
});
// Export for manual control if needed
export { initGsapAnimations, cleanupGsapAnimations, refreshScrollTrigger };