|
|
- /**
- * 可见性监听混入
- * 提供元素可见性监听功能,支持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()
- }
- }
- }
|