Files
hunli/vite-tailwindcss/src/directives/reveal.js
2026-07-30 18:00:27 +08:00

60 lines
1.5 KiB
JavaScript

import { ref, onMounted, onUnmounted } from 'vue'
/**
* v-reveal 滚动入场指令
*
* 用法:
* v-reveal -> 默认向上淡入
* v-reveal="'left'" -> 从左侧滑入
* v-reveal="{ variant: 'scale', delay: 200 }"
*
* 支持的 variant: up | down | left | right | scale | blur
*/
const VARIANTS = {
up: 'reveal-up',
down: 'reveal-down',
left: 'reveal-left',
right: 'reveal-right',
scale: 'reveal-scale',
blur: 'reveal-blur',
}
function parseBinding(value) {
if (!value) return { variant: 'up', delay: 0 }
if (typeof value === 'string') return { variant: value, delay: 0 }
return {
variant: value.variant || 'up',
delay: value.delay || 0,
}
}
const reveal = {
mounted(el, binding) {
const { variant, delay } = parseBinding(binding.value)
el.classList.add('reveal')
el.classList.add(VARIANTS[variant] || 'reveal-up')
if (delay) el.style.transitionDelay = `${delay}ms`
// 已处于视口(如首屏)时立即显示,避免永远不可见
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
el.classList.add('reveal--in')
observer.unobserve(el)
}
},
{ threshold: 0.12, rootMargin: '0px 0px -8% 0px' }
)
observer.observe(el)
el.__revealObserver = observer
},
unmounted(el) {
if (el.__revealObserver) {
el.__revealObserver.disconnect()
delete el.__revealObserver
}
},
}
export default reveal