slideComp.vue 2.37 KB
<template>
  <div ref="animatedElement" :style="computedStyle">
    <slot></slot>
  </div>
</template>

<script>
// 页面滚动时,元素缓慢上升效果, 视觉差效果组件
export default {
  name: 'slideIn',
  props: {
    duration: { // 动画持续时间
      type: Number,
      default: 1000
    },
    easing: { // 动画缓动效果
      type: String,
      default: 'ease'
    },
    distance: { // 动画距离
      type: Number,
      default: 100
    }
  },
  data() {
    return {
      hasAnimated: false // 是否已经动画过
    }
  },
  computed: {
    computedStyle() {
      return {
        opacity: this.hasAnimated ? 1 : 0,
        transform: this.hasAnimated ? 'translateY(0)' : `translateY(${this.distance}px)`,
        transition: `transform ${this.duration}ms ${this.easing}, opacity ${this.duration}ms ${this.easing}`
      }
    }
  },
  mounted() {
    if (typeof window !== 'undefined' && 'IntersectionObserver' in window) { // 检测是否支持IntersectionObserver
      this.createObserver() // 创建IntersectionObserver
    } else {
      // 如果不支持IntersectionObserver,则使用scroll事件来实现动画
      this.observeScroll()
    }
  },
  methods: {
    createObserver() {
      const observer = new IntersectionObserver(entries => { // IntersectionObserver回调函数
        entries.forEach(entry => { // 遍历每个观察目标
          if (entry.isIntersecting && !this.hasAnimated) { // 如果目标进入视口并且没有动画过
            this.hasAnimated = true // 标记动画过
            observer.unobserve(entry.target) // 停止观察
          }
        })
      }, { threshold: 0.1 }) // 观察阈值,表示目标在视口的百分比
      observer.observe(this.$refs.animatedElement) // 观察目标
    },
    observeScroll() {
      const onScroll = () => { // scroll事件回调函数
        if (this.isInViewport(this.$refs.animatedElement) && !this.hasAnimated) { // 如果目标在视口并且没有动画过
          this.hasAnimated = true // 标记动画过
          window.removeEventListener('scroll', onScroll) // 停止监听scroll事件
        }
      }
      window.addEventListener('scroll', onScroll) // 监听scroll事件
    },
    isInViewport(el) { // 判断目标是否在视口
      const rect = el.getBoundingClientRect()
      return rect.top < window.innerHeight && rect.bottom > 0
    }
  }
}
</script>