React Native - Animated View is shaking on Android - react-native

I'm trying to create a Animated header similar to this one:
https://raw.githubusercontent.com/maphongba008/react-native-animated-header/master/demo/android-gif.gif
After accomplish this, I realise that works fine on IOS but on Android, when I scroll the header and the body start shaking
https://user-images.githubusercontent.com/1559822/29156651-a664489e-7dc0-11e7-93f8-eb9878b47924.gif
Here is the code:
import React, { Component } from "react";
import { Animated } from "react-native";
import { query } from "src/api";
import PostList from "src/components/PostList/PostList";
import ContentSwitcher from "src/components/ContentSwitcher/ContentSwitcher";
import User from "src/components/User/User";
import {
HeaderCardHeight,
AnnouncementCardHeight
} from "src/screens/ScreenDimensions";
class Explore extends Component {
constructor(props) {
super();
this.HEADER_MAX_HEIGHT = HeaderCardHeight;
this.HEADER_MIN_HEIGHT = 120;
this.HEADER_SCROLL_DISTANCE =
this.HEADER_MAX_HEIGHT - this.HEADER_MIN_HEIGHT;
this.state = {
scrollY: new Animated.Value(0),
selectedType: "news"
};
this.headerHeight = this.state.scrollY.interpolate({
inputRange: [0, this.HEADER_SCROLL_DISTANCE],
outputRange: [this.HEADER_MAX_HEIGHT, this.HEADER_MIN_HEIGHT],
extrapolate: "clamp"
});
this.queries = {
news: query.newsList(props.user),
events: query.eventsList(props.user)
};
}
render() {
const { selectedType, scrollY } = this.state;
return (
<>
<Animated.View
style={{
height: this.headerHeight
}}
>
<ContentSwitcher
type={selectedType}
contentSelector={a => this.setState({ selectedType: a })}
/>
</Animated.View>
<PostList
style={{ position: "absolute", top: 0 }}
margin={this.headerHeight}
scrollEventThrottle={0}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: scrollY } } }
])}
layout={selectedType}
query={this.queries[selectedType]}
/>
</>
);
}
}
export default User(Explore);
Any idea to solve this issue?
Thank you

This is caused by the bouncing effect.
You can try to add:
alwaysBounceVertical={false}
bounces={false}
<PostList
style={{ position: "absolute", top: 0 }}
margin={this.headerHeight}
scrollEventThrottle={0}
onScroll={Animated.event([
{ nativeEvent: { contentOffset: { y: scrollY } } }
])}
layout={selectedType}
query={this.queries[selectedType]}
alwaysBounceVertical={false}
bounces={false}
/>

Related

Changed from react native class to function and it doesnt work 100%

