【问题标题】:React native Reanimated Conditional Animated View MovementReact native Reanimated Conditional Animated View Movement
【发布时间】:2021-11-23 18:27:36
【问题描述】:

我对@9​​87654322@ 真的很陌生。我正在尝试创建一个像 app 这样的自定义底页。我正在使用react-native-gesture-handler 中的PanGestureHandler 来移动动画视图上下移动。对于gestureHandler,我使用来自react-native-reanimateduseAnimatedGestureHandler 道具。我想将动画视图从起点移动到屏幕中间和屏幕底部。这是我的底部工作表起点image,当向下滚动卡片时,它应该像这样image 出现在屏幕中间,再次向下滚动它会像这样image 那样底部。

我在使用有条件的useAnimatedGestureHandler onEnd 运动时遇到了困难。目前我正在跟踪 onEnd's event.translationY 并以此为条件。

这是目前的工作方式:

当应用程序启动时,动画视图位于屏幕顶部,如果我将卡片滚动移动到底部,它会转到屏幕中间并且它不会从屏幕中间向下,我可以将它从屏幕中间向上移动屏幕中间,或者如果我用力滚动到底部,它会一直到底部,如果我尝试向上滚动视图,它不会进入中间,它只会向上启动视图。

我正在尝试制作基于条件的屏幕尺寸,但 I don't know how to make it.

我在expo-snacks分享了我的代码

这是我的全部代码

import React, { useState, useEffect } from "react";
import { StyleSheet, useWindowDimensions, RefreshControl } from "react-native";
import MapView from "react-native-maps";
import styled from "styled-components";

import {
  PanGestureHandler,
  PanGestureHandlerGestureEvent,
  FlatList,
} from "react-native-gesture-handler";

import Animated, {
  useAnimatedGestureHandler,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
  Easing,
  withSpring,
} from "react-native-reanimated";

const initialRegion = {
  latitudeDelta: 15,
  longitudeDelta: 15,
  latitude: 60.1098678,
  longitude: 24.7385084,
};

const api =
  "http://open-api.myhelsinki.fi/v1/events/?distance_filter=60.1699%2C24.9384%2C10&language_filter=en&limit=50";

export default function App() {
  const { height } = useWindowDimensions();
  const top = useSharedValue(height);
  const [event, setEvent] = useState([]);
  const [loading, setLoading] = useState(false);
  const prevTop = useSharedValue(height * 0.5);
  // This is Fetch Data
  const fetchData = async () => {
    try {
      setLoading(true);
      const response = await fetch(api);
      const data = await response.json();
      setEvent(data.data);
      setLoading(false);
    } catch (error) {
      console.log("erro", error);
    }
  };

  useEffect(() => {
    fetchData();
  }, []);

  const animatedStyle = useAnimatedStyle(() => {
    return {
      top: top.value * 0.2,
      bottom: 0,
    };
  });

  const gestureHandler = useAnimatedGestureHandler(
    {
      onStart(_, context) {
        context.translateY = top.value;
      },
      onActive(event, context) {
        top.value = context.translateY + event.translationY;
      },
      onEnd(event, _) {
        // THIS IS MY CONDITION OF ANIMATED VIEW
        if (event.translationY > 0 && event.translationY < 400) {
          console.log("middle-top", top.value);
          console.log("middle-height", height);

          top.value = withSpring(height * 2.5, {
            duration: 500,
            easing: Easing.inOut(Easing.ease),
          });
        } else if (event.translationY > 450 && event.translationY < 800) {
          console.log("bottom-top", top.value);
          console.log("bottom-height", height);
          top.value = withSpring(height * 4, {
            duration: 500,
            easing: Easing.inOut(Easing.ease),
          });
        } else if (event.translationY < 0) {
          console.log("start-top", top.value);
          console.log("start-height", height);
          top.value = withSpring(height, {
            duration: 500,
            easing: Easing.inOut(Easing.ease),
          });
        }
      },
    },
    [top]
  );

  return (
    <>
      <MapView style={styles.mapStyle} initialRegion={initialRegion} />
      <PanGestureHandler onGestureEvent={gestureHandler}>
        <Animated.View style={[styles.container, animatedStyle]}>
          <Title>I am scroll sheet</Title>
          <HeroFlatList
            data={event}
            refreshControl={
              <RefreshControl
                enabled={true}
                refreshing={loading}
                onRefresh={fetchData}
              />
            }
            keyExtractor={(_, index) => index.toString()}
            renderItem={({ item, index }) => {
              const image = item?.description.images.map((img) => img.url);
              const startDate = item?.event_dates?.starting_day;
              return (
                <EventContainer key={index}>
                  <EventImage
                    source={{
                      uri:
                        image[0] ||
                        "https://res.cloudinary.com/drewzxzgc/image/upload/v1631085536/zma1beozwbdc8zqwfhdu.jpg",
                    }}
                  />
                  <DescriptionContainer>
                    <Title ellipsizeMode="tail" numberOfLines={1}>
                      {item?.name?.en}
                    </Title>
                    <DescriptionText>
                      {item?.description?.intro || "No description available"}
                    </DescriptionText>
                    <DateText>{startDate}</DateText>
                  </DescriptionContainer>
                </EventContainer>
              );
            }}
          />
        </Animated.View>
      </PanGestureHandler>
    </>
  );
}

