banner.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import React, { useCallback, useEffect, useRef, useState } from 'react'
  2. import Slider from 'react-slick'
  3. import 'slick-carousel/slick/slick.css'
  4. import 'slick-carousel/slick/slick-theme.css'
  5. import classNames from 'classnames'
  6. import styles from './index.module.scss' // 自定义样式
  7. type Slide = {
  8. id: number
  9. background: string
  10. content: React.ReactNode
  11. }
  12. const VerticalBanner: React.FC<{ slides: Slide[] }> = ({ slides }) => {
  13. const sliderRef = useRef<Slider>(null)
  14. const isScrolling = useRef(false)
  15. const [activeIndex, setActiveIndex] = useState<number | undefined>(undefined)
  16. // 垂直轮播配置
  17. const settings = {
  18. dots: true,
  19. dotsClass: 'slick-dots vertical-dots', // 自定义垂直dots样式
  20. infinite: true,
  21. speed: 800,
  22. slidesToShow: 1,
  23. slidesToScroll: 1,
  24. arrows: false,
  25. autoplay: false,
  26. adaptiveHeight: true,
  27. initialSlide: 0,
  28. afterChange: (index: number) => {
  29. setActiveIndex(index)
  30. }
  31. }
  32. // 处理滚轮事件
  33. const handleWheel = useCallback((e: WheelEvent) => {
  34. if (isScrolling.current) return
  35. // 阻止页面默认滚动
  36. e.preventDefault()
  37. // 设置滚动锁定,避免连续触发
  38. isScrolling.current = true
  39. setTimeout(() => {
  40. isScrolling.current = false
  41. }, 1000) // 滚动动画期间锁定
  42. // 根据滚轮方向切换幻灯片
  43. if (e.deltaY > 0) {
  44. sliderRef.current?.slickNext()
  45. } else {
  46. sliderRef.current?.slickPrev()
  47. }
  48. }, [])
  49. // 注册滚轮事件
  50. useEffect(() => {
  51. setTimeout(() => {
  52. setActiveIndex(0)
  53. }, 300)
  54. const handleScroll = (e: WheelEvent) => {
  55. // 仅在组件挂载时处理
  56. if (sliderRef.current) {
  57. handleWheel(e)
  58. }
  59. }
  60. // 添加滚轮事件监听器
  61. window.addEventListener('wheel', handleScroll, { passive: false })
  62. return () => {
  63. // 组件卸载时移除监听器
  64. window.removeEventListener('wheel', handleScroll)
  65. }
  66. }, [handleWheel])
  67. return (
  68. <div className={styles.banner}>
  69. <Slider ref={sliderRef} {...settings}>
  70. {slides.map((slide, index) => {
  71. const isActive = activeIndex === index
  72. return (
  73. <div key={slide.id} className='fullscreen-slide'>
  74. <div className={classNames('slide-content', { animate: isActive })} style={{ background: `url(${slide.background}) no-repeat center center/cover` }}>
  75. {slide.content}
  76. </div>
  77. </div>
  78. )
  79. })}
  80. </Slider>
  81. </div>
  82. )
  83. }
  84. export default VerticalBanner