这是一个相对简单的问题,但我无法解决它。我构建了以下轮播/滑块:
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
更新时,幻灯片链接都会更新并突出显示新的当前幻灯片。
最后,您还可以通过滚动进行导航。问题是:
每当有人滚动时,我都会通过进行一些计算来检查他们在哪张幻灯片上:
onScroll={e => {
let { width } = wrapperRef.current.getBoundingClientRect();
let { scrollLeft } = wrapperRef.current;
if ((scrollLeft / width) % 1 === 0) {
setActiveSlide(scrollLeft / width);
}
}}
。这样做的结果是,只有在幻灯片完全进入视图时,才会调用setActiveSlide
。这会导致滞后体验:用户已滚动到下一张幻灯片,下一张幻灯片在视图中,但计算尚未完成(因为只有在幻灯片 100% 处于视图中时才完成(,因此活动的 SlideLink 在过程中很晚才更新。
我将如何解决这个问题?有没有办法乐观地更新这一点?
您可以稍微修改计算,以便在幻灯片在视图中超过 50% 时,将活动属性设置为该属性。
onScroll={e => {
let { width } = wrapperRef.current.getBoundingClientRect();
let { scrollLeft } = wrapperRef.current;
setActiveSlide(Math.round(scrollLeft / width) + 1);
}}
示例:宽度:800 像素的轮播,幻灯片总数:3。 因此,scrollLeft 将在 0 到 1600 之间变化
- 从 0-400:活动幻灯片 = 数学轮次(向左滚动/宽度(= 第 0 个索引 (或第一张幻灯片(
- 从 400-1200:活动幻灯片 = 第一个索引(或第二个索引( 幻灯片(,因为它在视图中更多
- 从 1200-1600:活动滑块 = 第二个索引(或第三张幻灯片(,因为它在视图中更多
希望对您有所帮助。如有疑问,请回复。
乍一看,我认为这应该是一个快速解决方案。事实证明,还需要多一点。我在这里创建一个工作示例:
https://codesandbox.io/s/dark-field-g78zb?fontsize=14&hidenavigation=1&theme=dark
重点是,setActiveSide
不应在onScroll
事件期间调用,而应在用户完成滚动时调用。这是在示例中使用onIdle
反应钩子实现的。