瑶都万能墙
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

205 lines
5.2 KiB

/**
* 可见性监听混入
* 提供元素可见性监听功能,支持H5、微信小程序、App端
* 用于实现浏览记录等功能
*/
import { VIEWPORT_CONFIG, createBrowseRecordParams } from '@/config/browseConfig.js'
export default {
data() {
return {
hasRecordedView: false, // 标记是否已记录浏览
viewTimer: null, // 浏览计时器
isInViewport: false, // 是否在可视区域内
observer: null, // 观察者实例
}
},
mounted() {
// 组件挂载后,使用Intersection Observer监听可见性
this.observeVisibility()
},
beforeDestroy() {
// 组件销毁前清理observer和计时器
this.cleanupObserver()
},
methods: {
/**
* 监听元素可见性
* @param {String} selector - 要监听的元素选择器,默认为'.works'
* @param {Object} config - 配置参数
*/
observeVisibility(selector = '.works', config = {}) {
const {
threshold = VIEWPORT_CONFIG.THRESHOLD,
dwellTime = VIEWPORT_CONFIG.DWELL_TIME,
margins = VIEWPORT_CONFIG.VIEWPORT_MARGINS
} = config
// #ifdef H5
if (typeof IntersectionObserver !== 'undefined') {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.hasRecordedView) {
// 元素进入可视区域且未记录过浏览
this.handleViewportEnter(dwellTime)
} else if (!entry.isIntersecting && this.isInViewport) {
// 元素离开可视区域
this.handleViewportLeave()
}
})
}, {
threshold: threshold
})
this.observer.observe(this.$el)
} else {
// 降级方案:延迟记录浏览
setTimeout(() => {
if (!this.hasRecordedView) {
this.recordBrowseView()
}
}, dwellTime)
}
// #endif
// #ifdef MP-WEIXIN
// 使用微信小程序原生的IntersectionObserver API
this.observer = wx.createIntersectionObserver(this, {
thresholds: [threshold],
initialRatio: 0,
observeAll: false
})
// 指定页面显示区域作为参照区域
this.observer.relativeToViewport(margins)
// 开始监听目标节点
this.observer.observe(selector, (res) => {
if (res.intersectionRatio > threshold && !this.hasRecordedView) {
// 当元素达到配置阈值以上进入可视区域且未记录过浏览时,处理进入可视区域事件
this.handleViewportEnter(dwellTime)
} else if (res.intersectionRatio <= threshold && this.isInViewport) {
// 元素离开可视区域
this.handleViewportLeave()
}
})
// #endif
// #ifdef APP-PLUS
// App端使用uni-app的createIntersectionObserver
this.observer = uni.createIntersectionObserver(this, {
thresholds: [threshold],
initialRatio: 0,
observeAll: false
})
this.observer.relativeToViewport(margins)
this.observer.observe(selector, (res) => {
if (res.intersectionRatio > threshold && !this.hasRecordedView) {
this.handleViewportEnter(dwellTime)
} else if (res.intersectionRatio <= threshold && this.isInViewport) {
this.handleViewportLeave()
}
})
// #endif
},
/**
* 处理进入可视区域事件
* @param {Number} dwellTime - 停留时间
*/
handleViewportEnter(dwellTime = VIEWPORT_CONFIG.DWELL_TIME) {
if (this.hasRecordedView) return
this.isInViewport = true
// 设置延迟计时器,确保用户真正浏览了内容
this.viewTimer = setTimeout(() => {
if (this.isInViewport && !this.hasRecordedView) {
this.recordBrowseView()
}
}, dwellTime)
},
/**
* 处理离开可视区域事件
*/
handleViewportLeave() {
this.isInViewport = false
// 清除计时器,如果用户快速滚动过去,不记录浏览
if (this.viewTimer) {
clearTimeout(this.viewTimer)
this.viewTimer = null
}
},
/**
* 记录浏览行为
* @param {String} itemType - 项目类型,默认为'dynamic'
* @param {String} itemId - 项目ID,默认从this.item.id获取
*/
recordBrowseView(itemType = 'dynamic', itemId = null) {
const id = itemId || (this.item && this.item.id)
if (this.hasRecordedView || !id) return
this.hasRecordedView = true
// 清除计时器
if (this.viewTimer) {
clearTimeout(this.viewTimer)
this.viewTimer = null
}
// 使用配置文件中的参数创建API请求参数
const params = createBrowseRecordParams(id, itemType)
// 调用浏览记录API
this.$api('addBrowseRecord', params, res => {
if (res.code === 200) {
console.log('浏览记录已保存:', id)
}
})
},
/**
* 清理观察者和计时器
*/
cleanupObserver() {
if (this.observer) {
// #ifdef H5
if (typeof this.observer.disconnect === 'function') {
this.observer.disconnect()
}
// #endif
// #ifdef MP-WEIXIN || APP-PLUS
if (typeof this.observer.disconnect === 'function') {
this.observer.disconnect()
}
// #endif
this.observer = null
}
if (this.viewTimer) {
clearTimeout(this.viewTimer)
this.viewTimer = null
}
},
/**
* 重置浏览记录状态
*/
resetBrowseRecord() {
this.hasRecordedView = false
this.isInViewport = false
this.cleanupObserver()
}
}
}