本篇内容主要讲解“antd4里table滚动如何实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“antd4里table滚动如何实现”吧!
首先antd4的table的底层实现是rc-table,就从rc-table来看看。
一、rc-table里Header、Footer、TableBody实现保持同频滚动的方法
场景:Table内容区域大于容器Table宽度,并且Table设置了scrollX,Header、Footer都有, 才关注同频滚动
那么是如何实现的?
监听onScroll方法获取到滚动条向左的滚动的距离scrollLeft;
同时给三个dom设置scrollLeft
二、 rc-table里的onScroll实现
先看一般的onScroll实现
监听onScroll获取scrollLeft
设置header、footer、tableBody的scrollLeft
下面是伪代码哈
const onScroll = (e: ScrollEvent) => { // 拿到scrollLeft const scrollLeft = e.target.scrollLeft // 给所有的header、footer、table-body设置scrollLeft header.scrollLeft = scrollLeft footer.scrollLeft = scrollLeft tableBody.scrollLeft = scrollLeft}
源码里onScroll的实现
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); //一个 滚动需要 控制 header、body、summary、stickyScrollBar所有同步滚动 // header设置scrollLeft scrollHeaderRef.current = mergedScrollLeft // body 设置scrollLeft scrollBodyRef.current = mergedScrollLeft } };
对比两个的实现,可以看到rc-table里的实现多了一个入参scrollLeft和一个if判断;
为什么多了一个入参、一个判断?继续往下看?
三、 Header、Footer的滚动监听
用组件FixedHolder实现,给FixedHolder绑定ref;
监听的是onWheel, 不是onScroll;
为什么监听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>; // 避免触发不必要滚动, 是一种优化 if (deltaX) { onScroll({ currentTarget, scrollLeft: currentTarget.scrollLeft + deltaX }); e.preventDefault(); } } fixHolder.current?.addEventListener('wheel', onWheel); return () => { fixHolder.current?.removeEventListener('wheel', onWheel); }; }, []);
不要将 onscroll 与 onwheel混淆。onwheel 是鼠标滚轮旋转,而 onscroll 处理的是对象内部内容区的滚动事件。
当dom满足下面任意一条的时候,不会触发onScroll;
overflow:hidden
滚动条不存在
FixHolder组件
设置了样式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,调用useCallback赋值dom;利用scrollRef.current监听wheel事件,转成onScroll,增加入参scrollLeft;
const setScrollRef = React.useCallback((element: HTMLElement) => { scrollRef.current = element; }, []);
四、TableBody的滚动
当然是监听onScroll事件;
给Tables设置scrollX的情况下,TableBody设置样式{overflow-x: auto}这样会有同频滚动
<div style={ ...scrollXStyle, ...scrollYStyle } onScroll={onScroll} ref={scrollBodyRef} > <TableComponent> {bodyColGroup} {bodyTable} </TableComponent> </div>
五、很nice的点
获得当前正在执行的dom
const [setScrollTarget, getScrollTarget] = useTimeoutLock(null);
getScrollTarget用来获得当前正在执行的dom
使用useState来存储正在执行的dom; 当组件重新渲染,dom更新,此时正在执行的dom,在下一个render的时候,就变了;useRef在下一次渲染之前不重新赋值,还是保留和上一次一样的值;
源码里使用useRef + setTimeout实现;useRef是用来存放当前正在执行的dom;setTimeout用来节流;
其中getState获取正在执行当前state,可能是空的;setState设置当前的State,并且在100ms以后清空设置的状态;
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; // 清空上一次的定时器 cleanUp(); timeoutRef.current = window.setTimeout(() => { frameRef.current = null; timeoutRef.current = undefined; }, 100); } function getState() { return frameRef.current; } useEffect(() => cleanUp, []); return [setState, getState];}
onScroll为什么设置if判断
getScrollTarget()调用onScroll的之前,是否有滚动的dom; 没有就更新;有,判断是否和触发onScroll是相同Dom;是,更新;目的是为了避免执行上一个onScroll的时候,下一个onScroll执行,陷入循环,就相当于节流了;hooks版本的节流
const compareTarget = currentTarget || EMPTY_SCROLL_TARGET; // 固定滚动项// 在处理上一个滚动的时候,禁止下一个也滚动执行onScrollif (!getScrollTarget() || getScrollTarget() === compareTarget) { setScrollTarget(compareTarget);}
看这块逻辑的时候,优化细节;从hooks的角度,实现节流;wheel和scroll都是滚动,但是也有区别;并且在react里支持dom绑定onScroll、onWheel。
到此,相信大家对“antd4里table滚动如何实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!