96 lines
2.4 KiB
JavaScript
96 lines
2.4 KiB
JavaScript
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
|
|
export const useIntersectionAnimations = (threshold = 0.1) => {
|
|
const elements = ref([])
|
|
let observer = null
|
|
|
|
const observerOptions = {
|
|
threshold,
|
|
rootMargin: '0px 0px -100px 0px'
|
|
}
|
|
|
|
const animateElement = (entry) => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('animate-in')
|
|
entry.target.style.opacity = '1'
|
|
entry.target.style.transform = 'translateY(0)'
|
|
|
|
// Unobserve after animation to improve performance
|
|
if (observer) {
|
|
observer.unobserve(entry.target)
|
|
}
|
|
}
|
|
}
|
|
|
|
const observeElements = () => {
|
|
// Check for reduced motion preference
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
|
|
if (prefersReducedMotion) {
|
|
// Skip animations for users who prefer reduced motion
|
|
document.querySelectorAll('[data-animate]').forEach(el => {
|
|
el.style.opacity = '1'
|
|
el.style.transform = 'none'
|
|
})
|
|
return
|
|
}
|
|
|
|
observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
animateElement(entry)
|
|
})
|
|
}, observerOptions)
|
|
|
|
// Find all elements with data-animate attribute
|
|
document.querySelectorAll('[data-animate]').forEach(el => {
|
|
// Set initial state
|
|
el.style.opacity = '0'
|
|
el.style.transition = 'all 0.6s cubic-bezier(0.4, 0, 0.2, 1)'
|
|
|
|
const animationType = el.dataset.animate
|
|
|
|
switch (animationType) {
|
|
case 'fade-up':
|
|
el.style.transform = 'translateY(30px)'
|
|
break
|
|
case 'fade-in':
|
|
// Just opacity, no transform
|
|
break
|
|
case 'scale-in':
|
|
el.style.transform = 'scale(0.95)'
|
|
break
|
|
case 'slide-left':
|
|
el.style.transform = 'translateX(50px)'
|
|
break
|
|
case 'slide-right':
|
|
el.style.transform = 'translateX(-50px)'
|
|
break
|
|
default:
|
|
el.style.transform = 'translateY(20px)'
|
|
}
|
|
|
|
// Add delay if specified
|
|
if (el.dataset.animateDelay) {
|
|
el.style.transitionDelay = el.dataset.animateDelay
|
|
}
|
|
|
|
observer.observe(el)
|
|
elements.value.push(el)
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
// Wait for DOM to be ready
|
|
setTimeout(observeElements, 100)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
if (observer) {
|
|
observer.disconnect()
|
|
}
|
|
})
|
|
|
|
return {
|
|
elements
|
|
}
|
|
} |