一区二区日本_久久久久久久国产精品_无码国模国产在线观看_久久99深爱久久99精品_亚洲一区二区三区四区五区午夜_日本在线观看一区二区

用react實(shí)現(xiàn)一個(gè)簡(jiǎn)單的scrollView組件

目錄 效果 設(shè)計(jì)考慮的問題 1: 結(jié)構(gòu) 結(jié)構(gòu)搭建 邏輯處理 問題 總結(jié) 效果 我們先看一下效果,大概就是希望點(diǎn)擊左邊按鈕 或者右邊按鈕將元素以 每一個(gè) 子選項(xiàng)卡長(zhǎng)度單位進(jìn)行精準(zhǔn)偏移
目錄
  • 效果
  • 設(shè)計(jì)考慮的問題
    • 1: 結(jié)構(gòu)
      • 結(jié)構(gòu)搭建
      • 邏輯處理
  • 問題
    • 總結(jié)

      效果

      我們先看一下效果,大概就是希望點(diǎn)擊左邊按鈕 或者右邊按鈕將元素以 每一個(gè) 子選項(xiàng)卡長(zhǎng)度單位進(jìn)行精準(zhǔn)偏移。

      設(shè)計(jì)考慮的問題

      在這之前,大家不妨思考一下這個(gè)需求給到你,你應(yīng)該怎么去設(shè)計(jì)這個(gè)東西?

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

      2 :我們應(yīng)該如何進(jìn)行偏移 ? 用定位還是位移 ? 他們有什么區(qū)別?如何保證偏移量一點(diǎn)不差?

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

      好,帶著以下幾個(gè)問題,我們一起來思考一下這個(gè)組件,應(yīng)該如何封裝。

      1: 結(jié)構(gòu)

      首先 , 結(jié)構(gòu)如下圖,我們有一個(gè) 父盒子(content-wapper-hidden) ,還有一個(gè)子盒子內(nèi)嵌在 父盒子中,父盒子負(fù)責(zé)元素的溢出隱藏,固定寬度,子盒子負(fù)責(zé)渲染,和滾動(dòng)。

      結(jié)構(gòu)搭建

      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 : 接下來 我們分別給兩個(gè) 按鈕綁定事件 , 并且 在初始化的時(shí)候 去計(jì)算 最大可偏移的值,

      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) // 兩者最大的偏移量 可滾動(dòng)距離
        const [currentOffsetIndex, setCurrentOffsetIndex] = useState(0) // 當(dāng)前滾動(dòng)元素的索引
        const handelIconClick = (isRoght: boolean) => {
        // 事件處理函數(shù) 
        }
        function getScrollOffset() {
          const scrollWidth = contentRef.current!.scrollWidth
          const clientWidth = contentRef.current!.clientWidth
          setMaxOffset(scrollWidth - clientWidth) // 計(jì)算最大偏移量
        }
        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 綁定了 一個(gè) 內(nèi)容元素 ,然后 初始化的時(shí)候 ,我們?nèi)ビ?jì)算了元素最大可滾動(dòng)的距離, 然后當(dāng)我們點(diǎn)擊了 按鈕時(shí) 我們獲取了 contentRef 下所有 綁定 類名為 item的元素,點(diǎn)擊時(shí)判斷 是否為 右側(cè) 如果是 右側(cè) 點(diǎn)擊 則 索引 + 1 否則 索引 -1 ,然后做邊界處理,依次獲得item的 offsetLeft ,注意 offsetleft 是相對(duì)于 父級(jí)元素的距離,然后 將元素 contentRef 進(jìn)行 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) // 兩者最大的偏移量 可滾動(dòng)距離
        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 子節(jié)點(diǎn)
          const TabItem = TabAllList[newIndex] as HTMLDivElement // 獲取下一個(gè) 準(zhǔn)備滾動(dòng)元素
          const TabItemOffsetLeft = TabItem.offsetLeft
          contentRef.current!.style.transform = `translateX(${-TabItemOffsetLeft}px)`
          setisContinueScroll(maxoffset > TabItemOffsetLeft) // 是否能繼續(xù)滾動(dòng) 如果 你的 offsetLeft 都
          // 比我可滾動(dòng)距離大了 , 則肯定是不能滾動(dòng)的
          setCurrentOffsetIndex(newIndex)
        }
        const getAllElements = (querySelectorName: string, type: 'id' | 'class' | 'el' = 'class') => {
          let seletorName = null
          if (type === 'id') { // 對(duì)選擇器 做不同類型處理
            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 組件就簡(jiǎn)單的封裝完成了 , 當(dāng)然你還可以集成 點(diǎn)擊某個(gè)選項(xiàng)時(shí) 再進(jìn)行偏移也是可以的,額可以拓展一下。

      問題

      為什么 用 tranform 而不是定位 ?

      答案很簡(jiǎn)單 : 定位移動(dòng)的回引發(fā)視圖重繪,而transform 不會(huì)觸發(fā)重回,出于這一點(diǎn)可以在性能上做優(yōu)化,當(dāng)然 掘友在上一張 彈幕的文章中也講到 這一點(diǎn),謝謝大家

      總結(jié)

      總結(jié)下來,這個(gè)組件 我們主要做的就是它的transform ,當(dāng)然還有更多的功能大家可以拓展一下,如果你覺得還有哪些可以補(bǔ)充的,歡迎評(píng)論區(qū)留言。

      以上就是用react實(shí)現(xiàn)一個(gè)簡(jiǎn)單的scrollView組件的詳細(xì)內(nèi)容,更多關(guān)于react實(shí)現(xiàn)scrollView組件的資料請(qǐng)關(guān)注技圈網(wǎng)其它相關(guān)文章!

      聲明:所有內(nèi)容來自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
      發(fā)表評(píng)論
      更多 網(wǎng)友評(píng)論0 條評(píng)論)
      暫無評(píng)論

      返回頂部

      主站蜘蛛池模板: 亚洲精彩视频 | 神马久久av | 在线免费av电影 | 欧美精品一区三区 | 羞羞的视频免费在线观看 | 麻豆国产精品777777在线 | www.成人在线视频 | 日本不卡视频在线播放 | 北条麻妃国产九九九精品小说 | 国产亚洲精品精品国产亚洲综合 | 精品网站999 | www亚洲免费国内精品 | 欧美一级大黄 | 精品美女久久久久久免费 | 久久国产精品99久久久久久丝袜 | 国产精品久久久久久久久动漫 | 99久久婷婷| 97免费在线视频 | 91视频一区 | 91一区二区 | 欧美日韩不卡合集视频 | 国产黄色网址在线观看 | 久久噜噜噜精品国产亚洲综合 | 国产精品毛片一区二区在线看 | 超碰人人人 | 欧美一区在线看 | 一区二区在线 | 国产成人精品久久二区二区91 | 99在线免费视频 | 久久a久久| 国产黄色精品在线观看 | 欧美日韩不卡在线 | 台湾av在线| 欧美一区二区三区久久精品视 | 美女一区 | 日本精品一区二区三区视频 | 欧美日韩三区 | 午夜寂寞影院在线观看 | 在线观看 亚洲 | 婷婷丁香在线视频 | 国产在线中文字幕 |