ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

React 实现鼠标水平滚动组件

2021-03-13 18:38:29  阅读:242  来源: 互联网

标签:current containerRef 滚动 鼠标 scrollLeft React offset 组件 const


实现要点

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

实现步骤

页面布局

  • 父元素采用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',
    width: '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}
        // onm ouseOver={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)

标签:current,containerRef,滚动,鼠标,scrollLeft,React,offset,组件,const
来源: https://www.cnblogs.com/li1234yun/p/14529894.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有