87 lines
2.4 KiB
JavaScript
87 lines
2.4 KiB
JavaScript
/**
|
||
* v-reveal 滚动入场指令
|
||
*
|
||
* 用法:
|
||
* v-reveal -> 默认向上淡入
|
||
* v-reveal="'left'" -> 从左侧滑入
|
||
* v-reveal="{ variant: 'scale', delay: 200 }"
|
||
* v-reveal="{ variant: 'up', immediate: true }" -> 不建观察器,下一帧直接入场(串行挂载用)
|
||
*
|
||
* 支持的 variant: up | down | left | right | scale | blur | postcard
|
||
*/
|
||
const VARIANTS = {
|
||
up: 'reveal-up',
|
||
down: 'reveal-down',
|
||
left: 'reveal-left',
|
||
right: 'reveal-right',
|
||
scale: 'reveal-scale',
|
||
blur: 'reveal-blur',
|
||
postcard: 'reveal-postcard',
|
||
}
|
||
|
||
function parseBinding(value) {
|
||
if (!value) return { variant: 'up', delay: 0, immediate: false }
|
||
if (typeof value === 'string') return { variant: value, delay: 0, immediate: false }
|
||
return {
|
||
variant: value.variant || 'up',
|
||
delay: value.delay || 0,
|
||
immediate: !!value.immediate,
|
||
}
|
||
}
|
||
|
||
const reveal = {
|
||
mounted(el, binding) {
|
||
// false:跳过入场(同模板不挂观察器)
|
||
if (binding.value === false) return
|
||
|
||
const { variant, delay, immediate } = parseBinding(binding.value)
|
||
el.classList.add('reveal')
|
||
el.classList.add(VARIANTS[variant] || 'reveal-up')
|
||
if (delay) el.style.transitionDelay = `${delay}ms`
|
||
|
||
// 串行挂载场景:父级已保证在视口内,免 IntersectionObserver
|
||
if (immediate) {
|
||
const kick = () => el.classList.add('reveal--in')
|
||
if (typeof requestAnimationFrame === 'function') {
|
||
el.__revealRaf = requestAnimationFrame(() => {
|
||
el.__revealRaf = requestAnimationFrame(() => {
|
||
el.__revealRaf = 0
|
||
kick()
|
||
})
|
||
})
|
||
} else {
|
||
el.__revealTimer = setTimeout(kick, 16)
|
||
}
|
||
return
|
||
}
|
||
|
||
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
|
||
}
|
||
if (el.__revealRaf) {
|
||
cancelAnimationFrame(el.__revealRaf)
|
||
el.__revealRaf = 0
|
||
}
|
||
if (el.__revealTimer) {
|
||
clearTimeout(el.__revealTimer)
|
||
el.__revealTimer = 0
|
||
}
|
||
},
|
||
}
|
||
|
||
export default reveal
|