文章目录
- 1、效果图
- 2、基本骨架
- 3、实现
- 4、完整代码
 
1、效果图

2、基本骨架
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    .container {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 100%;
      padding-top: 300px;
    }
    .item {
      width: 70vw;
      height: 500px;
      margin-bottom: 20px;
      border-radius: 15px;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="item" style="background-color: #cee288;" data-index="1"></div>
    <div class="item" style="background-color: #775414;" data-index="2"></div>
    <div class="item" style="background-color: #681dd1;" data-index="3"></div>
    <div class="item" style="background-color: #38d1a6;" data-index="4"></div>
    <div class="item" style="background-color: #50e211;" data-index="5"></div>
    <div class="item" style="background-color: #53357b;" data-index="6"></div>
    <div class="item" style="background-color: #f196ee;" data-index="7"></div>
  </div>
</body>
</html>
3、实现
为了不污染原有的样式,就不用css属性了,直接用
JS创建动画
创建完动画,不能先让它执行动画,执行与不执行是我们决定
    let itemsEl = document.querySelectorAll('.item');
    itemsEl.forEach(f => {
      // 为了不污染原有的 transition 等其他属性,可以自己创建一个动画
      console.log(isInViewport(f), f);
      if (!isInViewport(f)) return
      let translateYAnimate = f.animate([
        { transform: 'translateY(80px)' },
        { transform: 'translateY(0)' }
      ], {
        duration: 1000, // 动画时常
      })
      translateYAnimate.pause() // 先暂停所有动画,需要通过其他的方法来判断 这个DOM 是否进入可视区
    })

这样达到的效果是,界面一加载,都做动画了,并不是预想的结果,需要判断这个
Item是否进入可视区,如果进入可视区,才能做动画
      let observe = new IntersectionObserver((entries) => {
        entries.forEach(f => {
          console.log(f);
          if (f.isIntersecting) {
            translateYAnimate.play()
            observe.unobserve(f.target) // 观察一次就行,只需要做一次动画
          } else {
          }
        })
      })
      observe.observe(f)
这样确实可以实现了,但是 浏览器会记住滚动条的位置,就会导致有问题,
- 刚进来的时候,看到的DOM 并不需要做动画
- 如果滚动到某一个位置后,刷新界面,再往上滚动,上面的DOM 也不需要做动画了
核心就是判断当前的DOM 距离视口顶部的距离 是否超过了 视口的高度, 如果是,才需要做动画,否则不需要
4、完整代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    .container {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 100%;
      padding-top: 300px;
    }
    .item {
      width: 70vw;
      height: 500px;
      margin-bottom: 20px;
      border-radius: 15px;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="item" style="background-color: #cee288;" data-index="1"></div>
    <div class="item" style="background-color: #775414;" data-index="2"></div>
    <div class="item" style="background-color: #681dd1;" data-index="3"></div>
    <div class="item" style="background-color: #38d1a6;" data-index="4"></div>
    <div class="item" style="background-color: #50e211;" data-index="5"></div>
    <div class="item" style="background-color: #53357b;" data-index="6"></div>
    <div class="item" style="background-color: #f196ee;" data-index="7"></div>
  </div>
  <script>
  // 判断这个元素,是否在可视区里面
    const isInViewport = (el) => {
      const rect = el.getBoundingClientRect();
      console.log(rect.top);
      return (
        rect.top >= 0 &&
        rect.top >= (window.innerHeight || document.documentElement.clientHeight)
      );
    };
    let itemsEl = document.querySelectorAll('.item');
    // let elAnimateMap = new Map();
    itemsEl.forEach(f => {
      // 为了不污染原有的 transition 等其他属性,可以自己创建一个动画
      if (!isInViewport(f)) return
      let translateYAnimate = f.animate([
        { transform: 'translateY(80px)' },
        { transform: 'translateY(0)' }
      ], {
        duration: 1000, // 动画时常
      })
      translateYAnimate.pause() // 先暂停所有动画,需要通过其他的方法来判断 这个DOM 是否进入可视区
      // elAnimateMap.set(f, translateYAnimate)
      let observe = new IntersectionObserver((entries) => {
        entries.forEach(f => {
          console.log(f);
          if (f.isIntersecting) {
            translateYAnimate.play()
            // elAnimateMap.get(f.target).play()
            observe.unobserve(f.target) // 观察一次就行
          } else {
            // translateYAnimate.pause()
          }
        })
      })
      observe.observe(f)
    })
  </script>
</body>
</html>



















