文章目录
- 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>