Interpolated opacity is not initially invisible - reactjs

I am trying to create an interpolation on opacity. I create the clock, and decide the start time and end time of the animation. I interpolate it so it starts at 0, however my view is visible. Do you know why it's visible? Is it the clock is created not at default of 0 but of current time? Is it possible to initialize my animStartTime at the same value as clock?
Here is an expo showing the problem and code and screenshot below. We see a black square of 48x48. It should be invisible but its at full 100% opacity.
https://snack.expo.io/#noitidart/reanimated-interpolation-not-initially-invisible
import React from 'react';
import { View, StyleSheet } from 'react-native';
import Reanimated from 'react-native-reanimated';
export default function App() {
const animClock = new Reanimated.Clock();
// determine the times it will run from
const duration = 1400;
const animStartTime = new Reanimated.Value(0);
const animEndTime = Reanimated.add(animStartTime, duration);
// create the opacity
const animFrom = new Reanimated.Value(0);
const animTo = new Reanimated.Value(1);
const opacity = Reanimated.interpolate(animClock, {
inputRange: [animStartTime, animEndTime],
outputRange: [animFrom, animTo],
extrapolate: Reanimated.Extrapolate.CLAMP,
});
return (
<View style={{ justifyContent: 'center', alignItems: 'center', flex: 1 }}>
<Reanimated.View
style={{ width: 48, height: 48, backgroundColor: 'black', opacity }}
/>
</View>
);
}

I modified some part of your code and you can see it there

Related

How to apply animation in react-native?

I want to add animation to this search bar.
when the user touches the search bar it size decreases and again increases and gets to its default size(like animation in popups)
This is my code
<View style={{flexDirection:'row',alignSelf:'center'}}>
<TextInput
onChangeText={(text) => setSearch(text)}
onFocus={()=>{
setSize('92%');
setInterval(()=>{setSize('95%')},1000)
}}
placeholder="Search"
style={{...styles.searchbox,width:size}}
></TextInput>
</View>
I am currently trying to change width..
Firstly, I suggest you to take a look at RN animated documentation, maybe it will help you to understand better how the animations work.
Also, it depends on what you're having there: a class component or a function component.
If you're using a function component, you could do it like this:
Creating a custom hook, called, let's say useAnimation(), which would look something like this
export const useAnimation = ({ doAnimation, duration, initialValue, finalValue }) => {
const [animation, setAnimation] = useState(new Animated.Value(initialValue))
useEffect(() => {
Animated.spring(animation, {
toValue: doAnimation ? initialValue : finalValue,
duration,
bounciness: 8,
useNativeDriver: false
}).start();
}, [doAnimation]);
return animation
}
As it is said in the documentation, you could animate only Animated components, and for example if you want to have an animated View, the tag will be <Animated.View> {...} </Animated.View, but for the <TextInput> we have to create the animated component:
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
and combining the first 2 steps
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput)
const [collapsed, setCollapsed] = useState(true)
const animation = useAnimation({ doAnimation: collapsed, duration: 300, initialValue: 20, finalValue: 200 });
const onFocusText = () => setWidth(false)
return (
<AnimatedTextInput
onFocus={onFocusText}
placeholder={"Search something"}
style={{width: animation, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 4, padding: 10}}
/>
)
Also, if you're having a class component, you could have a method which will start the animation (but don't forget about the step 2 which is essential)
private size: Animated.Value = new Animated.Value(COLLAPSED_VALUE)
get resizeInputWidth(): Animated.CompositeAnimation {
return Animated.timing(this.size, {
toValue: EXPANDED_VALUE,
duration: 500,
})
}
startAnimation = () => this.resizeInputWidth.start()
<AnimatedTextInput
onFocus={this.startAnimation}
style={{ width: this.size }}
/>

React Native memory leak react native gesture handler

