Intersection Observer API 提供了一种异步观察目标元素与祖先元素或顶级文档 viewport 的交集中的变化的方法。容许你配置一个回调函数,每当目标 (target) 元素与设备视窗或者其余指定元素发生交集的时候执行。css
vue-scroll-loader 是我在使用 element UI 库过程当中,因为该库自带的无限滚动组件会有莫名其妙的 Bug,提了 issue 也久久没见修复,因而自(chong)力(fu)更(zao)生(lun)撸(zi)了一个相似的无限滚动组件,足够简单、足够小。html
在使用观察者 API 以前,vue-scorll-loader 1.x 版本是使用远古技术经过监听滚动条实现的,稍有常识的人都知道这种方式会有性能损耗 :-D,现在 Intersection Observer API 兼容性愈来愈好,再加上官方的 polyfill 就能够生产环境使用了,使人惊喜的是亢余的旧代码通过改造以后变为了短短了几行。前端
这里我并无使用常规的方式建立一个 warpper 包裹要加载的 list ,而是直接使用一个 loading 动画放在 list 下面,loding 便是组件自己,这样组件和 list 解耦,和布局无关,list 要使用无限滚动直接将此组件放下面便可,而不需额外的修改 list 布局vue
HTMLgit
使用 slot 提供用户自定义动画的能力github
<template lang="html">
<div class="loader" v-if="!loaderDisable">
<slot>
<svg viewBox="25 25 50 50" class="loader__svg" :style="size">
<circle cx="50" cy="50" r="20" class="loader__circle" :style="color"></circle>
</svg>
</slot>
</div>
</template>
复制代码
SCSS数组
<style lang="scss" scoped>
.loader{
display: flex;
align-items: center;
justify-content: center;
padding: 30px 0;
&__svg {
transform-origin: center;
animation: rotate 2s linear infinite;
}
&__circle {
fill: none;
stroke-width: 3;
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dashoffset: -125px;
}
}
</style>
复制代码
DEMObash
之前咱们是监听滚动条实现像下面这样:异步
缺点:同步,且会重复触发,每每须要 debounce 限制频率,须要手动计算相对位置。svg
window.addEventListener('scroll', e => {
// 一堆计算代码...
})
复制代码
而如今使用 Intersection Observer API :
优势:异步,只有处于交叉点才会触发,无需计算元素之间的相对位置。
const observer = new IntersectionObserver(([{ isIntersecting }]) => {
// 要作什么事情...
}, {
root: null, // 相对的视口元素,传入 null 则为顶级文档视口
rootMargin: '0px 0px 0px 0px', // 触发交叉回调时被观察元素相对于视口的偏移量
threshold: 0 // 一个具体数值或数值数组, 触发交叉回调时被观察元素的可见比例
})
// 传入被观察元素
observer.observe(el)
复制代码
你们可能注意到了,上面我在回调里面接收了一个 isIntersecting 参数,这是干什么用的呢? 缘由是当被观察元素通过和视口的交叉点时,会触发两次回调,一次进入交叉点,一次离开交叉点,进入为 true,离开为 false,因此咱们须要这个参数来保证加载回调只触发一次。
下面是结合 Vue 的无限滚动实现。
JavaScript
这里有个技巧,咱们 new IntersectionObserver() 会建立一个 observer 观察器,由于当离开当前组件时须要注销观察器,因此这里利用计算属性建立这个观察器的同时保持常驻,至关于简化了在 mounted() 中 new IntersectionObserver() 并把实例存入 data 这一步操做。
<script>
import 'intersection-observer'
export default {
name: 'ScrollLoader',
props: {
'loader-method': {
type: Function,
required: true
},
'loader-disable': {
type: Boolean,
default: false
},
'loader-distance': {
type: Number,
default: 0
},
'loader-color': {
type: String,
default: '#666666'
},
'loader-size': {
type: Number,
default: 50
},
'loader-viewport': {
type: Object,
default: null
}
},
computed: {
size () {
return {
width: `${this.loaderSize}px`
}
},
color () {
return {
stroke: this.loaderColor
}
},
options () {
return {
root: this.loaderViewport,
rootMargin: `0px 0px ${this.loaderDistance}px 0px`
}
},
observer () {
return new IntersectionObserver(([{ isIntersecting }]) => {
isIntersecting && !this.loaderDisable && this.loaderMethod()
}, this.options)
}
},
mounted () {
this.observer.observe(this.$el)
},
activated () {
this.observer.observe(this.$el)
},
deactivated () {
this.observer.unobserve(this.$el)
},
beforeDestroy () {
this.observer.unobserve(this.$el)
}
}
</script>
复制代码
Demo
在线预览:DEMO
项目地址:vue-scroll-loader
第一次在掘金发布文章,算不上什么干货,也不是很高深的技术,但仍是但愿这篇文章能帮助到像我这样的菜鸡前端,文章若有遗漏或错误的地方,还但愿大佬指点,谢谢你们!