I am trying to change a class into a function. This is mainly because if I can get it working, I want to use it for learning different animations, I have had some success but not 100%. Originally it displayed an icon that when clicked it spun it one way and then when clicked again it spun the other way. What I have tried to do it get rid of the icon and replace it with an image. It works when clicked once but then does nothing.
I am struggling with toggled aspect of it and setting the state I think because I cant seem to set it up properly in a function.
I have tried several things but this is the best I can get. If I show the original code and then what I have managed to change, maybe someone can point me in the right direction as to what I am doing wrong.
All I want is the image to display and then when clicked spins right and then if clicked again it spins left.
I am doing this so I can mess around with the settings and hopefully learn animation a bit better.
Any help would be greatly appreciated.
The original code :
import React from 'react';
import { View, StyleSheet, Animated, Image, TouchableOpacity } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
const TabIcon = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
if (menuToggled !== null) {
const animation = new Animated.Value(menuToggled ? 0 : 1);
Animated.timing(animation, {
toValue: menuToggled ? 1 : 0,
duration: 500,
useNativeDriver: true
}).start();
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
}
return (
<TouchableOpacity
style={styles.tabStyle}
onPress={onPress}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
export default class App extends React.Component {
state = {
menuToggled: null
}
toggleMenu = () => {
this.setState(prevState => {
return { menuToggled: !prevState.menuToggled };
});
}
render () {
return (
<View style={styles.container}>
<TabIcon
onPress={this.toggleMenu}
menuToggled={this.state.menuToggled}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
and what I have changed so far :
import React, { useRef, useState } from "react";
import { View, StyleSheet, Animated, Image, TouchableOpacity, Easing } from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState([null]);
toggleMenu = () => {
setMenuToggled(menuToggled === "null" ? "menuToggled" : "null");
}
const Spinner = ({
onPress,
menuToggled
}) => {
const logoStyles = [styles.logoStyle];
const animation = new Animated.Value(0);
const go = () => {
Animated.timing(animation, {
toValue: 1,
duration: 1500,
easing: Easing.elastic(1),
useNativeDriver: true
}).start();
}
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={go}
>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/></Animated.View>
</TouchableOpacity>
);
};
return (
<View style={styles.container}>
<Spinner
onPress={toggleMenu}
menuToggled={menuToggled}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;
There are a few issues. You first had menuToggled initialized to [null] when it should have been null. You also had forgotten to use onPress in TabIcon. The most noteworthy thing was wrapping TabIcon in a useCallback to prevent it from being recreated all the time. Expo snack:
import React, { useRef, useState, useCallback } from 'react';
import {
View,
StyleSheet,
Animated,
Image,
TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
const App = () => {
const spinValue = useRef(new Animated.Value(0)).current;
const [menuToggled, setMenuToggled] = useState(null);
const TabIcon = useCallback(({ onPress, menuToggled }) => {
const logoStyles = [styles.logoStyle];
// initialized base on menuToggled
// if not done then it will take an additional button press to trigger
// the animation
const animation = useRef(new Animated.Value(menuToggled ? 0 : 1)).current;
const startAnimation = () => {
Animated.timing(animation, {
toValue: menuToggled ? 1 :0,
duration: 500,
useNativeDriver: true,
}).start();
};
const rotateInterpolate = animation.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const animatedStyles = { transform: [{ rotate: rotateInterpolate }] };
logoStyles.push(animatedStyles);
return (
<TouchableOpacity
onPress={() => {
startAnimation();
onPress?.();
}}>
<Animated.View style={logoStyles}>
<Animated.Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
</Animated.View>
</TouchableOpacity>
);
},[]);
return (
<View style={styles.container}>
<TabIcon
onPress={() => setMenuToggled((prev) => !prev)}
menuToggled={menuToggled}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
},
tinyLogo: {
width: 150,
height: 150,
borderRadius: 100,
margin: 8,
},
});
export default App;

Problems with automatic image carousel when data is dynamic from an api using react native

I am implementing an image carousel which has an automatic rotation. When I implement it with static data (for example: creating a constant with an array) it works just the way I want it to.
However when I am getting the data from an api using axios, the carrosuel has a wrong behavior. The wrong behavior is as follows:
Swipe to the second image on the carousel and before moving on to the third image, go back to the first image and then go to the third image, then go to the fourth image, go back to the first image, and then go to the first image. fourth, this behavior is repeated x times.
So I think the problem is when I use axios. I attach the code of the classes that intervene in the problem that I am currently presenting.
I am using react native 0.62 with hooks and axios
HomeScreen.js
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import CategoriesScreen from "./Categories/CategoriesScreen";
import { ScrollView } from "react-native-gesture-handler";
import Carousel from "./Banner/BannerOficial";
import { axiosClient } from "../../config/axios";
export default function HomeScreen({ navigation }) {
const [banners, setBanners] = useState([]);
useEffect(() => {
getBannersAPI();
}, []);
function getBannersAPI(){
axiosClient
.get("/service/banner_available")
.then(async function (response) {
setBanners(response.data);
})
.catch(function (error) {
console.log("Error cargando los banners: ", error);
});
}
return (
<View style={{ flex: 1 }}>
<ScrollView>
<Carousel data={banners} />
<CategoriesScreen navigation={navigation} />
</ScrollView>
</View>
);
}
Carousel.js
import React, { useState, useEffect } from 'react'
import { View, Text, StyleSheet, Dimensions, FlatList, Animated } from 'react-native'
import CarouselItem from './BannerItem'
const { width, heigth } = Dimensions.get('window')
let flatList
function infiniteScroll(dataList){
const numberOfData = dataList.length
let scrollValue = 0, scrolled = 0
setInterval(function() {
scrolled ++
if(scrolled < numberOfData)
scrollValue = scrollValue + width
else{
scrollValue = 0
scrolled = 0
}
this.flatList.scrollToOffset({ animated: true, offset: scrollValue})
}, 3000)
}
const Carousel = ({ data }) => {
const scrollX = new Animated.Value(0)
let position = Animated.divide(scrollX, width)
const [dataList, setDataList] = useState(data)
useEffect(()=> {
setDataList(data)
infiniteScroll(dataList)
})
if (data && data.length) {
return (
<View>
<FlatList data={data}
ref = {(flatList) => {this.flatList = flatList}}
keyExtractor={(item, index) => 'key' + index}
horizontal
pagingEnabled
scrollEnabled
snapToAlignment="center"
scrollEventThrottle={16}
decelerationRate={"fast"}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => {
return <CarouselItem item={item} />
}}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }]
)}
/>
<View style={styles.dotView}>
{data.map((_, i) => {
let opacity = position.interpolate({
inputRange: [i - 1, i, i + 1],
outputRange: [0.3, 1, 0.3],
extrapolate: 'clamp'
})
return (
<Animated.View
key={i}
style={{ opacity, height: 10, width: 10, backgroundColor: '#595959', margin: 8, borderRadius: 5 }}
/>
)
})}
</View>
</View>
)
}
console.log('Please provide Images')
return null
}
const styles = StyleSheet.create({
dotView: { flexDirection: 'row', justifyContent: 'center'}
})
export default Carousel
CarouselItem.js
import React from "react";
import { View, StyleSheet, Text, Image, Dimensions} from 'react-native';
const { width, height} = Dimensions.get('window')
const CarouselItem = ({item}) => {
return(
<View style={styles.cardView}>
<Image style={styles.image} source = {{ uri: item.imagePath}}/>
</View>
)
}
const styles = StyleSheet.create({
cardView:{
flex:1,
width: width -20,
height: height / 7,
backgroundColor: "white",
margin: 10,
borderRadius: 10,
shadowColor: "#000",
shadowOffset: {width: 0.5, height: 0.5},
shadowOpacity: 0.5,
shadowRadius: 3,
elevation: 5,
},
image: {
width: width-20,
height: height / 3,
borderRadius: 10
}
})
export default CarouselItem

