大家下午好。听说在实现一些需要监听滚动条滚动到某个元素需要执行某段神秘代码的需求时,大伙总是不太乐意,现给大家介绍一个不用监听滚动事件就能完成需求的api,希望能给一点点启发。
废话少说,先上效果
- 懒加载
- 瀑布流
- 视频暂停
上代码
HTML
html
/* html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Intersection Observer API</title>
<style>
* {
padding: 0;
margin: 0;
}
.wrapper {
display: flex;
flex-wrap: wrap;
}
img {
width: 400px;
margin: auto;
}
</style>
</head>
<body>
<div class="wrapper">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1223089837,2011531023&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=727460147,2222092211&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2763645735,2016465681&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=38705621,4040292245&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3691080281,11347921&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=309902808,4013662088&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3915743384,2060495762&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=814015239,3764270914&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=735474575,14049849&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=55748064,2074988836&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1415984692,3889465312&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1635608122,693552335&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=4229354459,3466205664&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=801209673,1770377204&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1010739515,2488150950&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=755629088,1584786319&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2359570649,2574326109&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3326728979,3352004316&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1972611367,1123866192&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1418407542,1794380926&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1879685954,2134278564&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2466425392,342874463&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3355440349,3059059541&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=1335405753,180526972&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3522949495,3570538969&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=434014116,2108959724&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3439093793,987421329&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=238793329,1979556309&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=3355871248,953827123&fm=193&f=GIF">
<img src="./imgs/0.png" data-src="https://t7.baidu.com/it/u=2757385166,2243114851&fm=193&f=GIF">
</div>
<script src="./index.js"></script>
</body>
</html>
JS
js
// index.js
const ob = new IntersectionObserver(entries => {
entries.forEach(entrie => {
if(entrie.isIntersecting) {
const img = entrie.target;
img.src = img.dataset.src;
ob.unobserve(img)
}
})
}, {
threshold: 0
})
const imgs = document.querySelectorAll('img[data-src]');
imgs.forEach(img => {
ob.observe(img)
})
完事,下次再见
啊?客官还要解释?那好吧
接下来就是隆重推出今天的主角 IntersectionObserver API,这是一套用来监听 html 元素是否与指定元素有 交叉关系 的API。 就拿以下代码解释
js
const ob = new IntersectionObserver(
(entries) => {
console.log(entries);
},
{
root: null,
rootMargin: 0,
threshold: 0,
}
);
ob.observe(dom);
-
IntersectionObserver 第一个参数是回调函数,回调参数是一组被观察元素的具体交叉情况
-
第二个参数是配置项
- root: 表示被观察元素与哪个元素是否产生交叉,只能填被观察元素的父元素(祖先元素),默认 null 表示视口元素
- rootMargin: 表示被观察元素与 root 之间的距离,如值为 10 ,则被观察元素与 root 相距小于等于 10 就会视为已交叉;默认为0;如下图
- threshold: 表示交叉的阈值,值为 0-1 之间,默认为0,表示交叉了多少才视为交叉,如下图
- ob.observe(dom) 表示要观察哪个元素;
- 下图为回调函数中的参数;
主要关注 isInteresting 与 target 这两个属性
- isInteresting 表示是否交叉
- target 表示目前交叉的元素
回归主题
那么我们回到上面的懒加载的实现,只要监听每张图片是否与视口交叉即可。请看
js
const ob = new IntersectionObserver(entries => {
entries.forEach(entrie => {
if(entrie.isIntersecting) {
const img = entrie.target; // 获取当前交叉的图片
img.src = img.dataset.src; // 将 data-src 的值,赋值给 src
ob.unobserve(img) // 已加载的图片就没有必要再监听了, 移除监听
}
})
}, {
threshold: 0 // 只要一出现在视口就判定已交叉
})
const imgs = document.querySelectorAll('img[data-src]'); // 获取所有带着data-src属性的图片元素
imgs.forEach(img => {
ob.observe(img) // 遍历监听每一张图片
})
实现瀑布流,只需监听加载中的元素是否交叉即可
HTML
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Intersection Observer API</title>
<style>
* {
padding: 0;
margin: 0;
}
.wrapper {
display: flex;
flex-wrap: wrap;
}
img {
width: 350px;
margin: auto;
}
.mycircle {
stroke-dasharray: 125.6px;
animation: move linear 1s infinite;
transform-origin: center;
stroke-dashoffset: 30px;
fill: none;
stroke: #666;
stroke-width: 5px;
}
@keyframes move {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<div class="wrapper">
<img src="https://t7.baidu.com/it/u=1956604245,3662848045&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=1223089837,2011531023&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=2529476510,3041785782&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=727460147,2222092211&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=3779234486,1094031034&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=2763645735,2016465681&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=38705621,4040292245&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=3691080281,11347921&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=309902808,4013662088&fm=193&f=GIF">
<img src="https://t7.baidu.com/it/u=3915743384,2060495762&fm=193&f=GIF">
</div>
<div class="spin" style="text-align: center">
<svg width="150" height="150" viewBox="0 0 50 50">
<circle class="mycircle" cx="25" cy="25" r="20">
<animateTransform
attributeName="transform"
type="rotate"
from="0"
to="360"
dur="1s"
repeatCount="indefinite"
/>
</circle>
</svg>
</div>
<script src="./index2.js"></script>
</body>
</html>
JS
js
const img = [
"https://t7.baidu.com/it/u=814015239,3764270914&fm=193&f=GIF",
"https://t7.baidu.com/it/u=735474575,14049849&fm=193&f=GIF",
"https://t7.baidu.com/it/u=55748064,2074988836&fm=193&f=GIF",
"https://t7.baidu.com/it/u=1415984692,3889465312&fm=193&f=GIF",
"https://t7.baidu.com/it/u=1635608122,693552335&fm=193&f=GIF",
"https://t7.baidu.com/it/u=4229354459,3466205664&fm=193&f=GIF",
"https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF",
"https://t7.baidu.com/it/u=334080491,3307726294&fm=193&f=GIF",
"https://t7.baidu.com/it/u=801209673,1770377204&fm=193&f=GIF",
"https://t7.baidu.com/it/u=1010739515,2488150950&fm=193&f=GIF",
"https://t7.baidu.com/it/u=755629088,1584786319&fm=193&f=GIF",
"https://t7.baidu.com/it/u=2359570649,2574326109&fm=193&f=GIF",
"https://t7.baidu.com/it/u=3326728979,3352004316&fm=193&f=GIF",
]
function loadImage(imgs) {
imgs.forEach(item => {
const img = document.createElement('img')
img.src = item
document.querySelector('.wrapper').appendChild(img)
})
}
const ob = new IntersectionObserver(entries => {
if(entries[0].isIntersecting) {
setTimeout(() => {
loadImage(img)
}, 1000);
}
}, {
threshold: 0
})
const spin = document.querySelector('.spin');
ob.observe(spin) // ### 监听加载中的元素是否交叉
实现视频暂停
HTML
html
<!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;
}
.wrapper {
width: 100vw;
height: 100vh;
text-align: center;
}
.video {
margin-top: 100px;
margin-bottom: 1000px;
}
</style>
</head>
<body>
<div class="wrapper">
<video class="video" controls autoplay="true" src="哪吒之魔童降世.Nezha.Birth.of.the.Demon.Child.2019.HD1080P.国语中字.mp4" width="80%"></video>
</div>
<script src="./index3.js"></script>
</body>
</html>
JS
js
const ob = new IntersectionObserver(entries => {
const video = entries[0].target
if(entries[0].isIntersecting) {
video.play()
} else {
video.pause()
}
}, {
threshold: 1 // 只要视频有一点不见了就暂停
})
const video = document.querySelector('.video');
ob.observe(video)