Жесты касания не работают в Expo Go, но функционируют на вебе: как это исправить

Вопрос или проблема

Жесты касания не работают в Expo Go, но функционируют на вебе: как это исправить

Когда я открываю приложение в вебе с помощью команды npx expo start –tunnel, я могу проводить и переворачивать карты, но это не работает в expo go. Я вижу карты, но они не двигаются. Как будто сенсорный экран вообще не работает. Как я могу это исправить?

package.json

{
  "name": "word-cards",
  "version": "1.0.0",
  "main": "expo-router/entry",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "@expo/metro-runtime": "~3.2.3",
    "@react-native-async-storage/async-storage": "1.23.1",
    "axios": "^1.7.7",
    "expo": "^51.0.0",
    "expo-constants": "~16.0.1",
    "expo-font": "^12.0.10",
    "expo-linking": "~6.3.1",
    "expo-router": "~3.5.9",
    "expo-splash-screen": "~0.27.4",
    "expo-status-bar": "~1.12.1",
    "npm-upgrade": "^3.1.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.74.5",
    "react-native-deck-swiper": "^2.0.17",
    "react-native-dotenv": "^3.4.11",
    "react-native-flip-card": "^3.5.7",
    "react-native-gesture-handler": "~2.16.1",
    "react-native-reanimated": "~3.10.1",
    "react-native-safe-area-context": "4.10.5",
    "react-native-screens": "3.31.1",
    "react-native-swipe-gestures": "^1.0.5",
    "react-native-web": "~0.19.10"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}

index.js

import React, { useState, useRef, useCallback, useEffect } from 'react';
import { View, Text, StyleSheet, Dimensions, TouchableWithoutFeedback, Animated } from 'react-native';
import Swiper from 'react-native-deck-swiper';
import AsyncStorage from '@react-native-async-storage/async-storage';

const { width, height } = Dimensions.get('window');

const flashcards = [
  { id: 1, german: 'Ja', english: 'Yes' },
  { id: 2, german: 'Nein', english: 'No' },
  { id: 3, german: 'Bitte', english: 'Please' },
 ];

const shuffleArray = (array) => {
  return array.sort(() => Math.random() - 0.5);
};

