用react實現一個簡單的scrollView組件

    目錄 效果 設計考慮的問題 1: 結構 結構搭建 邏輯處理 問題 總結 效果 我們先看一下效果,大概就是希望點擊左邊按鈕 或者右邊按鈕將元素以 每一個 子選項卡長度單位進行精準偏移
    目錄
    • 效果
    • 設計考慮的問題
      • 1: 結構
        • 結構搭建
        • 邏輯處理
    • 問題
      • 總結

        效果

        我們先看一下效果,大概就是希望點擊左邊按鈕 或者右邊按鈕將元素以 每一個 子選項卡長度單位進行精準偏移。

        設計考慮的問題

        在這之前,大家不妨思考一下這個需求給到你,你應該怎么去設計這個東西?

        1 : 父盒子的長度多少 ? 如何控制多出的元素隱藏?

        2 :我們應該如何進行偏移 ? 用定位還是位移 ? 他們有什么區別?如何保證偏移量一點不差?

        3 :每次偏移的量是多少?如何處理邊界情況?

        好,帶著以下幾個問題,我們一起來思考一下這個組件,應該如何封裝。

        1: 結構

        首先 , 結構如下圖,我們有一個 父盒子(content-wapper-hidden) ,還有一個子盒子內嵌在 父盒子中,父盒子負責元素的溢出隱藏,固定寬度,子盒子負責渲染,和滾動。

        結構搭建

        scrollView.tsx

        import { ReactNode, memo, useRef } from 'react'
        import { ScrollViewWapper } from './style'
        interface ScrollViewProps {
          children: ReactNode
        }
        const ScrollView = memo((
          {
            children
          }: ScrollViewProps
        ) => {
          const contentRef = useRef<HTMLDivElement>(null)
          return (
            <ScrollViewWapper>
              <div className='leftIcon'>左邊</div>
              <div className='content-wapper-hidden'>
                <div className='render-content' ref={contentRef}>
                  {children}
                </div>
              </div>
              <div className='rightIcon'>右邊</div>
            </ScrollViewWapper>
          )
        })
        export default ScrollView

        邏輯處理

        1 : 接下來 我們分別給兩個 按鈕綁定事件 , 并且 在初始化的時候 去計算 最大可偏移的值,

        import { ReactNode, memo, useEffect, useRef, useState } from 'react'
        import { ScrollViewWapper } from './style'
        interface ScrollViewProps {
          children: ReactNode
        }
        const ScrollView = memo((
          {
            children
          }: ScrollViewProps
        ) => {
          const contentRef = useRef<HTMLDivElement>(null)
          const [maxoffset, setMaxOffset] = useState(0) // 兩者最大的偏移量 可滾動距離
          const [currentOffsetIndex, setCurrentOffsetIndex] = useState(0) // 當前滾動元素的索引
          const handelIconClick = (isRoght: boolean) => {
          // 事件處理函數 
          }
          function getScrollOffset() {
            const scrollWidth = contentRef.current!.scrollWidth
            const clientWidth = contentRef.current!.clientWidth
            setMaxOffset(scrollWidth - clientWidth) // 計算最大偏移量
          }
          useEffect(() => {
            getScrollOffset()
          }, [])
          return (
            <ScrollViewWapper>
              <div className='leftIcon' onClick={() => handelIconClick(false)}>左邊</div>
              <div className='content-wapper-hidden'>
                <div className='render-content' ref={contentRef}>
                  {children}
                </div>
              </div>
              <div className='rightIcon' onClick={() => handelIconClick(true)}>右邊</div>
            </ScrollViewWapper>
          )
        })
        export default ScrollView

        在這里我們通過 ref 綁定了 一個 內容元素 ,然后 初始化的時候 ,我們去計算了元素最大可滾動的距離, 然后當我們點擊了 按鈕時 我們獲取了 contentRef 下所有 綁定 類名為 item的元素,點擊時判斷 是否為 右側 如果是 右側 點擊 則 索引 + 1 否則 索引 -1 ,然后做邊界處理,依次獲得item的 offsetLeft ,注意 offsetleft 是相對于 父級元素的距離,然后 將元素 contentRef 進行 translate 位移

        import { ReactNode, memo, useEffect, useRef, useState } from 'react'
        import { ScrollViewWapper } from './style'
        interface ScrollViewProps {
          children: ReactNode
        }
        const ScrollView = memo((
          {
            children
          }: ScrollViewProps
        ) => {
          const contentRef = useRef<HTMLDivElement>(null)
          const [maxoffset, setMaxOffset] = useState(0) // 兩者最大的偏移量 可滾動距離
          const [currentOffsetIndex, setCurrentOffsetIndex] = useState(0)
          const [isContinueScroll, setisContinueScroll] = useState(true)
          const handelIconClick = (isRight: boolean) => {
            const newIndex = isRight ? currentOffsetIndex + 1 : currentOffsetIndex - 1
            if (newIndex < 0 || (!isContinueScroll && isRight)) return // 邊界處理
            const TabAllList = getAllElements('item', 'class') // 獲取conntentRef 下面的 所有 item 子節點
            const TabItem = TabAllList[newIndex] as HTMLDivElement // 獲取下一個 準備滾動元素
            const TabItemOffsetLeft = TabItem.offsetLeft
            contentRef.current!.style.transform = `translateX(${-TabItemOffsetLeft}px)`
            setisContinueScroll(maxoffset > TabItemOffsetLeft) // 是否能繼續滾動 如果 你的 offsetLeft 都
            // 比我可滾動距離大了 , 則肯定是不能滾動的
            setCurrentOffsetIndex(newIndex)
          }
          const getAllElements = (querySelectorName: string, type: 'id' | 'class' | 'el' = 'class') => {
            let seletorName = null
            if (type === 'id') { // 對選擇器 做不同類型處理
              seletorName = `#${querySelectorName.replace(/\^#/, '')}`
            } else if (type == 'class') {
              seletorName = `.${querySelectorName.replace(/\^./, '')}`
            } else {
              seletorName = `${querySelectorName.replace(/\^(.|#)/, '')}`
            }
            return contentRef.current!.querySelectorAll(seletorName)
          }
          function getScrollOffset() {
            const scrollWidth = contentRef.current!.scrollWidth
            const clientWidth = contentRef.current!.clientWidth
            setMaxOffset(scrollWidth - clientWidth)
          }
          useEffect(() => {
            getScrollOffset()
          }, [])
          return (
            <ScrollViewWapper>
              <div className='leftIcon' onClick={() => handelIconClick(false)}>左邊</div>
              <div className='content-wapper-hidden'>
                <div className='render-content' ref={contentRef}>
                  {children}
                </div>
              </div>
              <div className='rightIcon' onClick={() => handelIconClick(true)}>右邊</div>
            </ScrollViewWapper>
          )
        })
        export default ScrollView

        外部使用ScrollView 組件

        import classNames from 'classnames';
        import { memo, useState } from 'react';
        import ScrollView from './components';
        const Login = memo(() => {
          const [list, setList] = useState(['YYDS', '易烊千璽', '李易峰', '雞哥', '古巨基', '羅志祥', '肖站', '彭于晏'])
          const [currentIndex, setCurrentIndex] = useState(0)
          return (
            <div id="danmu-container">
              <ScrollView>
                {
                  list.map((item, index) => {
                    return (
                      <div className='item' key={item} onClick={() => setCurrentIndex(index)}>
                        <div className={classNames('tab-item', currentIndex === index ? 'active' : '')}>{item}</div>
                      </div>
                    )
                  })
                }
              </ScrollView>
            </div>
          )
        })
        export default Login 

        ok 到這里 scrollView 組件就簡單的封裝完成了 , 當然你還可以集成 點擊某個選項時 再進行偏移也是可以的,額可以拓展一下。

        問題

        為什么 用 tranform 而不是定位 ?

        答案很簡單 : 定位移動的回引發視圖重繪,而transform 不會觸發重回,出于這一點可以在性能上做優化,當然 掘友在上一張 彈幕的文章中也講到 這一點,謝謝大家

        總結

        總結下來,這個組件 我們主要做的就是它的transform ,當然還有更多的功能大家可以拓展一下,如果你覺得還有哪些可以補充的,歡迎評論區留言。

        以上就是用react實現一個簡單的scrollView組件的詳細內容,更多關于react實現scrollView組件的資料請關注技圈網其它相關文章!

        聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
        發表評論
        更多 網友評論0 條評論)
        暫無評論

        返回頂部

        主站蜘蛛池模板: 一区二区三区四区视频| 国产suv精品一区二区33| 日韩人妻无码一区二区三区久久 | 亚洲码一区二区三区| 福利片福利一区二区三区| 2022年亚洲午夜一区二区福利| 精品视频一区二区观看| 亚洲国产欧美一区二区三区| 日韩欧国产精品一区综合无码| 一区二区三区免费视频网站| 国产SUV精品一区二区88| 精品无码人妻一区二区三区品 | 亚洲AV无码一区二区三区网址| 好爽毛片一区二区三区四| 立川理惠在线播放一区| 精品国产精品久久一区免费式| 色一情一乱一伦一区二区三欧美| 精品人无码一区二区三区| 日韩一区二区在线观看视频| 精品无码国产一区二区三区AV| 午夜福利一区二区三区高清视频| 国产亚洲一区二区精品| 国产在线精品一区二区中文| 免费观看一区二区三区| 久久久91精品国产一区二区三区| 国产无套精品一区二区| 亚洲av综合av一区| 亚洲视频在线一区| 亚洲码欧美码一区二区三区| 无码国产精品久久一区免费| 国产一区二区不卡老阿姨| 精品日韩一区二区| 一区二区不卡久久精品| 国产成人无码一区二区在线观看| 国产激情一区二区三区在线观看| 午夜视频久久久久一区| 国产凹凸在线一区二区| 91精品一区二区三区在线观看| 波多野结衣一区视频在线| 亚洲一区二区三区成人网站| 日本无码一区二区三区白峰美 |