用react实现一个简单的scrollView组件

2023-12-01 0 355
目录
  • 效果
  • 设计考虑的问题
    • 1: 结构
      • 结构搭建
      • 逻辑处理
  • 问题
    • 总结

      效果

      我们先看一下效果,大概就是希望点击左边按钮 或者右边按钮将元素以 每一个 子选项卡长度单位进行精准偏移。

      用react实现一个简单的scrollView组件

      设计考虑的问题

      在这之前,大家不妨思考一下这个需求给到你,你应该怎么去设计这个东西?

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

      2 :我们应该如何进行偏移 ? 用定位还是位移 ? 他们有什么区别?如何保证偏移量一点不差?

      3 :每次偏移的量是多少?如何处理边界情况?

      好,带着以下几个问题,我们一起来思考一下这个组件,应该如何封装。

      1: 结构

      首先 , 结构如下图,我们有一个 父盒子(content-wapper-hidden) ,还有一个子盒子内嵌在 父盒子中,父盒子负责元素的溢出隐藏,固定宽度,子盒子负责渲染,和滚动。

      用react实现一个简单的scrollView组件

      结构搭建

      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

      用react实现一个简单的scrollView组件

      ok 到这里 scrollView 组件就简单的封装完成了 , 当然你还可以集成 点击某个选项时 再进行偏移也是可以的,额可以拓展一下。

      用react实现一个简单的scrollView组件

      问题

      为什么 用 tranform 而不是定位 ?

      答案很简单 : 定位移动的回引发视图重绘,而transform 不会触发重回,出于这一点可以在性能上做优化,当然 掘友在上一张 弹幕的文章中也讲到 这一点,谢谢大家

      总结

      总结下来,这个组件 我们主要做的就是它的transform ,当然还有更多的功能大家可以拓展一下,如果你觉得还有哪些可以补充的,欢迎评论区留言。

      以上就是用react实现一个简单的scrollView组件的详细内容,更多关于react实现scrollView组件的资料请关注悠久资源网其它相关文章!

      您可能感兴趣的文章:

      • React Native中ScrollView组件轮播图与ListView渲染列表组件用法实例分析
      • react native之ScrollView下拉刷新效果
      • Android超详细讲解组件ScrollView的使用
      • Flutter滚动组件之SingleChildScrollView使用详解
      • Android监听ScrollView滑动距离的简单处理
      • NestScrollView嵌套RecyclerView实现淘宝首页滑动效果

      收藏 (0) 打赏

      感谢您的支持,我会继续努力的!

      打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
      点赞 (0)

      悠久资源 JavaScript 用react实现一个简单的scrollView组件 https://www.u-9.cn/biancheng/javascript/4731.html

      常见问题

      相关文章

      发表评论
      暂无评论
      官方客服团队

      为您解决烦忧 - 24小时在线 专业服务