Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Android only】autoPlay does not work when scrolling up or down in a scroll view #587

Open
lchenfox opened this issue Apr 16, 2024 · 3 comments
Assignees
Labels
bug Something isn't working

Comments

@lchenfox
Copy link

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:

  1. Install and import react-native-reanimated-carousel.
  2. Wrapping Carousel in a scroll view.
  3. Sets autoPlay and loop as true.
  4. Scrolling up or down.
  5. 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>
  );
}
@lchenfox lchenfox added the bug Something isn't working label Apr 16, 2024
Copy link

dosubot bot commented Apr 16, 2024

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

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.

@minhtet-ko
Copy link

minhtet-ko commented May 10, 2024

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>
  );
}

@lchenfox
Copy link
Author

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants