How to drew a line between two points in react native? - android

I'm working on React Native.
Actually I want to draw a line between two points in react native.
The point is where I start touch and where I release touch.
I'm doing this using penResponder.
Using penResponder I get those point. Where i start touch and where I release touch.
Here is my code:
import React, {Component} from 'react';
import {
StyleSheet,
View,
Platform,
Text,
PanResponder,
Image,
} from 'react-native';
export default class App extends Component {
constructor() {
super();
//initialize state
this.panResponder;
this.state = {
locationX: 0,
locationY: 0,
locationSX: 0,
locationSY: 0,
};
//panResponder initialization
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onStartShouldSetPanResponderCapture: (event, gestureState) => {
this.setState({
locationX: event.nativeEvent.locationX.toFixed(2),
locationY: event.nativeEvent.locationY.toFixed(2),
});
},
onMoveShouldSetPanResponder: (event, gestureState) => false,
onMoveShouldSetPanResponderCapture: (event, gestureState) => false,
onPanResponderGrant: (event, gestureState) => false,
onPanResponderMove: (event, gestureState) => {},
onPanResponderRelease: (event, gestureState) => {
this.setState({
locationSX: event.nativeEvent.locationX.toFixed(2),
locationSY: event.nativeEvent.locationY.toFixed(2),
});
},
});
this.setState({
locationX: 0,
locationY: 0,
locationSX: 0,
locationSY: 0,
});
}
render() {
return (
<View style={styles.MainContainer}>
<View style={styles.childView}>
<View
style={[
{
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
{
top: parseFloat(this.state.locationY - 5),
left: parseFloat(this.state.locationX - 15),
},
]}
/>
<View
style={[
{
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
{
top: parseFloat(this.state.locationSY - 2),
left: parseFloat(this.state.locationSX - 11),
},
]}
/>
<View
style={{flex: 1, backgroundColor: 'transparent'}}
{...this.panResponder.panHandlers}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
},
childView: {
flex: 1,
overflow: 'hidden',
},
point: {
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
});
But how to draw line between the two points?
Actually I want this:
Please help!
Thanks in advance.

You can use svg (https://github.com/react-native-community/react-native-svg) to do this. I recommend you to put your PanResponder on top of your svg to handle touches.
Svg example:
<Svg height={windowHeight} width={windowWidth}>
<Line x1={startTouch.x} y1={startTouch.y} x2={endTouch.x} y2={endTouch.y} stroke="red" strokeWidth="2" />
</Svg>

Related

ReactNative animation is not working after the api call

here is my code.
const actionButtonsAnimated = new Animated.Value(0);
const animated = new Animated.Value(255);
const animateTrendingCardSheet = () => {
Animated.timing(animated, {
toValue: 0,
duration: 1500,
useNativeDriver: true,
}).start();
Animated.timing(actionButtonsAnimated, {
toValue: -180,
duration: 1000,
useNativeDriver: true,
}).start();
};
<Animated.View
style={[
{
transform: [{ translateY: actionButtonsAnimated }],
},
]}
>
<MainActionButtons />
</Animated.View>
<Animated.View
style={[
{
transform: [{ translateY: animated }],
width: "100%",
position: "absolute",
bottom: 0,
},
]}
>
<View style={styles.trendingCards}>
<Text h5 center color={colors.trendingText}>
Trending in your area...
</Text>
<View style={styles.flatlistWrapper}>
<FlatList
horizontal={true}
data={trendingCards}
renderItem={({ item }) => <TrendingCardComponent card={item} />}
/>
</View>
</View>
</Animated.View>
so if i call the animateTrendingCardSheet function inside useEffect like this.
useEffect(() => {
animateTrendingCardSheet()
}, [])
it works as expected but once i put it in a condition that it should be called after the API call has been finished it does not work at all if i again save the file it hot reload animation works
useEffect(() => {
if (loadTrendingCard) {
animateTrendingCardSheet();
}
}, [loadTrendingCard]);
Your issue is that after the first call to animateTrendingCardSheet the toValue that you are animating to is the current value of your Animated variables; so it looks like nothing is happening. You can counteract this by resetting your animation variables before calling your animation function:
import * as React from 'react';
import {
Text,
View,
StyleSheet,
Animated,
TouchableOpacity,
FlatList,
Button
} from 'react-native';
import Constants from 'expo-constants';
const MainActionButtons = () => {
return (
<View
style={{
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
}}>
<TouchableOpacity>Btn 1</TouchableOpacity>
<TouchableOpacity>Btn 2</TouchableOpacity>
<TouchableOpacity>Btn 3</TouchableOpacity>
<TouchableOpacity>Btn 4</TouchableOpacity>
</View>
);
};
const TrendingCardComponent = ({ card }) => {
return (
<View style={{ width: '100%' }}>
<Text>{card.title}</Text>
<Text>{card.message}</Text>
</View>
);
};
const trendingCards = [
{ title: 'A Cool Card', message: 'A cool message' },
{ title: 'Card 1', message: 'A cool message' },
{ title: 'A Cool Card', message: 'A cool message' },
{ title: 'A Cool Card', message: 'A cool message' },
];
const initialActionButton = 0;
const initialAnimated = 255;
export default function App() {
const actionButtonsAnimated = new Animated.Value(initialActionButton);
const animated = new Animated.Value(initialAnimated);
const opacity = new Animated.Value(1)
const onApiCall = ()=>{
// set opacity to 0
Animated.timing(opacity,{toValue:0,duration:500}).start(()=>{
// when view is invisible do resets
animated.setValue(initialAnimated)
actionButtonsAnimated.setValue(initialActionButton)
Animated.timing(opacity,{toValue:1,duration:500}).start()
animateTrendingCardSheet()
})
}
const animateTrendingCardSheet = () => {
Animated.timing(animated, {
toValue: 0,
duration: 1500,
useNativeDriver: true,
}).start();
Animated.timing(actionButtonsAnimated, {
toValue: -180 ,
duration: 1000,
useNativeDriver: true,
}).start();
};
// React.useEffect(() => {
// animateTrendingCardSheet();
// }, []);
return (
<View style={styles.container}>
<Button title="Simulate API call" onPress={onApiCall}/>
<Animated.View
style={[
{
transform: [{ translateY: actionButtonsAnimated }],
opacity
},
]}>
<MainActionButtons />
</Animated.View>
<Animated.View
style={[
{
transform: [{ translateY: animated }],
width: '100%',
position: 'absolute',
bottom: 0,
opacity
},
]}>
<View style={styles.trendingCards}>
<Text>Trending in your area...</Text>
<View style={styles.flatlistWrapper}>
<FlatList
style={{ flex: 1 }}
horizontal={true}
data={trendingCards}
renderItem={({ item }) => <TrendingCardComponent card={item} />}
/>
</View>
</View>
</Animated.View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
flatlistWrapper: {
width: '100%',
height: 200,
},
});
Demo
The above answer is good but it does not solve my problem, its all due to a stupid mistake, the issue was i was setting the state before calling the animation call function, as the state changes and it interfere the animation i had to use useRef like this.
const actionButtonsAnimated = useRef(new Animated.Value(initialActionButton)).current;
const animated = useRef(new Animated.Value(initialAnimated)).current;
it reference the state and animation works as expected.

How to implement Neumorphism View to React Native for Android?

I have to create Neumorphism styled UI into React Native project and I'm currently developing for android. I've currently encountered a problem in which shadows properties don't work as expected for android. I then tried using react-native-drop-shadow which I read from this blog:
https://blog.logrocket.com/applying-box-shadows-in-react-native/
Here is my current implementation
NeoView.tsx
import React, { ReactNode } from "react";
import { StyleSheet, View, ViewStyle } from "react-native";
import DropShadow from "react-native-drop-shadow";
type NeoViewProps = {
children: ReactNode;
size?: number;
style?: ViewStyle;
};
const NeoView = ({ children, size, style }: NeoViewProps) => {
return (
<>
<DropShadow style={styles.bottomShadow}></DropShadow>
<DropShadow style={styles.topShadow}>
<View
style={[
styles.inner,
{ width: size || 40, height: size || 40, borderRadius: 10 },
style,
]}
>
{children}
</View>
</DropShadow>
</>
);
};
export default NeoView;
const styles = StyleSheet.create({
inner: {
backgroundColor: "#F0F0F3",
alignItems: "center",
justifyContent: "center",
borderColor: "#D3D5D8",
borderWidth: 1,
},
topShadow: {
shadowOffset: {
width: -2,
height: -2,
},
shadowOpacity: 1,
shadowRadius: 4,
shadowColor: "#fff",
},
bottomShadow: {
shadowOffset: {
width: 2,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 4,
shadowColor: "#7A7987",
},
});

Pan Responder with transform not working in Android react native?

Problem:
In my react native app I have created an animation like tinder cards. It is working correctly in Ios but in Android Animated view which has panResponder and transform even does not render in the view . This is how I have organized my code.
const TinderCardAnimation = ({ theme, data, onHeartClick }) => {
const { width, height } = useWindowDimensions();
const [currentIndex, setCurrentIndex] = useState(0);
const pan = useRef(new Animated.ValueXY()).current;
const rotate = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: ['-10deg', '0deg', '10deg'],
extrapolate: 'clamp'
})
const rotateAndTranslate = {
transform: [{
rotate: rotate
},
...pan.getTranslateTransform()
]
}
const likeOpacity = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [0, 0, 1],
extrapolate: 'clamp'
})
const nopeOpacity = pan.x.interpolate({
inputRange: [-width / 2, 0, width / 2],
outputRange: [1, 0, 0],
extrapolate: 'clamp'
})
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) =>
true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
true,
onPanResponderTerminationRequest: (evt, gestureState) =>
true,
onPanResponderGrant: () => {
pan.setOffset({
x: pan.x._value,
y: pan.y._value
});
},
onPanResponderMove: (evt, gestureState) => {
pan.setValue({
x: gestureState.dx,
y: gestureState.dy
})
},
onPanResponderRelease: () => {
pan.flattenOffset();
},
onShouldBlockNativeResponder: (evt, gestureState) => {
// Returns whether this component should block native components from becoming the JS
// responder. Returns true by default. Is currently only supported on android.
return true;
}
})
).current;
return <View style={{ backgroundColor: '#ffffff' }}>{data?.map((item, index) => {
if (index < currentIndex) {
return null
} else if (index == currentIndex) {
return (<Animated.View {...panResponder.panHandlers} key={index} style={[rotateAndTranslate, { height: height / 5, width: width, padding: 10, position: 'absolute' }]}>
<Animated.View
style={{
opacity: likeOpacity,
transform: [{ rotate: "-30deg" }],
position: "absolute",
top: 50,
left: 40,
zIndex: 1000
}}
>
<TouchableOpacity >
<Image style={{ marginTop: height / 4 }} source={require("_assets/images/XCircleb.png")} />
</TouchableOpacity>
</Animated.View>
<Animated.View
style={{
opacity: nopeOpacity,
transform: [{ rotate: "30deg" }],
position: "absolute",
top: 50,
right: 40,
zIndex: 1000
}}
>
<TouchableOpacity onPress={() => onHeartClick(item?.id)}>
<Image style={{ marginTop: height / 4 }} source={require("_assets/images/HCircleb.png")} />
</TouchableOpacity>
</Animated.View>
<ProfileCard key={index} profile={item} />
</Animated.View>)
} else {
return (<Animated.View key={index} style={{ height: height / 5, width: width, padding: 10, position: 'absolute' }}>
<ProfileCard key={index} profile={item} />
</Animated.View>)
}
}).reverse()}
</View >
}
export default withTheme(TinderCardAnimation);
I tried a lot to make it work in Android. But I was unable to do so. Can someone help me to solve this issue? Thank you

