这是一个相对简单的问题,但我无法解决。我建立了以下轮播/滑块:
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在此过程中更新得非常晚。
我该如何解决?有什么方法可以乐观地更新吗?
乍看之下,我认为这应该是一个快速解决方案。原来还需要一点。我在这里创建一个工作示例:
https://codesandbox.io/s/dark-field-g78zb?fontsize=14&hidenavigation=1&theme=dark
主要要点是,不应在setActiveSide
事件期间调用onScroll
,而应在用户完成滚动时调用。在示例中,这是使用onIdle
react挂钩实现的。
您可以稍微修改计算,以便在视图中幻灯片超过50%时,将active属性设置为该属性。
onScroll={e => {
let { width } = wrapperRef.current.getBoundingClientRect();
let { scrollLeft } = wrapperRef.current;
setActiveSlide(Math.round(scrollLeft / width) + 1);
}}
示例:宽度为800像素的旋转木马,幻灯片总数为3。因此,scrollLeft的范围为0到1600]
希望有帮助。如有任何疑问,请回复。