Vue3实现 元素进入可视范围内执行动画的效果

效果

HTML部分(为了效果整得有点多)

思路:

  • 为每个元素绑定 ref:class,
  • 初始化一个 visibleItems 数组,值为布尔值
  • 通过setBoxRef来获取dom元素,
  • 使用 IntersectionObserver 检测每个元素是否进入视口,并在进入视口时设置 visibleItems 的值为 true
  • 定义 visible 类和关键帧动画 @keyframes 来实现元素的进入动画。
<template>
  <div class="container">
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[0] }"
      style="opacity: 0"
    >
      <h4>字体</h4>
      <div class="lbt_content scontent">1</div>
    </div>
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[1] }"
      style="opacity: 0"
    >
      <h4>轮播图</h4>
      <div class="lbt_content scontent"></div>
    </div>
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[2] }"
      style="opacity: 0"
    >
      <h4>轮播图</h4>
      <div class="lbt_content scontent"></div>
    </div>
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[3] }"
      style="opacity: 0"
    >
      <h4>轮播图</h4>
      <div class="lbt_content scontent"></div>
    </div>
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[4] }"
      style="opacity: 0"
    >
      <h4>轮播图</h4>
      <div class="lbt_content scontent">123</div>
    </div>
    <div
      class="lbt content"
      :ref="setBoxRef"
      :class="{ visible: visibleItems[5] }"
      style="opacity: 0"
    >
      <h4>轮播图</h4>
      <div class="lbt_content scontent">1231</div>
    </div>
  </div>
</template>

TypeScript部分

<script setup lang="ts">
import { onMounted, ref, reactive } from "vue";
const visibleItems = reactive([]);
/* 保存观察dom元素 */
const boxrefList = reactive([]);
/* 获取要观察的dom元素 */
const setBoxRef = (el: any) => {
  if (el) {
    boxrefList.push(el);
  }
};
const observeElements = () => {
  const options: any = {
    root: null, // 视口为浏览器窗口
    rootMargin: "0px",
    threshold: 0, // 当元素 0% 可见时触发
  };

  const observer = new IntersectionObserver((entries) => {
    /*
     *遍历IntersectionObserverEntry对象
     *每个 IntersectionObserverEntry 对象包含有关目标元素与视口或其祖先元素的交叉状态的信息
     */
    entries.forEach((entry) => {
      /* 查找entry.target在boxrefList中的索引 */
      const index = Array.from(boxrefList).indexOf(entry.target);
      /* entry.isIntersecting 为一个布尔值,表示目标元素是否与视口或其祖先元素相交 */

      if (entry.isIntersecting) {
        /* 如果 entry.isIntersecting 为 true,则将 visibleItems 对应索引位置的值设为 true,表示该元素在视口中 */
        visibleItems[index] = true;
      } else {
        /* 如果 entry.isIntersecting 为 false,则将 visibleItems 对应索引位置的值设为 false,表示该元素不在视口中 */
        visibleItems[index] = false;
      }
    });
  }, options);
  /* 遍历需要观察的元素,然后通过observer 来观察box元素 */
  boxrefList.forEach((box) => {
    observer.observe(box);
  });
};
onMounted(() => {
  observeElements();
});
</script>

css部分

.container {
  .content {
    width: 100%;
    margin-bottom: 50px;
    background: white;
    box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.3);
    padding: 10px;
    box-sizing: border-box;
    border-radius: 20px;

    .scontent {
      height: 700px;
    }
  }
  .visible {
    opacity: 1;
    transform-origin: bottom center;
    animation: enter-animation 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }
  /* 动画 */
  @keyframes enter-animation {
    0% {
      transform: scale(0.95) translateY(40px);
      opacity: 0;
    }

    100% {
      transform: scale(1) translateY(0);
      opacity: 1;
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值