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)
    
    
  • 相关阅读:
    FastApi 进阶
    flask为多个接口添加同一个拦截器的方法
    记一次flask上传文件返回200前端却504的问题
    Python在项目外更改项目内引用
    go mod 拉取私有仓库
    go跳出多层循环的几种方式
    Zap简单使用
    记一次Goroutine与wg导致的问题
    go判断字符串是否是IP地址
    SpringBoot的启动流程
  • 原文地址:https://www.cnblogs.com/li1234yun/p/14529894.html
Copyright © 2011-2022 走看看