React 实现鼠标水平滚动组件

react

实现要点

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

实现步骤

页面布局

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

事件监听

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

注意事项

  • 使用reactonWheel事件进行阻止默认行为无效,且会提示报错,所以使用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}

// 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)

以上是 React 实现鼠标水平滚动组件 的全部内容, 来源链接: utcz.com/z/383769.html

回到顶部