
vue3 实现进入离开视口的监听 v-viewport 指令
进入视口时的回调(
onEnter
)。离开视口时的回调(
onLeave
)。
指令代码
// src/directives/viewport.ts
import type { App, Directive, DirectiveBinding } from 'vue'
// 定义回调函数类型
type ViewportCallback = (entry: IntersectionObserverEntry) => void
// 定义指令配置选项接口
interface ViewportOptions {
threshold?: number | number[] // 触发回调的可见比例阈值
root?: Element | null // 观察器的根元素
rootMargin?: string // 根元素的边距
once?: boolean // 是否只触发一次
}
// 扩展 HTMLElement,添加自定义属性
interface ViewportElement extends HTMLElement {
_observer?: IntersectionObserver
_options?: ViewportOptions
_onEnterCallback?: ViewportCallback
_onLeaveCallback?: ViewportCallback
}
// 默认配置
const DEFAULT_OPTIONS: ViewportOptions = {
threshold: 0.1, // 默认当元素10%可见时触发
root: null, // 默认使用浏览器视口
rootMargin: '0px', // 默认无边距
once: false // 默认持续观察
}
// 创建指令
export const vViewport: Directive<ViewportElement, [ViewportCallback, ViewportCallback?, ViewportOptions?]> = {
mounted(el: ViewportElement, binding: DirectiveBinding) {
// 解析回调函数和配置选项
const onEnter = binding.value[0]
const onLeave = binding.value[1] || (() => {}) // 如果没有提供离开回调,使用空函数
const userOptions = binding.value[2] || {}
// 合并配置选项
const options = { ...DEFAULT_OPTIONS, ...userOptions }
// 存储回调和选项,用于更新时比较
el._onEnterCallback = onEnter
el._onLeaveCallback = onLeave
el._options = options
// 创建观察器实例
const observer = new IntersectionObserver((entries: IntersectionObserverEntry[]) => {
entries.forEach((entry: IntersectionObserverEntry) => {
if (entry.isIntersecting) {
// 进入视口时触发 onEnter 回调
onEnter(entry)
// 如果设置了 once 选项,在首次触发后取消观察
if (options.once) {
observer.unobserve(el)
}
} else {
// 离开视口时触发 onLeave 回调
onLeave(entry)
}
})
}, {
threshold: options.threshold,
root: options.root,
rootMargin: options.rootMargin
})
// 开始观察元素
observer.observe(el)
el._observer = observer
},
// 更新时进行配置比较
updated(el: ViewportElement, binding: DirectiveBinding) {
const newOnEnter = binding.value[0]
const newOnLeave = binding.value[1] || (() => {})
const newOptions = binding.value[2] || {}
// 检查回调或配置是否发生变化
const optionsChanged = JSON.stringify(newOptions) !== JSON.stringify(el._options)
const onEnterChanged = newOnEnter !== el._onEnterCallback
const onLeaveChanged = newOnLeave !== el._onLeaveCallback
// 如果有变化,重新创建观察器
if (optionsChanged || onEnterChanged || onLeaveChanged) {
// 清理旧的观察器
el._observer?.disconnect()
// 重新挂载指令
vViewport.mounted!(el, binding)
}
},
// 组件卸载时清理资源
unmounted(el: ViewportElement) {
if (el._observer) {
el._observer.disconnect()
delete el._observer
delete el._onEnterCallback
delete el._onLeaveCallback
delete el._options
}
}
}
// 注册指令的函数
export const registerViewportDirective = (app: App): void => {
app.directive('viewport', vViewport)
}
// 默认导出注册函数
export default registerViewportDirective
使用方法
1. 注册指令
在 main.ts
中注册指令:
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import registerViewportDirective from './directives/viewport'
const app = createApp(App)
registerViewportDirective(app)
app.mount('#app')
2. 在组件中使用
基本用法:监听进入和离开视口
<template>
<div
v-viewport="[onEnterViewport, onLeaveViewport]"
class="target-element"
>
监听进入和离开视口
</div>
</template>
<script setup lang="ts">
// 定义进入视口的回调
const onEnterViewport = (entry: IntersectionObserverEntry) => {
console.log('元素进入视口', entry.target)
entry.target.classList.add('visible')
}
// 定义离开视口的回调
const onLeaveViewport = (entry: IntersectionObserverEntry) => {
console.log('元素离开视口', entry.target)
entry.target.classList.remove('visible')
}
</script>
<style scoped>
.target-element {
min-height: 200px;
background: #f0f0f0;
margin: 20px 0;
transition: opacity 0.3s ease;
opacity: 0;
}
.target-element.visible {
opacity: 1;
}
</style>
配置选项:自定义阈值和行为
<template>
<div
v-viewport="[onEnterViewport, onLeaveViewport, { threshold: 0.5, rootMargin: '50px', once: false }]"
class="target-element"
>
配置选项示例
</div>
</template>
<script setup lang="ts">
// 定义进入视口的回调
const onEnterViewport = (entry: IntersectionObserverEntry) => {
console.log('元素进入视口,50% 可见时触发')
entry.target.classList.add('visible')
}
// 定义离开视口的回调
const onLeaveViewport = (entry: IntersectionObserverEntry) => {
console.log('元素离开视口')
entry.target.classList.remove('visible')
}
</script>
配置选项说明
v-viewport
指令支持以下配置选项:
示例效果
进入视口时:元素会添加
visible
类,触发动画或其他效果。离开视口时:元素会移除
visible
类,恢复初始状态。
总结
通过扩展 v-viewport
指令,我们不仅可以监听元素进入视口,还可以监听元素离开视口。这使得指令更加灵活,适用于更多场景,例如:
动画的触发和回退。
曝光统计和离开统计。
动态加载和卸载内容。
希望本文对你有所帮助!如果你有任何问题或建议,欢迎留言交流 😊
本文是原创文章,完整转载请注明来自 俞泊
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果