export default function App() {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [cards, setCards] = useState(shuffleArray([...flashcards])); 
  const [label, setLabel] = useState('');
  const [cardFrequency, setCardFrequency] = useState({});
  const flipAnimation = useRef(new Animated.Value(0)).current;
  const swiperRef = useRef(null);

  useEffect(() => {
    const loadCardData = async () => {
      try {
        const storedFrequency = await AsyncStorage.getItem('cardFrequency');
        if (storedFrequency) {
          setCardFrequency(JSON.parse(storedFrequency));
        }
      } catch (error) {
        console.log("Ошибка при загрузке данных карты", error);
      }
    };
    loadCardData();
  }, []);

  const flipCard = useCallback(() => {
    Animated.spring(flipAnimation, {
      toValue: flipAnimation._value === 0 ? 180 : 0,
      friction: 8,
      tension: 10,
      useNativeDriver: true,
    }).start();
  }, [flipAnimation]);

  const frontInterpolate = flipAnimation.interpolate({
    inputRange: [0, 180],
    outputRange: ['0deg', '180deg'],
  });
  const backInterpolate = flipAnimation.interpolate({
    inputRange: [0, 180],
    outputRange: ['180deg', '360deg'],
  });

  const frontAnimatedStyle = {
    transform: [{ rotateY: frontInterpolate }],
  };
  const backAnimatedStyle = {
    transform: [{ rotateY: backInterpolate }],
  };

  const updateCardOrder = () => {
    const newCards = [...flashcards].sort((a, b) => {
      const freqA = cardFrequency[a.id] || 1; 
      const freqB = cardFrequency[b.id] || 1;
      return Math.random() * (1 / freqA) - Math.random() * (1 / freqB);
    });
    setCards(newCards);
  };

  const handleSwiping = useCallback((x) => {
    if (x > 0) {
      setLabel('TRUE');
    } else if (x < 0) {
      setLabel('FALSE');
    } else {
      setLabel('');
    }
  }, []);

  const handleSwiped = useCallback((cardIndex) => {
    const card = cards[cardIndex % cards.length];
    const newFrequency = { ...cardFrequency };

    if (label === 'TRUE') {
      newFrequency[card.id] = (newFrequency[card.id] || 1) * 0.5; 
    } else if (label === 'FALSE') {
      newFrequency[card.id] = (newFrequency[card.id] || 1) * 2; 
    }

    setCardFrequency(newFrequency);
    AsyncStorage.setItem('cardFrequency', JSON.stringify(newFrequency)); 
    setCurrentIndex((prevIndex) => (prevIndex + 1) % flashcards.length);
    setLabel('');
    flipAnimation.setValue(0);
    updateCardOrder();
  }, [cardFrequency, cards, label, flipAnimation]);

  const handleSwipedAll = useCallback(() => {
    if (swiperRef.current) {
      swiperRef.current.jumpToCardIndex(0);
    }
    setCurrentIndex(0);
    flipAnimation.setValue(0);
    setLabel('');
  }, []);

  return (
    <View style={styles.container}>
      <Swiper
        ref={swiperRef}
        cards={cards}
        renderCard={(card) => (
          <TouchableWithoutFeedback onPress={flipCard}>
            <View style={styles.cardContainer}>
              <Animated.View style={[styles.card, frontAnimatedStyle]}>
                <Text style={styles.text}>{card.german}</Text>
              </Animated.View>
              <Animated.View style={[styles.card, styles.cardBack, backAnimatedStyle]}>
                <Text style={styles.text}>{card.turkish}</Text>
              </Animated.View>
              {label !== '' && (
                <View style={label === 'TRUE' ? styles.correctLabel : styles.wrongLabel}>
                  <Text style={styles.labelText}>{label}</Text>
                </View>
              )}
            </View>
          </TouchableWithoutFeedback>
        )}
        onSwiped={handleSwiped}
        onSwiping={handleSwiping}
        cardIndex={currentIndex}
        backgroundColor={'#F5FCFF'}
        stackSize={2}
        showSecondCard={false}
        animateCardOpacity
        swipeBackCard
        containerStyle={styles.swiperContainer}
        cardVerticalMargin={80}
        onSwipedAll={handleSwipedAll}
        infinite
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
    justifyContent: 'center',
    alignItems: 'center',
  },
  swiperContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  cardContainer: {
    width: width * 0.8,
    height: height * 0.6,
  },
  card: {
    width: '100%',
    height: '100%',
    borderRadius: 8,
    borderWidth: 2,
    borderColor: '#E8E8E8',
    justifyContent: 'center',
    backgroundColor: 'white',
    backfaceVisibility: 'hidden',
    shadowColor: "#000",
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.25,
    shadowRadius: 3.84,
    elevation: 5,
  },
  cardBack: {
    position: 'absolute',
    top: 0,
  },
  text: {
    textAlign: 'center',
    fontSize: 24,
    fontWeight: 'bold',
  },
  correctLabel: {
    position: 'absolute',
    top: 20,
    left: 20,
    backgroundColor: 'green',
    padding: 10,
    borderRadius: 5,
  },
  wrongLabel: {
    position: 'absolute',
    top: 20,
    right: 20,
    backgroundColor: 'red',
    padding: 10,
    borderRadius: 5,
  },
  labelText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Думая, что это может быть проблема с моим телефоном, я хотел запустить его с Expo Snack, но не смог попробовать, потому что не смог установить пакеты.

Ответ или решение

Чтобы решить проблему с отсутствием сенсорных жестов в Expo Go, которые нормально работают на веб-версии вашего приложения, выполните следующие рекомендации:

1. Убедитесь, что библиотеки правильно установлены

Проверьте, что у вас установлены все необходимые зависимости для работы жестов. В вашем package.json уже присутствует react-native-gesture-handler и react-native-reanimated, однако важно их правильно подключить и использовать.

2. Подключите Gesture Handler

Убедитесь, что вы импортируете и обернули ваше приложение в GestureHandlerRootView. Это критически важно для работы жестов на мобильных устройствах. В index.js сделайте следующее:

import 'react-native-gesture-handler'; // Импортируйте в самом начале
import { GestureHandlerRootView } from 'react-native-gesture-handler'; // Импортируйте GestureHandlerRootView

export default function App() {
  // ваш код...

  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      <View style={styles.container}>
        {/* Вставьте ваш компонент Swiper тут */}
      </View>
    </GestureHandlerRootView>
  );
}

3. Проверьте конфигурации анимации

Убедитесь, что вы используете библиотеку react-native-reanimated корректно. Некоторые версии могут требовать специальные настройки. Например, если используете react-native-reanimated версии 2 и выше, добавьте следующее в вашу babel.config.js:

module.exports = {
  presets: ['babel-preset-expo'],
  plugins: ['react-native-reanimated/plugin'],
};

4. Используйте useNativeDriver

Поскольку вы используете анимацию в своем приложении, убедитесь, что вы используете useNativeDriver: true в Animated.spring(). Это позволит анимациям работать более плавно и эффективно:

Animated.spring(flipAnimation, {
  toValue: flipAnimation._value === 0 ? 180 : 0,
  friction: 8,
  tension: 10,
  useNativeDriver: true, // уже установлено правильно
}).start();

5. Изменение на Expo Go

Иногда проблемы могут быть вызваны кэшированием в Expo Go. Попробуйте обновить Expo Go на вашем устройстве и сбросить кэш. Вы можете сделать это следующим образом:

  • Убедитесь, что у вас последняя версия Expo Go.
  • Запустите команду npx expo start -c для сброса кэша.

6. Установите чёткие размеры для компонентов

Для корректной работы свайпов иногда необходимо установить четкие размеры для карточек. Вы можете проверить стили вашего компонента в методе renderCard. Убедитесь, что ширина и высота карточек чётко установлены.

7. Проверьте устройство

Если проблема сохраняется, воспользуйтесь другим устройством, чтобы исключить возможность аппаратного сбоя.

Заключение

Следуя этим рекомендациям, вы сможете исправить проблему с жестами в вашем приложении Expo Go. Если после выполнения всех шагов проблема не решится, попробуйте создать новый проект по примеру вашего и постепенно добавлять функциональность, чтобы изолировать источник проблемы.

Оцените материал
Добавить комментарий

Капча загружается...