automatic scrolling in code for PDF using React Native and react-native-pdf

I'm a bit new to React Native programming, and I'm working on implementing an autoscroll feature for PDFs. For example, in some cases, I want to automatically scroll a PDF down x pages and then scroll at a desired speed. I followed this tutorial here here which works for just normal data, but when I used a object from react-native-pdf, it does not seem to scroll anymore. I'm wrapping the PDF object inside a ScrollView and can confirm that the scrolling code is being called. Can anyone suggest a solution or explain why this does not work with the PDF? Thanks so much!
I've also attached my code below if that helps. Currently, the PDF displays but is not autoscrolling at all.
import React from 'react';
import {StyleSheet, Dimensions, View, ScrollView} from 'react-native';
import Pdf from 'react-native-pdf';
export default class PDFScroll extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPosition: 0,
};
this.scrolling = this.scrolling.bind(this);
}
componentDidMount(){
this.activeInterval = setInterval(this.scrolling, 100);
}
componentWillUnmount(){
clearInterval(this.activeInterval);
}
scrolling() {
position = this.state.currentPosition + 50;
this.pdf.scrollTo({ x: position, animated: true });
// After position passes this value, snaps back to beginning
let maxOffset = 2000;
// Set animation to repeat at end of scroll
if (this.state.currentPosition > maxOffset) {
this.pdf.scrollTo({ x: 0, animated: false })
this.setState({ currentPosition: 0 });
}
else {
this.setState({ currentPosition: position });
}
}
render() {
const source = {
uri: 'http://samples.leanpub.com/thereactnativebook-sample.pdf',
cache: true,
};
return (
<View style={styles.container}>
<ScrollView
style={styles.scrollview}
horizontal={false}
bounces={true}
ref={(ref) => this.pdf = ref}
>
{
<Pdf
source={source}
onLoadComplete={(numberOfPages, filePath) => {
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`current page: ${page}`);
}}
onError={error => {
console.log(error);
}}
onPressLink={uri => {
console.log(`Link presse: ${uri}`);
}}
style={styles.pdf}
/>
}
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
marginTop: 25,
},
pdf: {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
});

React Native - Need to hide/show header with Animation on scroll regardless of scroll position

