实现效果:
加入帧动画前:
普通的静态页面
加入帧动画后:
可以看到,加入帧动画后,页面效果还是比较丝滑的。
技术实现
加入animation动画类
先用 **scss **定义三种动画类:
.withAnimation {
.fade1 {
animation: fadeInDown1 1s;
}
.fade2 {
animation: fadeInDown2 1.25s;
}
.fade3 {
animation: fadeInDown2 1.5s;
}
}
@keyframes fadeInDown1 {
0% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
22% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
@keyframes fadeInDown2 {
0% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
44% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
}
@keyframes fadeInDown3 {
0% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
66% {
transform: translate3d(0, 40px, 0);
opacity: 0;
}
100% {
-webkit-transform: none;
transform: none;
opacity: 1;
}
注意:.fade类前面需要有.withAnimation,作为接下来实现帧动画,添加.withAnimation .fade类帧动画的工具。
加入IntersectionObserver监听
IntersectionObserver
先简单过一下IntersectionObserver的使用:
var observer = new IntersectionObserver(callback,options);
IntersectionObserver支持两个参数:
- callback是当被监听元素的可见性变化时,触发的回调函数
- options是一个配置参数,可选,有默认的属性值
//初始化一个实例
var observer = new IntersectionObserver(changes => {
for (const change of changes) {
console.log(change.time);
// Timestamp when the change occurred
// 当可视状态变化时,状态发送改变的时间戳
// 对比时间为,实例化的时间,
// 比如,值为1000时,表示在IntersectionObserver实例化的1秒钟之后,触发该元素的可视性变化
console.log(change.rootBounds);
// Unclipped area of root
// 根元素的矩形区域信息,即为getBoundingClientRect方法返回的值
console.log(change.boundingClientRect);
// target.boundingClientRect()
// 目标元素的矩形区域的信息
console.log(change.intersectionRect);
// boundingClientRect, clipped by its containing block ancestors,
// and intersected with rootBounds
// 目标元素与视口(或根元素)的交叉区域的信息
console.log(change.intersectionRatio);
// Ratio of intersectionRect area to boundingClientRect area
// 目标元素的可见比例,即intersectionRect占boundingClientRect的比例,
// 完全可见时为1,完全不可见时小于等于0
console.log(change.target);
// the Element target
// 被观察的目标元素,是一个 DOM 节点对象
// 当前可视区域正在变化的元素
}
}, {});
// Watch for intersection events on a specific target Element.
// 对元素target添加监听,当target元素变化时,就会触发上述的回调
observer.observe(target);
// Stop watching for intersection events on a specific target Element.
// 移除一个监听,移除之后,target元素的可视区域变化,将不再触发前面的回调函数
observer.unobserve(target);
// Stop observing threshold events on all target elements.
// 停止所有的监听
observer.disconnect();
实现
页面组件中加入IntersectionObserver
useEffect(() => {
const options = {
rootMargin: '0px',
threshold: 0.2 // 指定交叉比例为 20% 时触发回调函数
};
const observer = new IntersectionObserver(([entry]) => {
// ...
}, options);
}, []);
监听dom元素的进入,添加对应class
fadeClass类的添加
- 确保父元素加入了fadeClass类,便于后续对加动画的dom元素进行锁定
- 子元素添加对应的fade动画类
<div className={cName([styles.rootFront,'fadeClass'])}>
<p className={cName([styles.text1, styles.fade1])}>123</p>
<p className={cName([styles.text2, styles.fade2])}>123</p>
<p className={cName([styles.text3, styles.fade3])}>123</p>
</div>
cName()是classnames中给dom元素添加多个class的方法,也可以用其他方法实现。
- 引入:“import cName from ‘classnames’”
- 依赖:npm i classnames
IntersectionObserver监听fadeClass元素的进入
useEffect(() => {
const elements = document.querySelectorAll('.fadeClass');
const options = {
rootMargin: '0px',
threshold: 0.2 // 指定交叉比例为 20% 时触发回调函数
};
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
entry.target.classList.add(styles.withAnimation);
}
// 进入时添加withAnimation类
}, options);
elements.forEach((dom) => {
observer.observe(dom);//每个元素,添加聆听事件
});
}, []);
不出意外,到这来就基本实现完成了,其他动画效果实现类似。