屏幕外水平滚动的顺风问题(溢出)

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

我有一个使用 Tailwind CSS 的基于 React 的 NextJS 应用程序。有一个页面组件显示从 A 到 Ö(瑞典语 aplhabet)排序的人员列表。使用移动视图时,用户体验规定非字母字母列表应在单行上可见,并且可以使用箭头或手指在屏幕上进行缩放。

问题:在移动视图中,列表从字母 J 开始,而不是字母 A。可能是因为它跨越或溢出屏幕。用户无法滚动到字母 A(见图)。

问题:如何使字母表适合滚动距离,如何让它进一步滚动回到第一个字母,或者更好的是,让它从字母 A 开始?

我当前的 LetterMeny 组件代码。

'use client';

import Link from 'next/link';
import React, { useEffect, useRef, useState } from 'react';
import { cn } from '@/utils/classnames';
import NavigateIcon from './icons/NavigateIcon';

const Letters = [
  'a',
  'b',
  'c',
  'd',
  'e',
  'f',
  'g',
  'h',
  'i',
  'j',
  'k',
  'l',
  'm',
  'n',
  'o',
  'p',
  'q',
  'r',
  's',
  't',
  'u',
  'v',
  'w',
  'x',
  'y',
  'z',
  'å',
  'ä',
  'ö',
];

type Props = {
  currentElementIndexInViewport: number;
  activeLetters: string[];
};

const LetterMenu: React.FC<Props> = ({ currentElementIndexInViewport, activeLetters }) => {
  const carousel = useRef<HTMLUListElement>(null);
  const letterMenuRef = useRef<HTMLDivElement>(null);
  const [isSticky, setIsSticky] = useState(false);
  const [isAtStart, setIsAtStart] = useState(true);
  const [isAtEnd, setIsAtEnd] = useState(false);

  const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
    event.preventDefault();
    const targetId = event.currentTarget.getAttribute('href')?.substring(1);
    const targetElement = targetId ? document.getElementById(targetId) : null;

    if (targetElement) {
      targetElement.scrollIntoView({
        behavior: 'smooth',
      });
    }
  };

  const updateButtonVisibility = () => {
    if (carousel.current) {
      const isStart = carousel.current.scrollLeft === 0;
      const isEnd =
        carousel.current.scrollLeft + carousel.current.offsetWidth === carousel.current.scrollWidth;
      setIsAtStart(isStart);
      setIsAtEnd(isEnd);
    }
  };

  const handleClickNext = () => {
    carousel.current?.scrollBy(300, 0);
    updateButtonVisibility();
  };

  const handleClickPrev = () => {
    carousel.current?.scrollBy(-300, 0);
    updateButtonVisibility();
  };

  useEffect(() => {
    updateButtonVisibility(); // Kollar startposition
    const handleScroll = () => {
      if (letterMenuRef.current) {
        setIsSticky(window.scrollY >= letterMenuRef.current.offsetTop);
      }
    };

    const handleCarouselScroll = () => {
      updateButtonVisibility();
    };

    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
  }, []);

  return (
    <div
      ref={letterMenuRef}
      className={cn('sticky top-0 z-10 w-screen bg-white', {
        'shadow-custom': isSticky,
      })}
    >
      {!isAtStart && (
        <button
          className="prev space-between absolute left-0 top-1/2 -translate-y-1/2 cursor-pointer content-center rounded-full px-2"
          onClick={handleClickPrev}
          aria-label="vänster"
        >
          <div className="inline-flex items-center justify-center rounded-full border border-transparent bg-black bg-opacity-20 px-4 shadow-lg">
            <NavigateIcon direction="left" variant="secondary" />
          </div>
        </button>
      )}
      {/* Vi har här valt att ha skräddarsydd inline-styling i CSS då det endast gäller denna komponenten */}
      <ul
        ref={carousel}
        className="carousel flex justify-center gap-[0.624rem] overflow-x-auto scroll-smooth py-4"
      >
        {Letters.filter((letter) => activeLetters.includes(letter)).map((letter, letterIndex) => (
          <li key={`nav_${letter}`}>
            <Link
              href={`#${letter}`}
              onClick={handleClick}
              className={`focus:ring-blue-500 rounded-full font-livvic text-xl font-bold uppercase underline focus:outline-none focus:ring-2 focus:ring-greenblue-primary sm:p-4 ${
                activeLetters[currentElementIndexInViewport] === letter
                  ? 'active bg-greenblue-1.45 p-2 no-underline'
                  : ''
              }`}
              style={{
                width: '2.1rem',
                height: '2.1rem',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
              }}
              tabIndex={0}
            >
              {letter}
            </Link>
          </li>
        ))}
      </ul>

      {!isAtEnd && (
        <button
          className="next absolute right-0 top-1/2 -translate-y-1/2 cursor-pointer content-center justify-center rounded-full px-4 shadow"
          onClick={handleClickNext}
          aria-label="höger"
        >
          <NavigateIcon direction="right" variant="secondary" />
        </button>
      )}
    </div>
  );
};

export default LetterMenu;
javascript reactjs next.js tailwind-css
1个回答
0
投票

您面临的问题可能是由于可滚动列表的初始滚动位置造成的。当组件安装时,它可能不会一直滚动到开始处。

为了确保列表从第一个字母开始,您可以在组件安装时以编程方式将列表滚动到开头。您可以通过在组件安装上运行的 useEffect 挂钩中添加一行代码来完成此操作。

试试这个:

useEffect(() => {
    // Scroll to start when component mounts
    if (carousel.current) {
        carousel.current.scrollLeft = 0;
    }

    updateButtonVisibility(); // Check start position
    const handleScroll = () => {
      if (letterMenuRef.current) {
        setIsSticky(window.scrollY >= letterMenuRef.current.offsetTop);
      }
    };

    const handleCarouselScroll = () => {
      updateButtonVisibility();
    };

    window.addEventListener('scroll', handleScroll);

    return () => {
      window.removeEventListener('scroll', handleScroll);
    };
}, []);```
© www.soinside.com 2019 - 2024. All rights reserved.