Replies: 3 comments
-
I've pretty much achieved what I want to. I have one final issue currently. If the user holds on to the screen while new slides load the slideTo works on the screen but when user scrolls again the teleport to the previous index. Step by step explination of the problem
And video of the problem Screen.Recording.2024-12-10.at.18.59.29.movWhat kind of event should I be listening to act on and slide then? Currently it seems like the inner index stays the same after the slide is initiated as when I swipe the old index is used. |
Beta Was this translation helpful? Give feedback.
-
I think it's related to the unfinished touch event. And once the swipe continues it jumps back because of it. |
Beta Was this translation helpful? Give feedback.
-
I was able to overcome this "Bug". I needed to simulate the touch event. Here is my working code. import { useEffect, useState, useCallback, useMemo } from "react";
import { Swiper, SwiperSlide } from "swiper/react";
import type { Swiper as SwiperType } from "swiper";
import { Virtual } from "swiper/modules";
import "swiper/css";
import 'swiper/css/virtual';
import { addDays, eachDayOfInterval, format, parse, subDays } from "date-fns";
import { throttle } from "lodash";
const dateFormat = "dd-MM-yyyy";
const slidesPerView = 3;
const daysBefore = slidesPerView * 3;
const daysAfter = slidesPerView * 10;
const initialScrollToIndex = daysBefore + 1;
const daysToAdd = daysAfter;
const totalSlides = daysBefore + daysAfter + 2;
export default function App() {
const today = new Date();
const [swiperRef, setSwiperRef] = useState<SwiperType | null>(null);
const [lastAction, setLastAction] = useState<"append" | "prepend" | null>(null);
const [slides, setSlides] = useState<string[]>(
eachDayOfInterval({
start: subDays(today, daysBefore),
end: addDays(today, daysAfter),
}).map((date) => format(date, dateFormat)),
);
useEffect(() => {
if (!swiperRef) return;
swiperRef.update();
}, [slides]);
const append = useCallback(() => {
setLastAction("append");
setSlides((prevSlides) => {
const lastDate = parse(
prevSlides[prevSlides.length - 1],
dateFormat,
new Date(),
);
const newDates = eachDayOfInterval({
start: addDays(lastDate, 1),
end: addDays(lastDate, daysToAdd),
}).map((date) => format(date, dateFormat));
return [...prevSlides.slice(daysToAdd), ...newDates];
});
}, []);
const prepend = useCallback(() => {
setLastAction("prepend");
setSlides((prevSlides) => {
const firstDate = parse(prevSlides[0], dateFormat, new Date());
const newDates = eachDayOfInterval({
start: subDays(firstDate, daysToAdd),
end: subDays(firstDate, 1),
}).map((date) => format(date, dateFormat));
return [...newDates, ...prevSlides.slice(0, -daysToAdd)];
});
}, []);
const throttledAppend = useMemo(
() => throttle(append, 5, { leading: false, trailing: true }),
[append],
);
const throttledPrepend = useMemo(
() => throttle(prepend, 5, { leading: false, trailing: true }),
[prepend],
);
const simulateTouchEnd = useCallback((el: HTMLElement) => {
/*
* Hack to trigger touch end event which never happens if user
* scrolls to the end and does not let go before loading happens.
*/
document.dispatchEvent(new TouchEvent('touchend', {
bubbles: true,
cancelable: true,
changedTouches: [
new Touch({
identifier: 0,
target: el,
clientX: 0,
clientY: 0,
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 0,
force: 0
})
]
}));
}, []);
return (
<div className="flex w-full h-full">
<Swiper
modules={[Virtual]}
virtual={{enabled: true, addSlidesAfter: 3, addSlidesBefore: 3}}
slidesPerView={slidesPerView}
initialSlide={initialScrollToIndex}
onSwiper={(swiper) => {
setSwiperRef(swiper);
}}
onReachEnd={() => {
throttledAppend();
}}
onReachBeginning={() => {
throttledPrepend();
}}
onUpdate={
(swiper) => {
if (lastAction === "append") {
swiper.slideTo(totalSlides - daysToAdd - 3, 0, false);
} else if (lastAction === "prepend") {
swiper.slideTo(daysToAdd + 1, 0, false);
}
simulateTouchEnd(swiper.el);
}
}
className="h-full w-full"
>
<SwiperSlide key="Start" virtualIndex={0}>
<div className="text-center">Start</div>
</SwiperSlide>
{slides.map((slideContent, index) => (
<SwiperSlide key={slideContent} virtualIndex={index + 1} className="text-center">
{slideContent}
</SwiperSlide>
))}
<SwiperSlide key="End" virtualIndex={slides.length + 1}>
<div className="text-center">End</div>
</SwiperSlide>
</Swiper>
</div>
);
} |
Beta Was this translation helpful? Give feedback.
-
Hey!
I'm adding slides to one end and removing slides from the other end when user reaches either end of the Swiper. The main issue I'm facing is that I'm unable to maintain the scroll position for the user while adding or removing slides.
Example of what I want to achieve is shown here. The docs state that we should not be using Manipulation module for React. The example in React is broken and does not actually work.
Stack
React
Swiper
CodeSandbox example of the problem
https://codesandbox.io/p/sandbox/8d6cl4
How should this be done in React? Any help or ideas would be highly appreciated.
Beta Was this translation helpful? Give feedback.
All reactions