【问题标题】:Horizontal Scrolling on React Component Using Vertical Mouse Wheel使用垂直鼠标滚轮在 React 组件上水平滚动
【发布时间】:2019-05-15 16:24:14
【问题描述】:

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

这是我基于我的代码的原始 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 似乎根本不响应这些更改的水平滚动。

【问题讨论】:

    标签: javascript reactjs scroll horizontal-scrolling mousewheel


    【解决方案1】:
    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
        })
    }
    

    【讨论】:

    • 这很好用,但e.preventDefault() 会抛出警告,因为它是针对无源组件的。您对此有何看法?
    【解决方案2】:

    好的,所以问题似乎是您只引用函数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

    【讨论】:

    • 可能有一种更简单的方法来实现这一点,如问题所示,使用轮子事件目标,但对我来说,我测试中的这个目标是 div 的内容而不是容器。所以元素引用方法对我来说似乎更可靠。
    • 感谢您的解决方案。有用。但是,它会禁用触控板左右滚动。关于如何调整的任何建议?
    • @Zach 我从来没有用触摸板测试过它,所以不知道这个问题,谢谢你提到它!现在我也可以在我自己的项目中解决这个问题 :) 我已经用一个简单的修复更新了帖子的代码(如果它不是 y 轴滚动,就不要阻止该事件)。我认为如果我们能检测到用户自己是否有一种水平滚动的方式(例如触摸板),那就太好了,这样我们就可以防止在这种情况下做任何事情,但现在这似乎不可能。
    【解决方案3】:

    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();
            }
    

    【讨论】:

    • 很好的答案!您可以建议对 TarVK 的答案进行编辑以保持其整洁。 anw,我宁愿将此函数与组件分开,并使用ref 作为参数声明它,如下所示:const useHorizontalScroll = (ref) =&gt; {…} 并删除此行const elRef = useRef();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-25
    相关资源
    最近更新 更多