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

问题描述 投票:0回答:6

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

这是我的代码基于的原始 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
6个回答
15
投票

好的,所以问题似乎是您只 refer 函数

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


10
投票
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
    })
}

7
投票

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

4
投票

我不能评论,因为我的声望不够

@arVK 的回答 有效,但使用 'scrollBy' 而不是 'scrollTo' 可以获得平滑的轮子。

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.scrollBy(e.deltaY, 0);
      };
      el.addEventListener("wheel", onWheel);
      return () => el.removeEventListener("wheel", onWheel);
    }
  }, []);
  return elRef;
}

0
投票

您可以直接使用

onWheel
活动:

import React, { useRef } from "react";
export const Content = () => {
  const ref = useRef<HTMLDivElement>(null);

  const onWheel = (e: UIEvent) => {
    const elelemnt = ref.current;
    if (elelemnt) {
      if (e.deltaY == 0) return;
      elelemnt.scrollTo({
        left: elelemnt.scrollLeft + e.deltaY,
      });
    }
  };
  return (
    <div ref={ref} onWheel={onWheel}>
          TEST CONTENT
    </div>
  );
};

0
投票

我使用了@TarVK 提供的答案,效果很好。只是有一个问题:当输入来自触控板时,滚动变得非常不稳定。我的解决方案是这样的:

import { useEffect, useRef } from 'react';

export function useHorizontalScroll() {
    const elRef = useRef();

    useEffect(() => {
        const el = elRef.current;
        if (el) {
            const onWheel = (e: WheelEvent) => {
                if (e.deltaY === 0) {
                    return;
                }
                if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) {
                    e.preventDefault();
                    el.scrollLeft += e.deltaX;
                }
                el.scrollTo({ left: el.scrollLeft + e.deltaY });
            };
            el.addEventListener('wheel', onWheel);
            return () => el.removeEventListener('wheel', onWheel);
        }
    }, []);
    return elRef;
}

此更新的实现检查水平滚动距离 (

e.deltaX
) 是否大于垂直滚动距离 (
e.deltaY
) 并且仅防止默认行为并在前者更大时水平滚动。

© www.soinside.com 2019 - 2024. All rights reserved.