溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

antd4里table滾動(dòng)如何實(shí)現(xiàn)

發(fā)布時(shí)間:2023-03-02 11:31:06 來源:億速云 閱讀:145 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“antd4里table滾動(dòng)如何實(shí)現(xiàn)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“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。

到此,相信大家對(duì)“antd4里table滾動(dòng)如何實(shí)現(xiàn)”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI