React native Reanimated Conditional Animated View Movement
Posted
技术标签:
【中文标题】React native Reanimated Conditional Animated View Movement【英文标题】: 【发布时间】:2021-11-23 18:27:36 【问题描述】:我对@987654322@ 真的很陌生。我正在尝试创建一个像 app 这样的自定义底页。我正在使用react-native-gesture-handler
中的PanGestureHandler
来移动动画视图上下移动。对于gestureHandler
,我使用来自react-native-reanimated
的useAnimatedGestureHandler
道具。我想将动画视图从起点移动到屏幕中间和屏幕底部。这是我的底部工作表起点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 |
【问题讨论】:
【参考方案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;
,
还有改进的余地。
【讨论】:
很遗憾,这不是一个好的解决方案。以上是关于React native Reanimated Conditional Animated View Movement的主要内容,如果未能解决你的问题,请参考以下文章
构建失败'配置项目':react-native-reanimated'时出现问题。在 React 原生项目中
React-native-reanimated:无法解析“./useValue”