My apps memory usage is increasing by 0,1 MB about every 3 seconds without me doing anything in the app. I made sure to remove all event listeners so that's not the problem, im out of tricks to solve this memory leak. Is there a tool to inspect which processes are writing to the ram or some other way to detect this leak ?
I have detected the memory leak, it was an issue with react-native-gesture-handler, I did this:
<PanGestureHandler
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onGestureEvent}>
<Animated.View style={{ transform: [{ translateX: this.translateX }] }}>
<FlatList />
</Animated.View>
</PanGestureHandler>
I didn't thought about that there is going to be a gesture handler in front of the whole FlatList which in my case contains 200+ items. I still don't ge why the memory usage is increasing without doing anything but I have resolved this issue.
This is my workaround:
const { width: SCREEN_WIDTH } = Dimensions.get('window');
const TOSS_SEC = 0.2;
const MULTIPLIER = Math.round(SCREEN_WIDTH / 90);
class ReanimatedFlatList extends React.Component {
constructor(props) {
super(props);
// drag Distance
const dragX = new Value(0);
// gesture state
const state = new Value(-1);
// drag velocity
const dragVX = new Value(0);
this.onGestureEvent = event([
{ nativeEvent: { translationX: dragX, velocityX: dragVX, state: state } },
]);
this.transX = new Value();
const prevDragX = new Value(0);
const clock = new Clock();
const snapPoint = cond(
lessThan(add(this.transX, multiply(TOSS_SEC, dragVX)), -80),
-100,
0,
);
this.unconstrainedX = cond(
eq(state, State.ACTIVE),
[
stopClock(clock),
set(this.transX, add(this.transX, sub(dragX, prevDragX))),
set(prevDragX, dragX),
this.transX,
],
[
set(prevDragX, 0),
set(
this.transX,
cond(
defined(this.transX),
runSpring(clock, this.transX, dragVX, snapPoint),
0,
),
),
],
);
this.translateX = interpolate(this.unconstrainedX, {
inputRange: [-100, 0],
outputRange: [-100, 0],
extrapolate: Extrapolate.CLAMP,
});
}
render() {
return (
<React.Fragment>
<Animated.View style={{ transform: [{ translateX: this.translateX }] }}>
<FlatList />
</Animated.View>
<PanGestureHandler
maxPointers={1}
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onGestureEvent}>
<Animated.View
style={{
transform: [{ translateX: multiply(this.translateX, MULTIPLIER) }],
position: 'absolute',
top: 0,
width: SCREEN_WIDTH,
right: -SCREEN_WIDTH + 50,
bottom: 0,
}}
/>
</PanGestureHandler>
</React.Fragment>
);
}
}
This allows me to move the FlatList 100 pt to the left and display something next to it much like a navigation drawer. This solution is not perfect because you are not going to be able to scroll between SCREEN_WIDTH - 50 pt and SCREEN_WIDTH on the x axis but I haven't found a better solution for now.

Pull Scrollview to reveal View - React Native

I'm trying to build something similar to IMessage's and WhatsApp's header in react native, where users can pull down to reveal a search bar in the header.
I have been able to pull down to reveal a hidden input, but because the scrollview's y value becomes negative on pull, it will bounce back to y = 0 and prevent the input from sticking to the top. I have tried using both translateY and scaleY to reveal the hidden input.
class List extends Component {
scrollY = new Animated.Value(0)
render() {
const translateY = this.props.scrollY.interpolate({
inputRange: [ -50, 0 ],
outputRange: [ 50, 0 ],
extrapolate: 'clamp',
})
return (
<>
<Animated.View style={[
styles.container,
{ transform: [ { translateY } ] },
]}>
<Input />
</Animated.View>
<Animated.ScrollView
onScroll={Animated.event(
[ { nativeEvent: { contentOffset: { y: this.scrollY } } } ],
{ useNativeDriver: true }
)}
scrollEventThrottle={16}
>
{...}
</Animated.ScrollView>
</>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: colors.white,
width: windowWidth,
height: 50,
position: 'absolute',
top: -50,
zIndex: -99,
},
});
I found this Stack Overflow post that has been useful to reference but it is IOS specific Pull down to show view
I solved this by using contentOffset and without any animations. I needed to make sure the scrollview was at least the size of the phone's windowHeight and then used contentOffset to push the initial y value of the Scrollview to the size of the header
<ScrollView
ListHeaderComponent={() => (
<Header headerHeight={hiddenHeaderHeight} />
)}
contentContainerStyle={{ minHeight: windowHeight }}
contentOffset={{ y: hiddenHeaderHeight }}
...
This solution works for a Flatlist as well.
One thing to note is contentOffset is an ios specific prop
check out this medium article. It provides a detailed explanation of how to do something similar to your desired behavior.

JSS Obj - refer value of property from another property in same parent obj