Currently I have this code:
import React, { Component, PureComponent } from 'react';
import { View, FlatList, RefreshControl, StatusBar, Animated, ScrollView, PanResponder } from 'react-native';
import { heightPercentageToDP as hp, widthPercentageToDP as wp } from 'react-native-responsive-screen';
import { connect } from 'react-redux';
import i18n from 'i18n-js';
import Post from '../components/Post';
import AppHeader from '../components/AppHeader';
class Posts extends PureComponent {
constructor(props) {
super(props);
this.state = {
curY: new Animated.Value(0),
height: 0
};
}
render() {
const { postsReducer } = this.props,
{ container } = styles;
return (
<View style={container}>
<Animated.View
style={{
transform: [{
translateY: this.state.curY.interpolate({
inputRange: [0, 1],
outputRange: [0, -1]
})
}], position: 'absolute', top: 0, width: wp('100%'), marginTop: StatusBar.currentHeight
}}
onLayout={({ nativeEvent }) => this.setState({ height: nativeEvent.layout.height })}
>
<AppHeader />
</Animated.View>
<Animated.ScrollView
scrollEventThrottle={16}
refreshControl={
<RefreshControl
onRefresh={this._onRefresh}
refreshing={refreshing}
tintColor='#5E81F4'
colors={["blue", "lightblue"]}
/>
}
contentContainerStyle={{ marginTop: this.state.height }}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.curY } } }],
{ useNativeDriver: true }
)}
>
{postsReducer.map((item, index) => (
<Post
postId={item._id}
userId={item.owner}
owner={item.owner}
title={item.title}
avatar={item.picture}
userName={item.userName}
updatedAt={item.updatedAt}
image={item.photo.split(",")}
description={item.description}
age={item.age}
time={item.time}
date={item.date}
location={item.location}
city={item.city}
commentCounter={item.commentCounter}
key={index}
/>
))}
</Animated.ScrollView>
</View>
);
}
}
const styles = {
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}
};
const mapStateToProps = ({ registrationReducer, postsReducer, usersReducer, }) => (
{
registrationReducer,
postsReducer,
usersReducer
}
);
export default connect(mapStateToProps, { setPosts })(Posts);
When I start scrolling down header hides, and when I scroll up again appears. But header appears only when I get to the beginning of list.
And I need such solution: when I'm for example in the middle of the list and start scroll up - header should appear, and when started scroll down it hides again. So it should be independently from position.
It work this way in Facebook mobile app for example.
I found solution. Just need to use Animated.diffClamp.
Here is the final code. Maybe will be useful for someone:
import React, { Component, PureComponent } from 'react';
import { View, FlatList, RefreshControl, StatusBar, Animated, ScrollView, PanResponder } from 'react-native';
import { heightPercentageToDP as hp, widthPercentageToDP as wp } from 'react-native-responsive-screen';
import { connect } from 'react-redux';
import i18n from 'i18n-js';
import Post from '../components/Post';
import AppHeader from '../components/AppHeader';
class Posts extends PureComponent {
constructor(props) {
super(props);
this.state = {
curY: new Animated.Value(0),
height: 0
};
}
render() {
const { postsReducer } = this.props,
{ container } = styles;
const headerDistance = Animated.diffClamp(this.state.curY, 0, 60).interpolate({
inputRange: [0, 1],
outputRange: [0, -1]
});
return (
<View style={container}>
<Animated.ScrollView
scrollEventThrottle={16}
refreshControl={
<RefreshControl
onRefresh={this._onRefresh}
refreshing={refreshing}
tintColor='#5E81F4'
colors={["blue", "lightblue"]}
/>
}
contentContainerStyle={{ marginTop: this.state.height }}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.curY } } }],
{ useNativeDriver: true }
)}
>
{postsReducer.map((item, index) => (
<Post
postId={item._id}
userId={item.owner}
owner={item.owner}
title={item.title}
avatar={item.picture}
userName={item.userName}
updatedAt={item.updatedAt}
image={item.photo.split(",")}
description={item.description}
age={item.age}
time={item.time}
date={item.date}
location={item.location}
city={item.city}
commentCounter={item.commentCounter}
key={index}
/>
))}
</Animated.ScrollView>
<Animated.View
style={{
transform: [{
translateY: headerDistance
}], position: 'absolute', top: 0, width: wp('100%'), marginTop: StatusBar.currentHeight
}}
onLayout={({ nativeEvent }) => this.setState({ height: nativeEvent.layout.height })}
>
<AppHeader />
</Animated.View>
</View>
);
}
}
const styles = {
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-start',
}
};
const mapStateToProps = ({ registrationReducer, postsReducer, usersReducer, }) => (
{
registrationReducer,
postsReducer,
usersReducer
}
);
export default connect(mapStateToProps, { setPosts })(Posts);

Multiple drag and drop simultaneous in React Native

