60 lines
1.5 KiB
JavaScript
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
|