antd4里table滾動(dòng)的實(shí)現(xiàn)
首先antd4的table的底層實(shí)現(xiàn)是rc-table,就從rc-table來看看。
一、rc-table里Header、Footer、TableBody實(shí)現(xiàn)保持同頻滾動(dòng)的方法
場(chǎng)景:Table內(nèi)容區(qū)域大于容器Table寬度,并且Table設(shè)置了scrollX,Header、Footer都有, 才關(guān)注同頻滾動(dòng)
那么是如何實(shí)現(xiàn)的?
- 監(jiān)聽onScroll方法獲取到滾動(dòng)條向左的滾動(dòng)的距離scrollLeft;
- 同時(shí)給三個(gè)dom設(shè)置scrollLeft
二、 rc-table里的onScroll實(shí)現(xiàn)
先看一般的onScroll實(shí)現(xiàn)
- 監(jiān)聽onScroll獲取scrollLeft
- 設(shè)置header、footer、tableBody的scrollLeft
下面是偽代碼哈
const onScroll = (e: ScrollEvent) => {
// 拿到scrollLeft
const scrollLeft = e.target.scrollLeft
// 給所有的header、footer、table-body設(shè)置scrollLeft
header.scrollLeft = scrollLeft
footer.scrollLeft = scrollLeft
tableBody.scrollLeft = scrollLeft
}
源碼里onScroll的實(shí)現(xiàn)
?const onScroll = ({
? ? currentTarget,
? ? scrollLeft,
? }: {
? ? currentTarget: HTMLElement;
? ? scrollLeft?: number;
? }) => {
? ? const mergedScrollLeft = typeof scrollLeft === 'number' ? scrollLeft : currentTarget.scrollLeft;
? ? const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;?
? ? if (!getScrollTarget() || getScrollTarget() === compareTarget) {?
? ? ? setScrollTarget(compareTarget);
? ? ? //一個(gè) 滾動(dòng)需要 控制 header、body、summary、stickyScrollBar所有同步滾動(dòng)
?? ??? ??? ?// header設(shè)置scrollLeft
?? ??? ??? ?scrollHeaderRef.current = mergedScrollLeft
?? ??? ??? ?// body 設(shè)置scrollLeft
?? ??? ??? ?scrollBodyRef.current = mergedScrollLeft
? ? }
? }; 對(duì)比兩個(gè)的實(shí)現(xiàn),可以看到rc-table里的實(shí)現(xiàn)多了一個(gè)入?yún)crollLeft和一個(gè)if判斷;
為什么多了一個(gè)入?yún)?、一個(gè)判斷?繼續(xù)往下看?
三、 Header、Footer的滾動(dòng)監(jiān)聽
- 用組件FixedHolder實(shí)現(xiàn),給FixedHolder綁定ref;
- 監(jiān)聽的是onWheel, 不是onScroll;
為什么監(jiān)聽onWheel不是onScroll?
React.useEffect(() => {
? ? ? function onWheel(e: WheelEvent) {
? ? ? ? // deltaX: Returns a double representing the horizontal scroll amount
? ? ? ? const { currentTarget, deltaX } = e as unknown as React.WheelEvent<HTMLDivElement>;
? ? ? ? // 避免觸發(fā)不必要滾動(dòng), 是一種優(yōu)化
? ? ? ? if (deltaX) {
? ? ? ? ? onScroll({ currentTarget, scrollLeft: currentTarget.scrollLeft + deltaX });
? ? ? ? ? e.preventDefault();
? ? ? ? }
? ? ? }
? ? ? fixHolder.current?.addEventListener('wheel', onWheel);
? ? ? return () => {
? ? ? ? fixHolder.current?.removeEventListener('wheel', onWheel);
? ? ? };
? ? }, []);不要將 onscroll 與 onwheel混淆。onwheel 是鼠標(biāo)滾輪旋轉(zhuǎn),而 onscroll 處理的是對(duì)象內(nèi)部?jī)?nèi)容區(qū)的滾動(dòng)事件。
當(dāng)dom滿足下面任意一條的時(shí)候,不會(huì)觸發(fā)onScroll;
- overflow:hidden
- 滾動(dòng)條不存在
FixHolder組件
設(shè)置了樣式overflow:hidden;
<div
style={{
overflow: 'hidden',
...(isSticky ? { top: stickyTopOffset, bottom: stickyBottomOffset } : {}),
}}
ref={setScrollRef}
className={classNames(className, {
[stickyClassName]: !!stickyClassName,
})}
/>
<table
style={{
tableLayout: 'fixed',
visibility: noData || mergedColumnWidth ? null : 'hidden',
}}
>
{(!noData || !maxContentScroll || allFlattenColumnsWithWidth) && (
<ColGroup
colWidths={mergedColumnWidth ? [...mergedColumnWidth, combinationScrollBarSize] : []}
columCount={columCount + 1}
columns={flattenColumnsWithScrollbar}
/>
)}
{children({
...props,
stickyOffsets: headerStickyOffsets,
columns: columnsWithScrollbar,
flattenColumns: flattenColumnsWithScrollbar,
})}
</table>
</div>通過ref,調(diào)用useCallback賦值dom;利用scrollRef.current監(jiān)聽wheel事件,轉(zhuǎn)成onScroll,增加入?yún)crollLeft;
const setScrollRef = React.useCallback((element: HTMLElement) => {
scrollRef.current = element;
}, []);
四、TableBody的滾動(dòng)
當(dāng)然是監(jiān)聽onScroll事件;
給Tables設(shè)置scrollX的情況下,TableBody設(shè)置樣式{overflow-x: auto}這樣會(huì)有同頻滾動(dòng)
<div
style={
...scrollXStyle,
...scrollYStyle
}
onScroll={onScroll}
ref={scrollBodyRef}
>
<TableComponent>
{bodyColGroup}
{bodyTable}
</TableComponent>
</div>
五、很nice的點(diǎn)
獲得當(dāng)前正在執(zhí)行的dom
const [setScrollTarget, getScrollTarget] = useTimeoutLock(null);
getScrollTarget用來獲得當(dāng)前正在執(zhí)行的dom
使用useState來存儲(chǔ)正在執(zhí)行的dom; 當(dāng)組件重新渲染,dom更新,此時(shí)正在執(zhí)行的dom,在下一個(gè)render的時(shí)候,就變了;useRef在下一次渲染之前不重新賦值,還是保留和上一次一樣的值;
源碼里使用useRef + setTimeout實(shí)現(xiàn);useRef是用來存放當(dāng)前正在執(zhí)行的dom;setTimeout用來節(jié)流;
其中g(shù)etState獲取正在執(zhí)行當(dāng)前state,可能是空的;setState設(shè)置當(dāng)前的State,并且在100ms以后清空設(shè)置的狀態(tài);
export function useTimeoutLock<State>(defaultState?: State): [(state: State) => void, () => State | null] {
? const frameRef = useRef<State | null>(defaultState || null);
? const timeoutRef = useRef<number>();
? function cleanUp() {
? ? window.clearTimeout(timeoutRef.current);
? }
? function setState(newState: State) {
? ? frameRef.current = newState;
?? ??? ?// 清空上一次的定時(shí)器
? ? cleanUp();
? ??
? ? timeoutRef.current = window.setTimeout(() => {
? ? ? frameRef.current = null;
? ? ? timeoutRef.current = undefined;
? ? }, 100);
? }
? function getState() {
? ? return frameRef.current;
? }
? useEffect(() => cleanUp, []);
? return [setState, getState];
}onScroll為什么設(shè)置if判斷
getScrollTarget()調(diào)用onScroll的之前,是否有滾動(dòng)的dom; 沒有就更新;有,判斷是否和觸發(fā)onScroll是相同Dom;是,更新;目的是為了避免執(zhí)行上一個(gè)onScroll的時(shí)候,下一個(gè)onScroll執(zhí)行,陷入循環(huán),就相當(dāng)于節(jié)流了;hooks版本的節(jié)流
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET;
// 固定滾動(dòng)項(xiàng)
// 在處理上一個(gè)滾動(dòng)的時(shí)候,禁止下一個(gè)也滾動(dòng)執(zhí)行onScroll
if (!getScrollTarget() || getScrollTarget() === compareTarget) {
setScrollTarget(compareTarget);
}
看這塊邏輯的時(shí)候,優(yōu)化細(xì)節(jié)??;從hooks的角度,實(shí)現(xiàn)節(jié)流;wheel和scroll都是滾動(dòng),但是也有區(qū)別;并且在react里支持dom綁定onScroll、onWheel;
相關(guān)鏈接:
rc-table: https://github.com/react-component/table
antd-table: https://ant.design/components/table-cn#components-table-demo-fixed-columns
到此這篇關(guān)于antd4里table滾動(dòng)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)antd4 table滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react將文件轉(zhuǎn)為base64上傳的示例代碼
本文主要介紹了react將文件轉(zhuǎn)為base64上傳的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09
React項(xiàng)目如何使用Element的方法步驟
本文主要介紹了React項(xiàng)目如何使用Element的方法步驟,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
React中的useState和setState的執(zhí)行機(jī)制詳解
這篇文章主要介紹了React中的useState和setState的執(zhí)行機(jī)制,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
React Native自定義標(biāo)題欄組件的實(shí)現(xiàn)方法
今天講一下如何實(shí)現(xiàn)自定義標(biāo)題欄組件,我們都知道RN有一個(gè)優(yōu)點(diǎn)就是可以組件化,在需要使用該組件的地方直接引用并傳遞一些參數(shù)就可以了,這種方式確實(shí)提高了開發(fā)效率。對(duì)React Native自定義標(biāo)題欄組件的實(shí)現(xiàn)方法感興趣的朋友參考下2017-01-01
React利用路由實(shí)現(xiàn)登錄界面的跳轉(zhuǎn)
這篇文章主要介紹了React利用路由實(shí)現(xiàn)登錄界面的跳轉(zhuǎn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
npx create-react-app xxx創(chuàng)建項(xiàng)目報(bào)錯(cuò)的解決辦法
這篇文章主要介紹了npx create-react-app xxx創(chuàng)建項(xiàng)目報(bào)錯(cuò)的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02
react項(xiàng)目如何運(yùn)行在微信公眾號(hào)
這篇文章主要介紹了react項(xiàng)目如何運(yùn)行在微信公眾號(hào),幫助大家更好的理解和學(xué)習(xí)使用react,感興趣的朋友可以了解下2021-04-04
React通過父組件傳遞類名給子組件的實(shí)現(xiàn)方法
React 是一個(gè)用于構(gòu)建用戶界面的 JAVASCRIPT 庫(kù)。這篇文章主要介紹了React通過父組件傳遞類名給子組件的方法,需要的朋友可以參考下2017-11-11
webpack4+react多頁面架構(gòu)的實(shí)現(xiàn)
webpack在單頁面打包上應(yīng)用廣泛,以create-react-app為首的腳手架眾多。這篇文章主要介紹了webpack4+react多頁面架構(gòu)的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10

