使用IntersectionObserver和animatecss实现滚动动画
效果
IntersectionObserver API介绍
IntersectionObserver是一种浏览器提供的 JavaScript API,用于监测元素与视窗的交叉状态。它可以告诉开发者一个元素是否进入或离开视窗,以及两者的交叉区域的大小和位置。 它提供了一种高效的方法来观察元素是否进入或离开视窗,而无需依赖滚动事件或定时器。它可以通过回调函数及设定的阈值来实时地通知开发者目标元素与视窗的交叉状态,并根据需要采取相应的操作。
基本使用方式
javascript
const io = new IntersectionObserver(callback, option);
上面代码中,IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:
callback
:可见性发现变化时的回调函数option
:配置对象(可选)。
构造函数的返回值是一个观察器实例。实例一共有4个方法:
observe
:开始监听特定元素unobserve
:停止监听特定元素disconnect
:关闭监听工作takeRecords
:返回所有观察目标的对象数组
observe
该方法需要接收一个target参数,值是Element类型,用来指定被监听的目标元素
observe
方法的参数是一个 DOM 节点,如果需要观察多个节点,就要多次调用这个方法:
javascript
// 获取元素
const target = document.getElementById("dom");
// 开始观察
io.observe(target);
unobserve
该方法需要接收一个target参数,值是Element类型,用来指定停止监听的目标元素
javascript
// 获取元素
const target = document.getElementById("dom");
// 停止观察
io.unobserve(target);
disconnect
该方法不需要接收参数,用来关闭观察器
javascript
// 关闭观察器
io.disconnect();
takeRecords
该方法不需要接收参数,返回所有被观察的对象,返回值是一个数组
javascript
// 获取被观察元素
const observerList = io.takeRecords();
callback 参数
目标元素的可见性变化时,就会调用观察器的回调函数callback
。
callback
一般会触发两次。一次是目标元素刚刚进入视口,另一次是完全离开视口。
javascript
const io = new IntersectionObserver((entries, observer) => {
console.log(entries);
console.log(observer);
});
callback
函数的参数接收两个参数changes
和observer
:
entries
:这是一个数组,每个成员都是一个被观察对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,那么entries
数组里面就会打印出两个元素,如果只观察一个元素,我们打印changes[0]
就能获取到被观察对象observer
: 这是一个对象,返回我们在实例中传入的第二个参数option(如果没传,则返回默认值)
函数封装
javascript
// observer.js
export default function (selector, callback, threshold = 0.1) {
let list = document.querySelectorAll(selector);
let observer = new IntersectionObserver(
(entries) => {
entries.forEach((element, index) => {
if (element.isIntersecting) {
callback(element.target, index);
observer.unobserve(element.target); // 移除监听
}
});
},
{
threshold,
}
);
list.forEach((item) => observer.observe(item));
}
调用实现动画
TIP
动画效果是通过animate.css实现的,必须安装animate.css
javascript
// animate.js
import { onMounted } from "vue";
import observer from "@/utils/observer";
export default function () {
onMounted(() => {
// 组件标题
observer(".home_item_title", (ele) => {
ele.classList.add("animate__fadeInDown");
});
observer(".home_item_desc", (ele) => {
ele.classList.add("animate__fadeInUp");
});
// masking组件
observer(".mask_item", (ele, index) => {
<!-- animate__fadeInDown 是 animatecss动画的类名 -->
ele.classList.add("animate__fadeInDown");
ele.style.animationDelay = index * 300 + "ms";
});
// billing组件
observer(".home_bill_item", (ele, index) => {
let className = ele.classList[ele.classList.length - 1];
switch (className) {
case "home_bill_content":
ele.style.animationDelay = index * 400 + "ms";
<!-- animate__fadeInDown 是 animatecss动画的类名 -->
ele.classList.add("animate__fadeInDown");
break;
case "home_bill_img":
<!-- animate__bounceIn 是 animatecss动画的类名 -->
ele.classList.add("animate__bounceIn");
ele.style.animationDelay = index * 500 + "ms";
break;
case "home_bill_desc":
<!-- animate__flipInX 是 animatecss动画的类名 -->
ele.classList.add("animate__flipInX");
ele.style.animationDelay = 1800 + "ms";
break;
}
});
});
}
vue
<!-- home页面组件 -->
<script setup>
import customAimate from "./js/animate"
import Billing from "./components/billing.vue"
customAimate()
</script>
<template>
<div>
<Billing></Billing>
</div>
</template>
vue
<!-- billing组件 -->
<script setup>
import { homeImages } from "@/common/images"
</script>
<template>
<div class="py-8 duration-500 md:py-16 box">
<!-- animate__animated 是 animatecss动画的类名必须添加 -->
<div class="font-bold text-center md:text-xl home_item_title animate__animated">
智能开单
</div>
<div class="mt-2 text-xs text-center home_item_desc animate__animated">自动化计费,标准化管理,杜绝收银漏洞</div>
<div class="flex flex-col items-center justify-center w-full">
<div class="flex flex-wrap items-center justify-center w-full px-8 mt-8 max_box md:px-32">
<div class="w-1/3 md:w-1/5 animate__animated home_bill_item home_bill_content">
<img :src="homeImages.billing4" alt="" class="w-full">
<div class="mt-2 text-sm text-center text-primary">蚁企云Sass收银系统</div>
</div>
<img :src="homeImages.arrow" alt="" class="w-1/6 animate__animated home_bill_item home_bill_img"
style="max-width: 3.125rem; height: 100%;">
<div class="w-1/3 md:w-1/5 animate__animated home_bill_item home_bill_content">
<img :src="homeImages.billing3" alt="" class="w-full">
<div class="mt-2 text-sm text-center text-primary">网关</div>
</div>
<img :src="homeImages.arrow" alt="" class="w-1/6 animate__animated home_bill_item home_bill_img"
style="max-width: 3.125rem; height: 100%;">
<div class="w-1/3 md:w-1/5 animate__animated home_bill_item home_bill_content">
<img :src="homeImages.billing2" alt="" class="w-full">
<div class="mt-2 text-sm text-center text-primary">灯</div>
</div>
<img :src="homeImages.arrow" alt="" class="w-1/6 animate__animated home_bill_item home_bill_img"
style="max-width: 3.125rem; height: 100%;">
<div class="w-1/3 md:w-1/5 animate__animated home_bill_item home_bill_content">
<img :src="homeImages.billing1" alt="" class="w-full">
<div class="mt-2 text-sm text-center text-primary">设备</div>
</div>
</div>
<div class="justify-center md:w-full max_box md:px-32 animate__animated home_bill_item home_bill_desc">
<div class="flex flex-wrap items-center px-8 md:justify-around md:px-0">
<div class="flex items-start mt-8 ">
<div style="width: 20px;height: 20px;background: #4A66E940;margin-top: .125rem;"
class="flex items-center justify-center rounded-full">
<div class="w-1/2 rounded-full h-1/2 bg-primary"></div>
</div>
<div class="ml-2 text-sm">
<div class="mb-2">开单-设备通电,自动计费;</div>
<div>结账-设备断电,停止计费。</div>
</div>
</div>
<div class="flex items-start mt-8">
<div style="width: 20px;height: 20px;background: #4A66E940;margin-top: .125rem;"
class="flex items-center justify-center rounded-full">
<div class="w-1/2 rounded-full h-1/2 bg-primary"></div>
</div>
<div class="ml-2 text-sm">
<div class="mb-2">智能开关 <span class="text-primary">无需走线、布线,安装简单;</span></div>
<div>Zigbee本地传输,自带中继功能,信号稳定。</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>