const styles = StyleSheet.create({
  container: {
    position: "absolute",
    left: 0,
    right: 0,
    top: 0,

    backgroundColor: "white",
    shadowOffset: {
      height: -6,
      width: 0,
    },
    shadowOpacity: 0.1,
    shadowRadius: 5,
    borderTopEndRadius: 15,
    borderTopLeftRadius: 15,
  },
  mapStyle: {
    flex: 1,
  },
});

const HeroFlatList = styled(FlatList).attrs({
  contentContainerStyle: {
    flexGrow: 1,
  },
})`
  padding: 12px;
`;

const Title = styled.Text`
  font-size: 16px;
  font-weight: 700;
  margin-bottom: 10px;
  align-self: center;
  padding: 10px;
`;

const DescriptionText = styled.Text`
  font-size: 14px;
  opacity: 0.7;
`;

const DateText = styled.Text`
  font-size: 14px;
  opacity: 0.8;
  color: #0099cc;
`;

const EventImage = styled.Image`
  width: 70px;
  height: 70px;
  border-radius: 70px;
  margin-right: 20px;
`;

const DescriptionContainer = styled.View`
  width: 200px;
`;

const EventContainer = styled(Animated.View)`
  flex-direction: row;
  padding: 20px;
  margin-bottom: 10px;
  border-radius: 20px;
  background-color: #fff;
  shadow-color: #000;
  shadow-opacity: 0.3;
  shadow-radius: 20px;
  shadow-offset: 0 10px;
`;

技术信息

Tech Version
react-native-gesture-handler ^1.10.3
react-native-reanimated ^2.2.0

【问题讨论】:

    标签: react-native animation translate react-native-gesture-handler react-native-reanimated-v2


    【解决方案1】:

    不是完美的解决方案... 添加了一个新的 sharedValue 来跟踪它是向上还是向下移动。

    const prevTop = useSharedValue(height * 0.5);
    

    以及手势结束时的相应代码。

          onEnd() {
            if (top.value > prevTop.value) {
              top.value = withTiming(height * 0.98);
            } else {
              top.value = withTiming(Math.min(200, top.value));
            }
            prevTop.value = top.value;
          },
    

    还有改进的余地。

    【讨论】:

    • 很遗憾,这不是一个好的解决方案。
    猜你喜欢
    • 2022-11-17
    • 2022-01-11
    • 2021-11-08
    • 2021-11-05
    • 2022-08-14
    • 2021-01-05
    • 2022-11-30
    • 1970-01-01
    • 2020-10-12
    相关资源
    最近更新 更多