Hello i'm creating a game in react native and i'm stuck because i wan't both players can drag and drop horizontaly an element in same time on the same phone.
I have two components like that:
export class Player1 extends Component{
constructor(props){
super(props);
this.state = {
pan : new Animated.ValueXY()
};
}
componentWillMount(){
this.panResponder = PanResponder.create({
onMoveShouldSetResponderCapture : () => true,
onMoveShouldSetPanResponderCapture : () => true,
onPanResponderGrant : (e, gestureState) => {
this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
this.state.pan.setValue({x: 0, y: 0});
},
onPanResponderMove : Animated.event([null,{
dx : this.state.pan.x,
}]),
onPanResponderRelease: (e, {vx, vy}) => {
}
});
}
render(){
return (
<View style={styles.mainContainer}>
{this.renderDraggable()}
</View>
);
}
renderDraggable(){
return (
<View style={styles.draggableContainer}>
<Animated.View
style={[this.state.pan.getLayout(), styles.triangle]}
{...this.panResponder.panHandlers} >
</Animated.View>
</View>
);
}
}
And in my screen i call my components like that:
export default function HomeScreen() {
return (
<View>
<Player1></Player1>
<Player2></Player2>
</View>
);
}
Thanks for your help
I found a solution, i used react-native-gesture-handle like in the directory doubleDraggable of the example: https://kmagiera.github.io/react-native-gesture-handler/docs/example.html
My Code:
import React, { Component } from 'react';
import { Animated, StyleSheet, View } from 'react-native';
import {
PanGestureHandler,
ScrollView,
State,
} from 'react-native-gesture-handler';
export class Players extends Component {
constructor(props) {
super(props);
this._translateX = new Animated.Value(0);
this._translateY = new Animated.Value(0);
this._lastOffset = { x: 0, y: 0 };
this._onGestureEvent = Animated.event(
[
{
nativeEvent: {
translationX: this._translateX,
},
},
],
);
}
_onHandlerStateChange = event => {
if (event.nativeEvent.oldState === State.ACTIVE) {
this._lastOffset.x += event.nativeEvent.translationX;
this._translateX.setOffset(this._lastOffset.x);
this._translateX.setValue(0);
this._translateY.setOffset(this._lastOffset.y);
this._translateY.setValue(0);
}
};
render() {
return (
<PanGestureHandler
{...this.props}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}>
<Animated.View
style={[
styles.box,
{
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
],
},
this.props.boxStyle,
]}
/>
</PanGestureHandler>
);
}
}
export default class Example extends Component {
render() {
return (
<View style={styles.scrollView}>
<DraggableBox />
</View>
);
}
}
const styles = StyleSheet.create({
scrollView: {
flex: 1,
},
box: {
position: 'absolute',
width: 0,
height: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 25,
borderRightWidth: 25,
borderBottomWidth: 50,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
},
});
And Screen:
<View styles={styles.container}>
<Players boxStyle={styles.player1}></Players>
<Players boxStyle={styles.player2}></Players>
</View>
I have been searching for something similar endlessly for a few days but I couldn't find these demos that react-native-gesture-handler provides. Thanks a lot for posting this here #Lillian Pacaud. Here is the link for several of their demos including the draggable component: https://snack.expo.dev/#adamgrzybowski/react-native-gesture-handler-demo
If you need any simultaneous presses/gesture/drags/etc... your best bet is to use react-native-gesture-handler because the native implementation of all touch/gesture-based components in react native don't allow for simultaneous interactions with each especially for Android.
I made a functional component that does the same thing as the accepted answer. Just pass whatever you want to be draggable as a child under the component. It can handle simultaneous drags as well like the accepted answer on both iOS and Android.
Example of using the draggable component:
import React from 'react';
import { View } from 'react-native';
import { DraggableTest } from '../components/test';
export default function Screen() {
return (
<View style={{ flex: 1 }}>
<DraggableTest>
<View
style={{ width: 150, height: 150, backgroundColor: 'lime' }}
/>
</DraggableTest>
</View>
);
}
The draggable component:
import React, { useRef } from 'react';
import { Animated, StyleSheet } from 'react-native';
import { PanGestureHandler, State } from 'react-native-gesture-handler';
export function DraggableTest({ children }) {
const pan = useRef(new Animated.ValueXY()).current;
const lastOffset = useRef({ x: 0, y: 0 }).current;
const onGestureEvent = Animated.event(
[{ nativeEvent: { translationX: pan.x, translationY: pan.y } }],
{ useNativeDriver: false },
);
const onHandlerStateChange = event => {
if (event.nativeEvent.oldState == State.ACTIVE) {
lastOffset.x += event.nativeEvent.translationX;
lastOffset.y += event.nativeEvent.translationY;
pan.setOffset({ x: lastOffset.x, y: lastOffset.y });
pan.setValue({ x: 0, y: 0 });
}
};
return (
<PanGestureHandler
onGestureEvent={onGestureEvent}
onHandlerStateChange={onHandlerStateChange}>
<Animated.View style={[pan.getLayout(), styles.animatedView]}>
{children}
</Animated.View>
</PanGestureHandler>
);
}
const styles = StyleSheet.create({
animatedView: {
position: 'absolute',
},
});