持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情
今天开始一起学习分享 ahooks 的源码
ahooks是阿里巴巴出品的一套高质量可靠的 React Hooks 库
今天分享 第10个 hooks-useScroll
useScroll
首先看下 useScroll 的作用是什么
useScroll 的作用是监听元素的滚动位置。
接下来 看下 API
API
const position = useScroll(target, shouldUpdate);
Params
一共两个参数
第1个参数是目标的元素,可以传入dom节点,也可以传入 ref
第2个参数是boolean值,表示控制是否更新滚动信息
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| target | DOM 节点或者 ref | Element | Document | (() => Element) | MutableRefObject<Element> | document |
| shouldUpdate | 控制是否更新滚动信息 | ({ top: number, left: number }) => boolean | - |
Result
结果返回一个position,表示滚动容器当前的滚动位置
| 参数 | 说明 | 类型 |
|---|---|---|
| position | 滚动容器当前的滚动位置 | { left: number, top: number } | undefined |
接下来 看下 用法
基本用法
尝试滚动一下文字内容。
import React, { useRef } from 'react';
import { useScroll } from 'ahooks';
export default () => {
const ref = useRef(null);
const scroll = useScroll(ref);
return (
<>
<p>{JSON.stringify(scroll)}</p>
<div
style={{
height: '160px',
width: '160px',
border: 'solid 1px #000',
overflow: 'scroll',
whiteSpace: 'nowrap',
fontSize: '32px',
}}
ref={ref}
>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aspernatur atque, debitis ex
excepturi explicabo iste iure labore molestiae neque optio perspiciatis
</div>
<div>
Aspernatur cupiditate, deleniti id incidunt mollitia omnis! A aspernatur assumenda
consequuntur culpa cumque dignissimos enim eos, et fugit natus nemo nesciunt
</div>
<div>
Alias aut deserunt expedita, inventore maiores minima officia porro rem. Accusamus ducimus
magni modi mollitia nihil nisi provident
</div>
<div>
Alias aut autem consequuntur doloremque esse facilis id molestiae neque officia placeat,
quia quisquam repellendus reprehenderit.
</div>
<div>
Adipisci blanditiis facere nam perspiciatis sit soluta ullam! Architecto aut blanditiis,
consectetur corporis cum deserunt distinctio dolore eius est exercitationem
</div>
<div>Ab aliquid asperiores assumenda corporis cumque dolorum expedita</div>
<div>
Culpa cumque eveniet natus totam! Adipisci, animi at commodi delectus distinctio dolore
earum, eum expedita facilis
</div>
<div>
Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
</div>
</div>
</>
);
};
当滚动文字的时候 top值会实时更新
监测整页的滚动
尝试滚动一下页面。当滚动页面的时候,会监听整个页面的滚动
import React from 'react';
import { useScroll } from 'ahooks';
export default () => {
const scroll = useScroll(document);
return (
<div>
<div>{JSON.stringify(scroll)}</div>
</div>
);
};
控制滚动状态的监听
在垂直方向 100px 到 200px 的滚动范围内监听
import React, { useRef } from 'react';
import { useScroll } from 'ahooks';
export default () => {
const ref = useRef(null);
const scroll = useScroll(ref, (val) => val.top > 100 && val.top < 200);
return (
<>
<p>{JSON.stringify(scroll)}</p>
<div
style={{
height: '160px',
width: '160px',
border: 'solid 1px #000',
overflow: 'scroll',
whiteSpace: 'nowrap',
fontSize: '36px',
}}
ref={ref}
>
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. A aspernatur atque, debitis ex
excepturi explicabo iste iure labore molestiae neque optio perspiciatis
</div>
<div>
Aspernatur cupiditate, deleniti id incidunt mollitia omnis! A aspernatur assumenda
consequuntur culpa cumque dignissimos enim eos, et fugit natus nemo nesciunt
</div>
<div>
Alias aut deserunt expedita, inventore maiores minima officia porro rem. Accusamus ducimus
magni modi mollitia nihil nisi provident
</div>
<div>
Alias aut autem consequuntur doloremque esse facilis id molestiae neque officia placeat,
quia quisquam repellendus reprehenderit.
</div>
<div>
Adipisci blanditiis facere nam perspiciatis sit soluta ullam! Architecto aut blanditiis,
consectetur corporis cum deserunt distinctio dolore eius est exercitationem
</div>
<div>Ab aliquid asperiores assumenda corporis cumque dolorum expedita</div>
<div>
Culpa cumque eveniet natus totam! Adipisci, animi at commodi delectus distinctio dolore
earum, eum expedita facilis
</div>
<div>
Quod sit, temporibus! Amet animi fugit officiis perspiciatis, quis unde. Cumque
dignissimos distinctio, dolor eaque est fugit nisi non pariatur porro possimus, quas quasi
</div>
</div>
</>
);
};
接下来一起看下 源码
源码
1.首先定义 目标元素 target的类型
target?: Target,
export type Target = BasicTarget<Element | Document>;
2.定义 shouldUpdate 的类型
shouldUpdate: ScrollListenController = () => true,
export type ScrollListenController = (val: Position) => boolean;
3.通过 state 来 初始化 当前位置信息
const [position, setPosition] = useRafState<Position>();
4.使用 useLatest来获取当前最新的shouldUpdate函数值
const shouldUpdateRef = useLatest(shouldUpdate);
5.获取当前的目标元素,如果元素不存在,则直接 return
const el = getTargetElement(target, document);
if (!el) {
return;
}
6.使用addEventListener来监听元素滚动,使用removeEventListener来移除元素滚动监听
el.addEventListener('scroll', updatePosition);
return () => {
el.removeEventListener('scroll', updatePosition);
};
7.接下来在看下 执行的函数updatePosition。首先判断 目标元素是否是document,如果不是document,就将当前元素的left和top赋值给newPosition。如果是document,并且是document.scrollingElement,则将document.scrollingElement.scrollLeft和document.scrollingElement.scrollTop赋值给newPosition,否则在window.pageXOffset、document.documentElement.scrollTop、document.body.scrollTop最大值赋值给newPosition。最后通过setPosition更新newPosition。
useEffectWithTarget(
() => {
const el = getTargetElement(target, document);
if (!el) {
return;
}
const updatePosition = () => {
let newPosition: Position;
if (el === document) {
if (document.scrollingElement) {
newPosition = {
left: document.scrollingElement.scrollLeft,
top: document.scrollingElement.scrollTop,
};
} else {
// When in quirks mode, the scrollingElement attribute returns the HTML body element if it exists and is potentially scrollable, otherwise it returns null.
// https://developer.mozilla.org/zh-CN/docs/Web/API/Document/scrollingElement
// https://stackoverflow.com/questions/28633221/document-body-scrolltop-firefox-returns-0-only-js
newPosition = {
left: Math.max(
window.pageXOffset,
document.documentElement.scrollLeft,
document.body.scrollLeft,
),
top: Math.max(
window.pageYOffset,
document.documentElement.scrollTop,
document.body.scrollTop,
),
};
}
} else {
newPosition = {
left: (el as Element).scrollLeft,
top: (el as Element).scrollTop,
};
}
if (shouldUpdateRef.current(newPosition)) {
setPosition(newPosition);
}
};
updatePosition();
el.addEventListener('scroll', updatePosition);
return () => {
el.removeEventListener('scroll', updatePosition);
};
},
[],
target,
);
8.最后返回position
return position;