123 lines
3.5 KiB
TypeScript
123 lines
3.5 KiB
TypeScript
// directives/intersectionTransition.ts
|
|
import { Directive } from 'vue'
|
|
|
|
interface TransitionOptions {
|
|
type?: string
|
|
duration?: number | string
|
|
easing?: string
|
|
delay?: number | string
|
|
opacity?: number | string
|
|
transform?: string
|
|
threshold?: number
|
|
}
|
|
|
|
interface IntersectionElement extends HTMLElement {
|
|
_observer?: IntersectionObserver
|
|
_options?: TransitionOptions
|
|
}
|
|
|
|
function setupTransition(el: IntersectionElement, binding: any) {
|
|
// Handle different binding value types
|
|
let options: TransitionOptions = {}
|
|
|
|
if (typeof binding.value === 'string') {
|
|
// Simple string usage: v-show-on-intersect="'fade-up'"
|
|
options = { type: binding.value }
|
|
} else if (typeof binding.value === 'object') {
|
|
// Object usage: v-show-on-intersect="{ type: 'fade-up', duration: '2s', transform: 'translateX(-100px)' }"
|
|
options = { ...binding.value }
|
|
}
|
|
|
|
// Set defaults
|
|
const {
|
|
type = 'fade-up',
|
|
duration = '1s',
|
|
easing = 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
delay = '0s',
|
|
opacity = '0',
|
|
transform,
|
|
threshold = 0.1
|
|
} = options
|
|
|
|
el._options = options
|
|
|
|
// Get initial transform (custom or preset)
|
|
const initialTransform = transform || getInitialTransform(type)
|
|
|
|
// Initially hide the element
|
|
el.style.opacity = String(opacity)
|
|
el.style.transform = initialTransform
|
|
el.style.transition = `all ${duration} ${easing} ${delay}`
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
// Show element
|
|
el.style.opacity = '1'
|
|
el.style.transform = 'none'
|
|
} else {
|
|
// Hide element
|
|
el.style.opacity = String(opacity)
|
|
el.style.transform = initialTransform
|
|
}
|
|
})
|
|
}, {
|
|
threshold: threshold as number
|
|
})
|
|
|
|
el._observer = observer
|
|
observer.observe(el)
|
|
}
|
|
|
|
export const intersectionTransition: Directive = {
|
|
mounted(el: IntersectionElement, binding) {
|
|
setupTransition(el, binding)
|
|
},
|
|
|
|
updated(el: IntersectionElement, binding) {
|
|
// Handle dynamic updates to the directive value
|
|
if (JSON.stringify(binding.value) !== JSON.stringify(binding.oldValue)) {
|
|
// Disconnect old observer and setup with new options
|
|
if (el._observer) {
|
|
el._observer.disconnect()
|
|
}
|
|
setupTransition(el, binding)
|
|
}
|
|
},
|
|
|
|
unmounted(el: IntersectionElement) {
|
|
if (el._observer) {
|
|
el._observer.disconnect()
|
|
}
|
|
}
|
|
}
|
|
|
|
function getInitialTransform(transitionType: string): string {
|
|
switch (transitionType) {
|
|
case 'fade-up':
|
|
return 'translateY(5em)'
|
|
case 'fade-down':
|
|
return 'translateY(-5em)'
|
|
case 'slide-left':
|
|
return 'translateX(5em)'
|
|
case 'slide-right':
|
|
return 'translateX(-5em)'
|
|
case 'scale':
|
|
return 'scale(0.8)'
|
|
case 'scale-up':
|
|
return 'scale(1.2)'
|
|
case 'rotate':
|
|
return 'rotate(180deg)'
|
|
case 'rotate-left':
|
|
return 'translateY(100px) rotate(12deg) scale(0.9)'
|
|
case 'rotate-right':
|
|
return 'translateY(100px) rotate(-12deg) scale(0.9)'
|
|
case 'flip-x':
|
|
return 'rotateX(90deg)'
|
|
case 'flip-y':
|
|
return 'rotateY(90deg)'
|
|
default:
|
|
return ''
|
|
}
|
|
}
|