v-slide-in滑动入视口

51 阅读1分钟
const DISTANCE = 150
const DURATION = 1000

// 判断元素是否在视口之下
function isBelowViewport(el) {
  const rect = el.getBoundingClientRect()
  return rect.top > window.innerHeight
}
const animationMap = new WeakMap()
// 判断一个元素与视口是否重叠
const ob = new IntersectionObserver((entries) => {
  console.log(entries)
  for (const entry of entries) {
    if (entry.isIntersecting) {
      //所有在视口中的元素都可以执行动画化
      const animation = animationMap.get(entry.target)
      animation.play()
      // 动画仅执行一次,执行过后移除视口监听
      ob.unobserve(entry.target)
    }
  }
})

export default {
  mounted(el) {
    // 只关心是否之下的元素,给这些元素添加动画,其他元素不关心
    if (!isBelowViewport(el)) {
      return
    }
    const animation = el.animate(
      [
        {
          transform: `translateY(${DISTANCE}px)`,
          opacity: 0.5
        },
        {
          transform: 'translateY(0)',
          opacity: 1
        }
      ],
      {
        duration: DURATION,
        easing: 'ease'
      }
    )
    ob.observe(el)
    animation.pause() // 动画暂停
  },
  // 卸载时需清除观察器
  unmounted(el) {
    ob.unobserve(el)
  }
}