1. 首页
  2. IT资讯

90 行代码,15 个元素教你如何实现无限滚动

“u003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRbiRsmjFrZY4rp” img_width=”640″ img_height=”100″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRb5t7egG1mUqFs” img_width=”1080″ img_height=”763″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003E作者 | 前端劝退师u003Cu002Fpu003Eu003Cpu003E责编 | 胡巍巍u003Cu002Fpu003Eu003Cpu003E在本篇文章你将会学到:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003EIntersectionObserver API的用法,以及如何兼容。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E如何在React Hook中实现无限滚动。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E如何正确渲染多达10000个元素的列表。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E无限下拉加载技术使用户在大量成块的内容面前一直滚动查看。这种方法是在你向下滚动的时候不断加载新内容。u003Cu002Fpu003Eu003Cpu003E当你使用滚动作为发现数据的主要方法时,它可能使你的用户在网页上停留更长时间并提升用户参与度。u003Cu002Fpu003Eu003Cpu003E随着社交媒体的流行,大量的数据被用户消费。无线滚动提供了一个高效的方法让用户浏览海量信息,而不必等待页面的预加载。u003Cu002Fpu003Eu003Cpu003E如何构建一个体验良好的无限滚动,是每个前端无论是项目或面试都会碰到的一个课题。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRT4Gwk56bt5iOJ” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E早期的解决方案u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003E关于无限滚动,早期的解决方案基本都是依赖监听scroll事件:u003Cu002Fpu003Eu003Cpreu003Efunction fetchData {u003Cbru003Efetch(path).then(res => doSomeThing(res.data));u003Cbru003E}u003Cbru003Eu003Cbru003Ewindow.addEventListener(‘scroll’, fetchData);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E然后计算各种.scrollTop、.offset.top等等。u003Cu002Fpu003Eu003Cpu003E手写一个也是非常枯燥。而且:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Escroll事件会频繁触发,因此我们还需要手动节流。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E滚动元素内有大量DOM,容易造成卡顿。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRT7S2kzFTfre26″ img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E交叉观察者:u003Cu002Fstrongu003Eu003Cstrongu003EIntersectionObserveru003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpreu003Eu003Cpreu003Econst box = document.querySelector(‘.box’);u003Cbru003Econst intersectionObserver = new IntersectionObserver((entries) => {u003Cbru003Eentries.forEach((item) => {u003Cbru003Eif (item.isIntersecting) {u003Cbru003Econsole.log(‘进入可视区域’);u003Cbru003E}u003Cbru003E})u003Cbru003E});u003Cbru003EintersectionObserver.observe(box);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E敲重点:IntersectionObserver API是异步的,不随着目标元素的滚动同步触发,性能消耗极低。u003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E2.1 IntersectionObserverEntry对象u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E这里我就粗略的介绍下需要用到的:IntersectionObserverEntry对象。u003Cu002Fpu003Eu003Cpu003Ecallback函数被调用时,会传给它一个数组,这个数组里的每个对象就是当前进入可视区域或者离开可视区域的对象(IntersectionObserverEntry对象)u003Cu002Fpu003Eu003Cpu003E这个对象有很多属性,其中最常用的属性是:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Etarget: 被观察的目标元素,是一个 DOM 节点对象u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EisIntersecting: 是否进入可视区域u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EintersectionRatio: 相交区域和目标元素的比例值,进入可视区域,值大于0,否则等于0u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E2.3 optionsu003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E调用IntersectionObserver时,除了传一个回调函数,还可以传入一个option对象,配置如下属性:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Ethreshold: 决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eroot: 用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003ErootMargin: 用来扩大或者缩小视窗的的大小,使用css的定义方法,10px 10px u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E30u003Cu002Fiu003Epx 20px表示top、right、bottom 和 left的值u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpreu003Eu003Cpreu003Econst io = new IntersectionObserver((entries) => {u003Cbru003Econsole.log(entries);u003Cbru003E}, {u003Cbru003Ethreshold: [0, 0.5],u003Cbru003Eroot: document.querySelector(‘.container’),u003Cbru003ErootMargin: “10px 10px u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E30u003Cu002Fiu003Epx 20px”,u003Cbru003E});u003Cbru003Eu003Cu002Fpreu003Eu003Cstrongu003E2.4 observeru003Cu002Fstrongu003Eu003Cu002Fpreu003Eu003Cpreu003Eu003Cpreu003Eobserver.observer(nodeone); u002Fu002F仅观察nodeOne u003Cbru003Eobserver.observer(nodeTwo); u002Fu002F观察nodeOne和nodeTwou003Cbru003Eobserver.unobserve(nodeOne); u002Fu002F停止观察nodeOneu003Cbru003Eobserver.disconnect; u002Fu002F没有观察任何节点u003Cbru003Eu003Cu002Fpreu003Eu003Cbru003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRTJXJ1kBqzfCnu” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E如何在React Hook中使用IntersectionObserveru003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003E在看Hooks版之前,来看正常组件版的:u003Cu002Fpu003Eu003Cpreu003Eclass SlidingWindowScroll extends Reactu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E.Comu003Cu002Fiu003Eponent {u003Cbru003Ethis.$bottomElement = React.createRef;u003Cbru003E…u003Cbru003EcomponentDidMount {u003Cbru003Ethis.intiateScrollObserver;u003Cbru003E}u003Cbru003EintiateScrollObserver = => {u003Cbru003Econst options = {u003Cbru003Eroot: ,u003Cbru003ErootMargin: ‘0px’,u003Cbru003Ethreshold: 0.1u003Cbru003E};u003Cbru003Ethis.observer = new IntersectionObserver(this.callback, options);u003Cbru003Ethis.observer.observe(this.$bottomElement.current);u003Cbru003E}u003Cbru003Erender {u003Cbru003Ereturn (u003Cbru003E<u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E className=’img’ ref={this.$bottomElement}>u003Cbru003E)u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-2″u003E众所周知u003Cu002Fiu003E,React 16.x后推出了useRef来替代原有的createRef,用于追踪DOM节点。那让我们开始吧:u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRTJXJ7YR5xGDl” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E原理u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003E实现一个组件,可以显示具有15个元素的固定窗口大小的n个项目的列表:u003Cu002Fpu003Eu003Cpu003E即在任何时候,无限滚动n元素上也仅存在15个DOM节点。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRcgsnb43wBLJ0S” img_width=”600″ img_height=”288″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Culu003Eu003Cliu003Eu003Cpu003E采用relativeu002Fabsolute 定位来确定滚动位置u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E追踪两个ref: topu002Fbottom来决定向上u002F向下滚动的渲染与否u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E切割数据列表,保留最多15个DOM元素。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRTJXJ7uHXU5GZc” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003EuseState 声明状态变量u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003E我们开始编写组件SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook:u003Cu002Fpu003Eu003Cpreu003Econst THRESHOLD = 15;u003Cbru003Econst SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook = (props) => {u003Cbru003Econst [start, setStart] = useState(0);u003Cbru003Econst [end, setEnd] = useState(THRESHOLD);u003Cbru003Econst [observer, setObserver] = useState;u003Cbru003Eu002Fu002F 其它代码…u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E1. useState 的简单理解:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpreu003Econst [属性, 操作属性的方法] = useState(默认值);u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h3″u003Eu003Cstrongu003E2. 变量解析u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Culu003Eu003Cliu003Eu003Cpu003Estart:当前渲染的列表第一个数据,默认为0u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eend: 当前渲染的列表最后一个数据,默认为15u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eobserver: 当前观察的视图ref元素u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRTLSNam5ZxLDlM” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003EuseRef 定义追踪的DOM 元素u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpreu003Eu003Cpreu003Econst $bottomElement = useRef;u003Cbru003Econst $topElement = useRef;u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E正常的无限向下滚动只需u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E关注u003Cu002Fiu003E一个dom元素,但由于我们是固定15个dom元素渲染,u003Cu002Fpu003E需要判断向上或向下滚动。u003Cbru003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRTRIuPNI7JPB03″ img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E内部操作方法和和对应useEffectu003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003E请配合注释食用:u003Cu002Fpu003Eu003Cpreu003Eu003Cpreu003EuseEffect( => {u003Cbru003Eu002Fu002F 定义观察u003Cbru003EintiateScrollObserver;u003Cbru003Ereturn => {u003Cbru003Eu002Fu002F 放弃观察u003Cbru003EresetObservationu003Cbru003E}u003Cbru003E},[end]) u002Fu002F因为[end] 是同步刷新,这里用一个就行了。u003Cbru003Eu003Cbru003Eu002Fu002F 定义观察u003Cbru003Econst intiateScrollObserver = => {u003Cbru003Econst options = {u003Cbru003Eroot: ,u003Cbru003ErootMargin: ‘0px’,u003Cbru003Ethreshold: 0.1u003Cbru003E};u003Cbru003Econst Observer = new IntersectionObserver(callback, options)u003Cbru003Eu002Fu002F 分别观察开头和结尾的元素u003Cbru003Eif ($topElement.current) {u003Cbru003EObserver.observe($topElement.current);u003Cbru003E}u003Cbru003Eif ($bottomElement.current) {u003Cbru003EObserver.observe($bottomElement.current);u003Cbru003E}u003Cbru003Eu002Fu002F 设初始值u003Cbru003EsetObserver(Observer)u003Cbru003E}u003Cbru003Eu003Cbru003Eu002Fu002F 交叉观察的具体回调,观察每个节点,并对实时头尾元素索引处理u003Cbru003Econst callback = (entries, observer) => {u003Cbru003Eentries.forEach((entry, index) => {u003Cbru003Econst listLength = props.list.length;u003Cbru003Eu002Fu002F 向下滚动,刷新数据u003Cbru003Eif (entry.isIntersecting && entry.target.id === “bottom”) {u003Cbru003Econst maxStartIndex = listLength – 1 – THRESHOLD; u002Fu002F 当前头部的索引u003Cbru003Econst maxEndIndex = listLength – 1; u002Fu002F 当前尾部的索引u003Cbru003Econst newEnd = (end + 10) <= maxEndIndex ? end + 10 : maxEndIndex; u002Fu002F 下一轮增加尾部u003Cbru003Econst newStart = (end – 5) <= maxStartIndex ? end – 5 : maxStartIndex; u002Fu002F 在上一轮的基础上计算头部u003Cbru003EsetStart(newStart)u003Cbru003EsetEnd(newEnd)u003Cbru003E}u003Cbru003Eu002Fu002F 向上滚动,刷新数据u003Cbru003Eif (entry.isIntersecting && entry.target.id === “top”) {u003Cbru003Econst newEnd = end === THRESHOLD ? THRESHOLD : (end – 10 > THRESHOLD ? end – 10 : THRESHOLD); u002Fu002F 向上滚动尾部元素索引不得小于15u003Cbru003Elet newStart = start === 0 ? 0 : (start – 10 > 0 ? start – 10 : 0); u002Fu002F 头部元素索引最小值为0u003Cbru003EsetStart(newStart)u003Cbru003EsetEnd(newEnd)u003Cbru003E}u003Cbru003E});u003Cbru003E}u003Cbru003Eu003Cbru003Eu002Fu002F 停止滚动时放弃观察u003Cbru003Econst resetObservation = => {u003Cbru003Eobserver && observer.unobserve($bottomElement.current);u003Cbru003Eobserver && observer.unobserve($topElement.current);u003Cbru003E}u003Cbru003Eu003Cbru003Eu002Fu002F 渲染时,头尾ref处理u003Cbru003Econst getReference = (index, isLastIndex) => {u003Cbru003Eif (index === 0)u003Cbru003Ereturn $topElement;u003Cbru003Eif (isLastIndex)u003Cbru003Ereturn $bottomElement;u003Cbru003Ereturn ;u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cbru003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRTRIuPaahjHjS” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E渲染界面u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpreu003Eu003Cpreu003E const {list, height} = props; u002Fu002F 数据,节点高度u003Cbru003Econst updatedList = list.slice(start, end); u002Fu002F 数据切割u003Cbru003Eu003Cbru003Econst lastIndex = updatedList.length – 1;u003Cbru003Ereturn (u003Cbru003E<ul style={{position: ‘relative’}}>u003Cbru003E{updatedList.map((item, index) => {u003Cbru003Econst top = (height * (index + start)) + ‘px’; u002Fu002F 基于相对 & 绝对定位 计算u003Cbru003Econst refVal = getReference(index, index === lastIndex); u002Fu002F map循环中赋予头尾refu003Cbru003Econst id = index === 0 ? ‘top’ : (index === lastIndex ? ‘bottom’ : ”); u002Fu002F 绑IDu003Cbru003Ereturn (<u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E className=”u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E-card” key={item.key} style={{top}} ref={refVal} id={id}>{item.value}<u002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E>);u003Cbru003E})}u003Cbru003E<u002Ful>u003Cbru003E);u003Cbru003Eu003Cu002Fpreu003Eu003Cbru003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRTYlVXmICmMrFE” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E如何使用u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EAppu003Cu002Fiu003E.js:u003Cu002Fpu003Eu003Cpreu003Eu003Cpreu003Eimport React from ‘react’;u003Cbru003Eimport ‘.u002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EAppu003Cu002Fiu003E.css’;u003Cbru003Eimport { SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook } from “.u002FSlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook”;u003Cbru003Eimport MY_ENDLESS_LIST from ‘.u002FConstants’;u003Cbru003Efunction u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EAppu003Cu002Fiu003E {u003Cbru003Ereturn (u003Cbru003E<div className=”u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EAppu003Cu002Fiu003E”>u003Cbru003E<h1>15个元素实现无限滚动<u002Fh1>u003Cbru003E<SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook list={MY_ENDLESS_LIST} height={195}u002F>u003Cbru003E<u002Fdiv>u003Cbru003E);u003Cbru003E}u003Cbru003Eu003Cbru003Eexport default u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EAppu003Cu002Fiu003E;u003Cbru003Eu003Cu002Fpreu003E定义一下数据 Constants.js:u003Cbru003Eu003Cu002Fpreu003Eu003Cpreu003Eu003Cpreu003Econst MY_ENDLESS_LIST = [u003Cbru003E{u003Cbru003Ekey: 1,u003Cbru003Evalue: ‘A’u003Cbru003E},u003Cbru003E{u003Cbru003Ekey: 2,u003Cbru003Evalue: ‘B’u003Cbru003E},u003Cbru003E{u003Cbru003Ekey: 3,u003Cbru003Evalue: ‘C’u003Cbru003E},u003Cbru003Eu002Fu002F 中间就不贴了…u003Cbru003E{u003Cbru003Ekey: 45,u003Cbru003Evalue: ‘AS’u003Cbru003E}u003Cbru003E]u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003ESlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook.js:u003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cpreu003Eu003Cpreu003Eimport React, { useState, useEffect, useRef } from “react”;u003Cbru003Econst THRESHOLD = 15;u003Cbru003Eu003Cbru003Econst SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook = (props) => {u003Cbru003Econst [start, setStart] = useState(0);u003Cbru003Econst [end, setEnd] = useState(THRESHOLD);u003Cbru003Econst [observer, setObserver] = useState;u003Cbru003Econst $bottomElement = useRef;u003Cbru003Econst $topElement = useRef;u003Cbru003Eu003Cbru003EuseEffect( => {u003Cbru003EintiateScrollObserver;u003Cbru003Ereturn => {u003Cbru003EresetObservationu003Cbru003E}u003Cbru003Eu002Fu002F eslint-disable-next-line react-hooksu002Fexhaustive-depsu003Cbru003E},[start, end])u003Cbru003Eu003Cbru003Econst intiateScrollObserver = => {u003Cbru003Econst options = {u003Cbru003Eroot: ,u003Cbru003ErootMargin: ‘0px’,u003Cbru003Ethreshold: 0.1u003Cbru003E};u003Cbru003Econst Observer = new IntersectionObserver(callback, options)u003Cbru003Eif ($topElement.current) {u003Cbru003EObserver.observe($topElement.current);u003Cbru003E}u003Cbru003Eif ($bottomElement.current) {u003Cbru003EObserver.observe($bottomElement.current);u003Cbru003E}u003Cbru003EsetObserver(Observer)u003Cbru003E}u003Cbru003Eu003Cbru003Econst callback = (entries, observer) => {u003Cbru003Eentries.forEach((entry, index) => {u003Cbru003Econst listLength = props.list.length;u003Cbru003Eu002Fu002F Scroll Downu003Cbru003Eif (entry.isIntersecting && entry.target.id === “bottom”) {u003Cbru003Econst maxStartIndex = listLength – 1 – THRESHOLD; u002Fu002F Maximum index value `start` can takeu003Cbru003Econst maxEndIndex = listLength – 1; u002Fu002F Maximum index value `end` can takeu003Cbru003Econst newEnd = (end + 10) <= maxEndIndex ? end + 10 : maxEndIndex;u003Cbru003Econst newStart = (end – 5) <= maxStartIndex ? end – 5 : maxStartIndex;u003Cbru003EsetStart(newStart)u003Cbru003EsetEnd(newEnd)u003Cbru003E}u003Cbru003Eu002Fu002F Scroll upu003Cbru003Eif (entry.isIntersecting && entry.target.id === “top”) {u003Cbru003Econst newEnd = end === THRESHOLD ? THRESHOLD : (end – 10 > THRESHOLD ? end – 10 : THRESHOLD);u003Cbru003Elet newStart = start === 0 ? 0 : (start – 10 > 0 ? start – 10 : 0);u003Cbru003EsetStart(newStart)u003Cbru003EsetEnd(newEnd)u003Cbru003E}u003Cbru003Eu003Cbru003E});u003Cbru003E}u003Cbru003Econst resetObservation = => {u003Cbru003Eobserver && observer.unobserve($bottomElement.current);u003Cbru003Eobserver && observer.unobserve($topElement.current);u003Cbru003E}u003Cbru003Eu003Cbru003Eu003Cbru003Econst getReference = (index, isLastIndex) => {u003Cbru003Eif (index === 0)u003Cbru003Ereturn $topElement;u003Cbru003Eif (isLastIndex)u003Cbru003Ereturn $bottomElement;u003Cbru003Ereturn ;u003Cbru003E}u003Cbru003Eu003Cbru003Econst {list, height} = props;u003Cbru003Econst updatedList = list.slice(start, end);u003Cbru003Econst lastIndex = updatedList.length – 1;u003Cbru003Eu003Cbru003Ereturn (u003Cbru003E<ul style={{position: ‘relative’}}>u003Cbru003E{updatedList.map((item, index) => {u003Cbru003Econst top = (height * (index + start)) + ‘px’;u003Cbru003Econst refVal = getReference(index, index === lastIndex);u003Cbru003Econst id = index === 0 ? ‘top’ : (index === lastIndex ? ‘bottom’ : ”);u003Cbru003Ereturn (<u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E className=”u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E-card” key={item.key} style={{top}} ref={refVal} id={id}>{item.value}<u002Fu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E>);u003Cbru003E})}u003Cbru003E<u002Ful>u003Cbru003E);u003Cbru003E}u003Cbru003Eexport { SlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eook };u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E以及少许样式:u003Cu002Fpu003Eu003Cpreu003E.u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-5″u003Eliu003Cu002Fiu003E-card {u003Cbru003Edisplay: flex;u003Cbru003Ejustify-content: center;u003Cbru003Elist-style: none;u003Cbru003Ebox-shadow: 2px 2px 9px 0px #bbb;u003Cbru003Epadding: 70px 0;u003Cbru003Emargin-bottom: 20px;u003Cbru003Eborder-radius: 10px;u003Cbru003Eposition: absolute;u003Cbru003Ewidth: 80%;u003Cbru003E}u003Cbru003Eu003Cu002Fpreu003Eu003Cpu003E然后你就可以慢慢耍了。u003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRcgsnpRIwy6aPE” img_width=”640″ img_height=”384″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRTYlVY98d1MEiD” img_width=”340″ img_height=”57″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h2″u003Eu003Cstrongu003E兼容性处理u003Cu002Fstrongu003Eu003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Cpu003EIntersectionObserver不兼容Safari?u003Cu002Fpu003Eu003Cpu003E莫慌。我们有polyfill版。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRcgsnq21vlgowR” img_width=”1080″ img_height=”428″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003Eu003Cpu003E每周34万u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-3″u003E下载u003Cu002Fiu003E量呢,放心用吧。u003Cu002Fpu003Eu003Cpu003E项目源地址:https:u002Fu002Fgithubu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E.comu003Cu002Fiu003Eu002Froger-hirou002FSlidingWindowScrolu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003ElHu003Cu002Fiu003Eooku003Cu002Fpu003Eu003Cpu003E资料:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003ECreating Infinite Scroll with 15 Elementsu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EIntersectionObserve初试u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E【END】u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRVgbnJl7d57wok” img_width=”591″ img_height=”324″ alt=”90 行代码,15 个元素教你如何实现无限滚动” inline=”0″u003E”

原文始发于:90 行代码,15 个元素教你如何实现无限滚动

主题测试文章,只做测试使用。发布者:熱鬧獨處,转转请注明出处:http://www.cxybcw.com/15069.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code