【Android only】autoPlay does not work when scrolling up or down in a scroll view
lchenfox opened this issue · comments
Describe the bug
As described above, autoPlay
settings does not work when scrolling up or down with touches on carousel
in a scroll view on Android. It works well on iOS.
To Reproduce
Steps to reproduce the behavior:
- Install and import
react-native-reanimated-carousel
. - Wrapping
Carousel
in a scroll view. - Sets
autoPlay
andloop
astrue
. - Scrolling up or down.
- The
autoPlay
does not work on android, in other words, the autoPlay stops.
Expected behavior
autoPlay
works well on Android even if when scrolling up or down.
Screenshots
12_1713234373.mp4
Versions (please complete the following information):
- react: v18.2.0
- react-native: v0.73.4
- react-native-reanimated: v3.7.2
- react-native-reanimated-carousel: v3.5.1
- react-native-gesture-handler: v2.16.0
Smartphone (please complete the following information):
- Device: [Galaxy A52 5G] and other android devices.
- OS: [e.g. Android]
- Browser [--]
- Version [Android 12]
Additional context
Codes
import { Dimensions, ScrollView, Image, View } from "react-native";
import Carousel from "react-native-reanimated-carousel";
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function App(props) {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<ScrollView>
<View style={{ height: 500, backgroundColor: "blue" }} />
<Carousel
data={[
"https://picnew13.photophoto.cn/20190309/chuntianlvsedianshanghaibaobeijingbanner-32416436_1.jpg",
"https://img1.baidu.com/it/u=3661953276,17905528&fm=253&fmt=auto&app=138&f=JPEG?w=768&h=260",
]}
width={Dimensions.get("window").width}
height={250}
loop={true}
autoPlay={true}
panGestureHandlerProps={{
activeOffsetX: [-10, 10],
failOffsetY: [-20, 20],
}}
renderItem={({ item }) => {
return (
<View>
<Image style={{ width: "100%", height: "100%" }} source={{ uri: item }} />
</View>
);
}}
/>
<View style={{ height: 200, backgroundColor: "red" }} />
</ScrollView>
</GestureHandlerRootView>
);
}
The issue you're experiencing with autoPlay
not working on Android when used within a ScrollView is a known problem. A common workaround is to ensure the entire app is wrapped in GestureHandlerRootView
from react-native-gesture-handler
. This adjustment has helped others with similar issues:
import { Dimensions, ScrollView, Image, View } from "react-native";
import Carousel from "react-native-reanimated-carousel";
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function App(props) {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<ScrollView>
...
<Carousel
...
/>
...
</ScrollView>
</GestureHandlerRootView>
);
}
This solution is based on feedback from similar issues reported in the react-native-reanimated-carousel
repository. If this doesn't resolve the problem, consider manually controlling the autoPlay
feature based on the ScrollView's scroll activity. This involves pausing autoPlay
when scrolling starts and resuming it once scrolling stops. You can refer to the useAutoPlay
hook in the carousel's source code for guidance on implementing this behavior.
Try these approaches and see if they address the issue. If you encounter further difficulties or need more assistance, feel free to ask.
Sources
- Jerk while scrolling the carousel.
- The stack card carousel stops while scrolling
- Carousel in ScrollView layout
- Cannot Scroll on Android
- Hi
- src/hooks/useAutoPlay.ts
- example/app/src/pages/fold/index.tsx
About Dosu
This response is meant to be useful and save you time. It is not meant to be a precise solution, but rather a starting point for your own research.
Help me be more useful! Please leave a 👍 if this is helpful and 👎 if it is irrelevant.
If you want to continue the conversation, start your reply with @dosu-bot.
I also faced this issue.
My Carousel Component is inside Scrollview. (BannerImageSlider Component)
Problem 1 -> This slider stop between 2 items when scrolling initialize horizontally with a little bit of vertical scroll.
Problem 2 -> After fixed Problem 1, another problem is auto play doesn't work if pan gesture of slider ended by cancel ( onCancelled props of panGestureHandlerProps)
Both Problem 1 and Problem 2 are solved.
This solution may help you :)
Problem 1
0-02-06-5535f28b5be5c3acea3512ec1d9032cca6ac51130d7cddc8677ef61d93bd5e6d_e9a927c5dfd77faa.mp4
Problem 2
0-02-06-f8ea40f6fedfb564b27af64ea9dc3d1a61f4329d944f9dfdef3bcc7b19d1097f_95ce870f92a1f72a.mp4
After fixed Problem 1 and Problem 2
0-02-06-e25c015139d625e677376a488f34c1cfd1467a69f876ce11f386cf055b5ee64d_4189b6d298690931.mp4
// Solution
import React, { useRef, useState } from "react";
import { Dimensions, View } from "react-native";
import { Text } from "react-native-paper";
import Carousel from "react-native-reanimated-carousel";
export default function BannerImageSlider({
leftSpacing = 0,
rightSpacing = 0,
}) {
const width = Dimensions.get("window").width;
const containerWidth = width - leftSpacing - rightSpacing;
const sliderRef = useRef(null);
const absoluteProgressRef = useRef(0);
const isScrolling = useRef(false);
const [isFailedGesture, setIsFailedGesture] = useState(false);
return (
<View style={{ alignItems: "center" }}>
<Carousel
loop
ref={sliderRef}
width={containerWidth}
height={containerWidth / 2}
// Manually control autoPlay based on isFailedGesture (state)
autoPlay={!isFailedGesture}
data={[...Array(5)]}
scrollAnimationDuration={300}
autoPlayInterval={1000}
onSnapToItem={(index) => {}}
windowSize={20}
onProgressChange={(offsetProgress, absoluteProgress) => {
absoluteProgressRef.current = absoluteProgress;
}}
panGestureHandlerProps={{
activeOffsetX: [-10, 10],
// Fixed Problem 1 by comparing absoluteProgress (Ref) and
// currentIndex then manually scroll item
onCancelled: (event) => {
const currentIndex = sliderRef.current.getCurrentIndex();
const absoluteProgress = absoluteProgressRef.current;
if (absoluteProgress > currentIndex) {
sliderRef.current.next();
} else {
sliderRef.current.prev();
}
setIsFailedGesture(true);
},
}}
// Fixed Problem 2 by setting isFailedGesture (state) on scroll begin
// and on scroll end then manually control autoPlay value based on
// this state
onScrollBegin={() => setIsFailedGesture(false)}
onScrollEnd={() => setIsFailedGesture(false)}
snapEnabled={false}
pagingEnabled={true}
renderItem={({ item, index }) => (
<View
style={{
flex: 1,
justifyContent: "center",
}}
>
<Text variant="titleLarge" style={{ textAlign: "center" }}>
{index}
</Text>
</View>
)}
/>
</View>
);
}
@minhtet-ko Thank you for your answer. However, although the autoPlay
works after using your solution. But it introduces another problem that each time I put my finger on it and drag it, sliderRef.current.next()
is always triggered first. The expected result should be that the sliderRef.current.next()
and setIsFailedGesture(true)
are called after releasing my finger(Gesture). Anyway, thanks again.