React中的简单轮播-如何注册滚动到下一张幻灯片?

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

这是一个相对简单的问题,但我无法解决。我建立了以下轮播/滑块:

const BlogPostCardSlider = ({ children }) => {
  const [activeSlide, setActiveSlide] = useState(0);
  const activeSlideRef = useRef(null);
  const wrapperRef = useRef(null);
  const firstRenderRef = useRef(true);

  useEffect(() => {
    if (firstRenderRef.current) {
      //this is checking whether its the first render of the component. If it is, we dont want useEffect to run.
      firstRenderRef.current = false;
    } else if (activeSlideRef.current) {
      activeSlideRef.current.scrollIntoView({
        behavior: 'smooth',
        block: 'nearest',
        inline: 'nearest'
      });
    }
  }, [activeSlide]);

  const moveRight = () => {
    if (activeSlide + 1 >= children.length) {
      return children.length - 1;
    }
    return activeSlide + 1;
  };

  const moveLeft = () => {
    if (activeSlide - 1 <= 0) {
      return 0;
    }
    return activeSlide - 1;
  };

  return (
    <div id="trap" tabIndex="0">
      <button onClick={() => setActiveSlide(moveLeft)}>PREV</button>

      {children.map((child, i) => {
        return (
          <SlideLink
            key={`slideLink-${i}`}
            isActive={activeSlide === i}
            onClick={e => {
              setActiveSlide(i);
            }}
          >
            {i + 1}
          </SlideLink>
        );
      })}

      <Wrapper
        onScroll={e => {
          let { width } = wrapperRef.current.getBoundingClientRect();
          let { scrollLeft } = wrapperRef.current;

          if ((scrollLeft / width) % 1 === 0) {
            setActiveSlide(scrollLeft / width);
          }
        }}
        ref={wrapperRef}
      >
        {children.map((child, i) => {
          return (
            <Slide
              key={`slide-${i}`}
              ref={i === activeSlide ? activeSlideRef : null}
            >
              {child}
            </Slide>
          );
        })}
      </Wrapper>

      <button onClick={() => setActiveSlide(moveRight)}>NEXT</button>
    </div>
  );
};

export default BlogPostCardSlider;

它在轮播中将其子项显示为幻灯片。您可以通过按“下一步”或“上一步”按钮浏览轮播。还有一个组件可以显示正在播放的幻灯片(例如:1 2 *3* 4等)。我称这些为SlideLink(s)。每当activeSlide更新时,SlideLink都会更新并突出显示新的当前幻灯片。

最后,您也可以滚动浏览。 这里是问题

[每当有人滚动时,我都会通过进行一些计算来检查他们正在滑动的幻灯片:

onScroll={e => {
          let { width } = wrapperRef.current.getBoundingClientRect();
          let { scrollLeft } = wrapperRef.current;

          if ((scrollLeft / width) % 1 === 0) {
            setActiveSlide(scrollLeft / width);
          }
        }}

...的结果是,setActiveSlide 仅在幻灯片完全进入视图后才被调用。这会带来缓慢的体验:用户滚动到下一张幻灯片,在下一张幻灯片在视图中,但是计算尚未完成(因为只有在视图中100%看到幻灯片后才完成计算),因此活动的SlideLink在此过程中更新得非常晚。

我该如何解决?有什么方法可以乐观地更新吗?

javascript css reactjs carousel react-hooks
2个回答
0
投票

乍看之下,我认为这应该是一个快速解决方案。原来还需要一点。我在这里创建一个工作示例:

https://codesandbox.io/s/dark-field-g78zb?fontsize=14&hidenavigation=1&theme=dark

主要要点是,不应在setActiveSide事件期间调用onScroll,而应在用户完成滚动时调用。在示例中,这是使用onIdle react挂钩实现的。


0
投票

您可以稍微修改计算,以便在视图中幻灯片超过50%时,将active属性设置为该属性。

onScroll={e => {
    let { width } = wrapperRef.current.getBoundingClientRect();
    let { scrollLeft } = wrapperRef.current;
    setActiveSlide(Math.round(scrollLeft / width) + 1);
}}

示例:宽度为800像素的旋转木马,幻灯片总数为3。因此,scrollLeft的范围为0到1600]

  • 从0-400:活动幻灯片= Math.round(scrollLeft / width)=第0个索引(或第一张幻灯片)
  • 从400-1200:有效幻灯片=第一索引(或第二索引)幻灯片),因为它在视图中更多]
  • [从1200-1600年:有效幻灯片=第二索引(或第三张幻灯片),因为它在视图中更多
  • 希望有帮助。如有任何疑问,请回复。

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