Creating an AnimatedTextInput causes the keyboard to close immediately

I'm having trouble getting an AnimatedTextInput to work properly where a normal TextInput works properly. When I press the TextInput to start typing, there are no problems, and I can start typing immediately. However, on an AnimatedTextInput, the software keyboard closes immediately.
So, this works just fine:
return (
<View>
<TextInput style={[style, styles.input, { paddingTop: 20, paddingBottom: 20 }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
However this:
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
return (
<View>
<AnimatedTextInput style={[style, styles.input, { paddingTop: paddingTop, paddingBottom: paddingBottom }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
Immediately causes the software keyboard to quickly open then close when touching the input to gain focus/to start typing. On searching, answers I've seen say this is related to being part of a scrollview, but this happens whether in a scrollview or not.
For reference, here's the entire file:
import React, { useRef } from 'react';
import { Animated, StyleSheet, Text, TextInput, View } from 'react-native';
const styles = StyleSheet.create({
input: {
padding: 24,
borderColor: 'transparent',
borderWidth: 1,
borderRadius: 4,
fontFamily: 'Avenir-Medium',
fontSize: 16,
backgroundColor: '#F8F8F8',
},
labelContainer: {
position: 'absolute',
left: 16,
top: 9,
paddingHorizontal: 8,
},
label: {
color: '#ABB4BD',
fontFamily: 'Avenir-Heavy',
fontSize: 12,
},
})
// extend from native TextInput props
const TextField = (props) => {
const { label, style, ...restOfProps } = props;
const [isFocused, setIsFocused] = React.useState(false);
const opacity = useRef(new Animated.Value(0)).current;
const paddingTop = useRef(new Animated.Value(20)).current;
const paddingBottom = useRef(new Animated.Value(20)).current;
const refInput = useRef();
React.useEffect(() => {
Animated.timing(
paddingBottom,
{
useNativeDriver: false,
toValue: isFocused ? 10 : 20,
duration: 200,
}
).start();
Animated.timing(
paddingTop,
{
useNativeDriver: false,
toValue: isFocused ? 30 : 20,
duration: 200,
}
).start();
Animated.timing(
opacity,
{
useNativeDriver: false,
toValue: isFocused ? 1 : 0,
duration: 200,
}
).start();
}, [isFocused])
const handleFocus = (input) => {
console.log('here')
setIsFocused(true);
}
const handleBlur = () => {
console.log('there')
setIsFocused(false);
}
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
return (
<View>
<AnimatedTextInput style={[style, styles.input, { paddingTop: paddingTop, paddingBottom: paddingBottom }]} {...restOfProps} onFocus={() => handleFocus()} onBlur={handleBlur} ref={refInput} />
<Animated.View style={[styles.labelContainer, { opacity: opacity}]}>
<Text style={styles.label}>{label}</Text>
</Animated.View>
</View>
)
};
export default TextField;

How to draw a line once I drag on screen in React Native?

Actually I want to draw a line once I drag on screen.
Please see the image:
Suppose I swipe from point A to point B. Then here will be create a line (Like the image).
Please give me an idea how to do it.
It's easily done by using PanResponer (https://reactnative.dev/docs/panresponder) and SVG (https://github.com/react-native-community/react-native-svg).
Here is the code:
import React, {Component} from 'react';
import Svg, {Line} from 'react-native-svg';
import {
StyleSheet,
View,
Platform,
Text,
PanResponder,
Image,
Dimensions,
} from 'react-native';
var width = Dimensions.get('window').width;
var height = Dimensions.get('window').height;
export default class App extends Component {
constructor() {
super();
//initialize state
this.panResponder;
this.state = {
startTouchX: 0,
startTouchY: 0,
endTouchX: 0,
endTouchY: 0,
};
//panResponder initialization
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onStartShouldSetPanResponderCapture: (event, gestureState) => {
this.setState({
startTouchX: event.nativeEvent.locationX.toFixed(2),
startTouchY: event.nativeEvent.locationY.toFixed(2),
});
},
onMoveShouldSetPanResponder: (event, gestureState) => false,
onMoveShouldSetPanResponderCapture: (event, gestureState) => false,
onPanResponderGrant: (event, gestureState) => false,
onPanResponderMove: (event, gestureState) => {},
onPanResponderRelease: (event, gestureState) => {
this.setState({
endTouchX: event.nativeEvent.locationX.toFixed(2),
endTouchY: event.nativeEvent.locationY.toFixed(2),
});
},
});
this.setState({
startTouchX: 0,
startTouchY: 0,
endTouchX: 0,
endTouchY: 0,
});
}
render() {
return (
<View style={styles.MainContainer}>
<View style={styles.childView}>
<Svg height={height} width={width} position="absolute">
<Line
x1={this.state.startTouchX}
y1={this.state.startTouchY}
x2={this.state.endTouchX}
y2={this.state.endTouchY}
stroke="red"
strokeWidth="8"
/>
</Svg>
<View
style={{flex: 1, backgroundColor: 'transparent'}}
{...this.panResponder.panHandlers}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer: {
flex: 1,
},
childView: {
flex: 1,
overflow: 'hidden',
},
point: {
height: 22,
width: 22,
marginTop: 5,
position: 'absolute',
borderRadius: 14,
backgroundColor: '#afeeee',
},
});

Categories

Resources