使用垂直鼠标滚轮在 React 组件上水平滚动

IT技术 javascript reactjs scroll horizontal-scrolling mousewheel
2021-05-20 13:23:27

我有一个组件,当在较小的桌面窗口中时,它会调整大小为一排水平的引导卡。对于没有水平鼠标滚轮且不使用触摸板的用户,我希望允许用户在悬停在此特定组件上时使用垂直鼠标滚轮移动水平滚动。

这是我基于代码的原始 StackOverflow 问题:https : //stackoverflow.com/a/15343916/8387497

水平滚动辅助组件:

function horizontalScroll (event) {
  const delta = Math.max(-1, Math.min(1, (event.nativeEvent.wheelDelta || -event.nativeEvent.detail)))
  event.currentTarget.scrollLeft -= (delta * 10)
  event.preventDefault
}

我如何在需要水平滚动的组件上实现它:

<Row className='announcements-home' onWheel={horizontalScroll} >

当我将此horizontalScroll辅助函数放置在 React onWheel 事件中时,它会水平和垂直滚动。我想要的结果只是水平滚动。此外,Firefox 似乎完全没有响应这些更改的水平滚动。

3个回答
onWheel = (e) => {
    e.preventDefault()
    var container = document.getElementById('container')
    var containerScrollPosition = document.getElementById('container').scrollLeft
    container.scrollTo({
        top: 0,
        left: largeContainerScrollPosition + e.deltaY
        behaviour: 'smooth' //if you want smooth scrolling
    })
}

好的,所以问题似乎是您只引用该函数event.preventDefault而不是调用它。在最后添加一些括号调用它应该做的伎俩: event.preventDefault()

然而,我在寻找一些简单的代码时发现了这个问题,所以如果其他人处于相同的情况,我也会保留我为此创建的钩子:

import { useRef, useEffect } from "react";

export function useHorizontalScroll() {
  const elRef = useRef();
  useEffect(() => {
    const el = elRef.current;
    if (el) {
      const onWheel = e => {
        if (e.deltaY == 0) return;
        e.preventDefault();
        el.scrollTo({
          left: el.scrollLeft + e.deltaY,
          behavior: "smooth"
        });
      };
      el.addEventListener("wheel", onWheel);
      return () => el.removeEventListener("wheel", onWheel);
    }
  }, []);
  return elRef;
}

用法:

import React from "react";
import { useSideScroll } from "./useSideScroll";

export const SideScrollTest = () => {
  const scrollRef = useHorizontalScroll();
  return (
    <div ref={scrollRef} style={{ width: 300, overflow: "auto" }}>
      <div style={{ whiteSpace: "nowrap" }}>
        I will definitely overflow due to the small width of my parent container
      </div>
    </div>
  );
};

注意:在尝试进行连续滚动时,滚动行为“平滑”似乎会带来一些麻烦。可以省略此行为以获得适当的连续滚动,但它看起来会生涩。

据我所知,对此没有简单的解决方案。然而,我在我自己的项目中创建了一个相当复杂的解决方案,所以我想有些人可能也会欣赏:https : //gist.github.com/TarVK/4cc89772e606e57f268d479605d7aded

TarVK 提出的钩子还有一个小问题。一旦你滚动到最后并继续滚动,当我们习惯于包含开始滚动的元素时,什么也不会发生。所以我做了一个修复:

export function useHorizontalScroll () {
  const elRef = useRef();
  useEffect(() => {
    const el = elRef.current;
    if (el) {
      const onWheel = (e) => {
        if (e.deltaY === 0) return;
        if (
          !(el.scrollLeft === 0 && e.deltaY < 0) &&
          !(el.scrollWidth - el.clientWidth - Math.round(el.scrollLeft) === 0 && 
              e.deltaY > 0)
        ) {
          e.preventDefault();
        }
        el.scrollTo({
          left: el.scrollLeft + e.deltaY,
          behavior: 'smooth'
        });
      };
      el.addEventListener('wheel', onWheel);
      return () => el.removeEventListener('wheel', onWheel);
    }
  }, []);
  return elRef;
}

只有当有空间可以在那个方向滚动时,它才会有条件地阻止默认行为,因此当没有空间可以滚动时,例如整个页面将开始滚动。变化在这里:

        if (
          !(el.scrollLeft === 0 && e.deltaY < 0) &&
          !(el.scrollWidth - el.clientWidth - Math.round(el.scrollLeft) === 0 && 
              e.deltaY > 0)
        ) {
          e.preventDefault();
        }