zoukankan      html  css  js  c++  java
  • React 实现鼠标水平滚动组件

    实现要点

    • 页面布局
    • 监听鼠标滚动事件
    • 计算滚动位置进行对齐

    实现步骤

    页面布局

    • 父元素采用flex布局且设置flex-wrap: nowrap使其子元素可以完全展开
    • 子元素设置flex-shrink: 0使其能够不进行自适应缩小

    事件监听

    • 通过调用event.preventDefault()阻止浏览器默认行为
    • 使用useRef()获取父元素的DOM元素,使用.current获取dom对象进行操作
    • 设置父元素的wheel鼠标滚动监听事件,并进行对应的计算

    注意事项

    • 使用react onWheel事件进行阻止默认行为无效,且会提示报错,所以使用ref获取dom元素代替
    • react 事件是合成事件且不持久,不可异步传入

    元素滚动

    • 元素可以通过scrollTo()方法进行滚动

    • Tips:

      • offsetWidth/offsetHeight 获取元素宽高
      • scrollLeft/Top 获取偏移位置
      • scrollWidth 获取滚动宽度

    参考代码

    import { createStyles, withStyles } from '@material-ui/core/styles'
    import { SitePropsType } from 'components/base/Site'
    import { useEffect, useRef } from 'react'
    
    const styles = createStyles({
      root: {
        overflowX: 'auto',
      },
      container: {
        display: 'flex',
        flexWrap: 'nowrap',
        overflowX: 'auto',
      },
      item: {
        height: '300px',
         '100%',
        backgroundColor: '#f0f0f0',
        border: '1px solid #333333',
        flexShrink: 0,
        // '&:hover': {
        //   cursor: 'pointer',
        // },
      },
      indicator: {},
    })
    
    interface SiteSwiperProps {
      classes?: {
        root: string
        container: string
        item: string
        indicator: string
      }
      sites: SitePropsType[]
      row?: number
    }
    
    /**
     * 计算滚动位置
     * @param currentScrollLeft
     * @param scrollElWith
     */
    const computeScroll = (
      currentScrollLeft: number,
      scrollElWith: number
    ): number => {
      // 判断滚动偏移是否满足滚动要求
      console.log('current scroll left:', currentScrollLeft)
      const index = Math.round(currentScrollLeft / scrollElWith)
      return scrollElWith * index
    }
    
    function SiteSwiper({ classes, sites, row = 3 }: SiteSwiperProps): JSX.Element {
      const containerRef = useRef(null)
      const timer = useRef(null)
    
      useEffect(() => {
        console.log('current ref:', containerRef)
        containerRef.current.addEventListener('wheel', (e) => {
          console.log('mouse wheel event:', e)
          // 阻止原生滚动事件
          e.preventDefault()
    
          // 获取滚动位置
          let scrollLeft = containerRef.current.scrollLeft
          const scrollTotalWidth = containerRef.current.scrollWidth
          const scrollItemWidth = containerRef.current.offsetWidth
    
          // 获取容器的宽度
          console.log(
            'current container:',
            containerRef.current.offsetWidth,
            e.deltaY
          )
          // 即时水平滚动偏移值
          const bufferOffset = 70
          const scrollBehavior = 'smooth'
          let offset = scrollLeft + e.deltaY * 4 // 放大偏移倍数
          if (offset >= scrollTotalWidth - scrollItemWidth + bufferOffset) {
            // 到达最后元素
            offset = offset - scrollTotalWidth - bufferOffset
            // scrollBehavior = 'auto'
          } else if (offset + bufferOffset < 0) {
            // 达到第一元素
            offset = scrollTotalWidth + offset - bufferOffset
            // scrollBehavior = 'auto'
          } else {
            // 其它情况
          }
          console.log('offset y at time:', scrollLeft, offset)
          containerRef.current.scrollTo({
            top: 0,
            left: offset,
            behavior: scrollBehavior,
          })
    
          // 防抖
          if (timer.current) {
            clearTimeout(timer.current)
          }
    
          timer.current = setTimeout(() => {
            // 计算滚动最后的位置进行位置矫正
            console.log('TIME OUT: starting position correct...')
            // 计算是否滚动
            scrollLeft = computeScroll(offset, scrollItemWidth)
    
            containerRef.current.scrollTo({
              top: 0,
              left: scrollLeft,
              behavior: 'smooth',
            })
          }, 700)
        })
      })
    
      return (
        <div className={classes.root} id="swiper-container">
          {/* Content */}
          <div
            className={classes.container}
            // onScroll={handleMouseScroll}
            // onMouseOver={handleMouseOver}
            // onWheel={handleWheel}
            ref={containerRef}
          >
            {[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((item) => (
              <div className={`${classes.item} swiper-item`} key={item}>
                {item}
              </div>
            ))}
          </div>
    
          {/* Indicator */}
          <div className={classes.indicator}></div>
        </div>
      )
    }
    
    export default withStyles(styles)(SiteSwiper)
    
    
  • 相关阅读:
    拓端tecdat|R语言JAGS贝叶斯回归模型分析博士生延期毕业完成论文时间
    拓端tecdat|数据感知游客的森林公园游憩需求
    空间100%
    uniq -c 去掉重复行
    工作中实用的Shell脚本实例
    Linux下如何解压和压缩rar格式的包
    LRM-00109: could not open parameter file
    Xmanager5 Passive oracle图形化界面出来之后鼠标点不了
    谷歌浏览器请求返回JSON内容自动格式化
    JENKINS中创建全局变量并在JOB中使用
  • 原文地址:https://www.cnblogs.com/li1234yun/p/14529894.html
Copyright © 2011-2022 走看看