I got a material-ui AppBar component that is position="fixed" so it sticks to the upper border of the window as a main manu bar. Components name here is "header" Its getting styled with this JSS obj.
Since the main container slips under the AppBar Component to top 0, when its position is fixed i need to margin-top it down right under the AppBar in order to have them positioned consecutively.
Preferably, I would like to set the margin-top to have the actual value of the AppBar height so I dont need to set it manualy. (also the AppBar height adjusts to its content, so it may be of variable size)
However I dont know how to do this, so I have to set the height/margin-top(main_container) manually.
At least: How do I make the main_horizontal_container.marginTop get its value form the header.height, so I only have to set it once?
Unfort, that does not work as planned - "TypeError: Cannot read property 'height' of undefined"
const styles = theme => ({
main_horizontal_container:{
display: "flex",
get marginTop () {return this.header.height}
},
header:{
height: 64,
},
sidebar:{
//flexBasis: content,
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
minWidth: 0, // So the Typography noWrap works
},
toolbar: theme.mixins.toolbar,
});
It seems that this in this.header.height refers to main_horizontal_container which doesn't have header. Instead, you could extract header to a variable:
const styles = theme => {
const header = {
height: 64,
};
return ({
main_horizontal_container:{
display: "flex",
get marginTop () {return header.height}
},
header,
sidebar:{
//flexBasis: content,
},
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing.unit * 3,
minWidth: 0, // So the Typography noWrap works
},
toolbar: theme.mixins.toolbar,
});
}

How to draw square to tag an object. (React Native)

I see many mobile apps having a feature that user can draw a square to indicate something to tag on the image.
I'm building Face Tagging app and basically user draws square on the boundary of human's face on the image.
I've Googled it many times but I'm not sure RN has some feature library for tagging.
How can I achieve this? Is there any good library to draw square on image? And it will be even better if I can remember its coordinate width, height, and position of the rectangle.
Any idea or suggestion will be highly appreciated!
This is an example below
You can use React Native ART library to draw shapes on top of image. It is a standard library that ships with React Native (although not linked to the binary by default).
Regarding the algorithm:
Render an image
Overlay image with React Native's ART.Surface
Detect taps to get coordinates + overlay the rest of the image
Once you have coordinates of the tap, you can draw a shape you want
Stop drawing shape when user removes his finger (onPressOut event)
Where to go from here:
Basic tutorial, explaining how to wire up React Native and ART
Unofficial(there no official one) documentation
You can add children (in your case, square Views) to an Image tag, so you could do something like
<Image src={...}>
<View
style={{
position: 'absolute',
top: 120
left: 100,
height: 50,
width: 50,
borderWidth: 1
}}
/>
</Image>
You can get the x and y coordinates with the PanResponder API instead of hardcoding the top and left style properties
Edit: RN 50=< Removed support of nested content inside , use ImageBackground instead
I'd suggest looking into react-native PanResponder, or react-native-gesture-handler PanGestureHandler. It is a component that responds to touch input, and calculates the x and y values when you drag your finger, it will also tell you the distance travelled from where the finger started.
You can use this data and pass the x and y travel distance back into the width and height value of a View component to make it drag out a box with your finger.
EDIT:
Here's something I just put together as a bit of an experiment using react-native-gesture-handler.
import { View } from 'react-native';
import { GestureEvent, PanGestureHandler, PanGestureHandlerEventPayload } from 'react-native-gesture-handler';
const Test = () => {
const [start, setStart] = useState<{ x: number; y: number }>(null);
const [end, setEnd] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
const [dimensions, setDimensions] = useState<{ w: number; h: number }>({ w: 0, h: 0 });
const onPress = (event: GestureEvent<PanGestureHandlerEventPayload>) => {
const { x, y, translationX, translationY } = event.nativeEvent;
if (!start) setStart({ x: y, y: x });
setDimensions({ w: translationX, h: translationY });
};
const onEnd = () => {
if (!start) return;
setEnd(start);
setStart(null);
};
return (
<View style={{ flex: 1 }}>
<PanGestureHandler onGestureEvent={onPress} onEnded={onEnd}>
<View style={{ width: '100%', height: '100%', backgroundColor: 'red' }}>
<View
style={{
position: 'absolute',
backgroundColor: 'blue',
top: start?.x ?? end?.x,
left: start?.y ?? end?.y,
width: dimensions?.w ?? 0,
height: dimensions?.h ?? 0,
}}
/>
</View>
</PanGestureHandler>
</View>
);
};
export default Test;

Resources