I'm trying to create an animated "parallax" image in React-Native. I have a static background image and two individual images as an overlay. The goal is to create something similar to the image found on this website http://www.digitalhands.net/ or https://www.galaxia.co/. Where do I even start with this? Initially I'd be happy with it moving just on itself from left to right and etc. Afterwards I want to make it so that it would use the gyroscope to get the x and y values for animating the image.
A parallax effect consists of images moving in different speeds and in the same direction, such that the 'closer' an object is, the faster it moves, which creates the illusion of three dimensions.
To achieve this effect in react-native, you can use the Animated library to interpolate the position of one image as a fraction of the position of another.
For the sake of an example, let's assume you want the parallax effect in the vertical direction, and that all images are positioned at 0 vertically.
First, you would need an animated value in your component's state:
this.state = {
...
imagePos: new Animated.Value(0)
}
Then, for each Image style you can add a transform on its y axis:
<Animated.Image src={...} style={[firstImageStyle, {
transform: [{translateY: this.state.imagePos.interpolate({
inputRange: [0, 1],
outputRange: [0, 100]
})}]
}]}
<Animated.Image src={...} style={[secondImageStyle, {
transform: [{translateY: this.state.imagePos.interpolate({
inputRange: [0, 1],
outputRange: [0, 50]
})}]
}]}
Note the use of Animated.Image instead of Image to make the image animatable.
This would cause the first image to move horizontally between 0-100 dp when imagePos has values between 0 and 1, and the second image to move between 0-50 dp.
To change the value of the animated value you can use any of the functions in the Animated library (timing, spring, decay, etc.) or you can attach it to some native event.
The animated library documentation has much more detail.
As for the use of the gyroscope, I haven't gone into the details, but you can probably use react-native-sensors or react-native-motion-manager to get the values you need, and attach them to the animation.
By using a package like https://github.com/react-native-sensors/react-native-sensors I managed to get the x, y, z values of the phones position and animate an image with it.
constructor() {
super();
this.state = {
x: 0,
y: 0,
z: 0,
};
}
componentWillMount() {
// Normal RxJS functions
accelerationObservable
.subscribe(({x, y, z}) => this.setState({
x: x,
y: y,
z: z,
}));
}
render() { return (
<Image style={{left: this.state.x + this.state.y, top: this.state.z}}
source={require('image.png')}/>
)
}
Related
I kind of new using animation, im using react native reanimated to create some animations, but i really dont understand how interpolate works, the input range, output range, the animation that i want its inside a scrollview, each item should has its own Animated.View, so the first one will be 100% width and the rest will be like 20%, i want to create this design:
for now my code is this:
const animatedWidth = useAnimatedStyle(() => {
const width_ = interpolate(
cardWidth,
[0,1, 2],
[4, 4, 5],
);
return {
width: width_,
};
});
return (
<>
<Animated.ScrollView horizontal = {true}>
{
extras.map((extra) => {
return (
<>
<Animated.View style = {[{height: 220, margin: 5}, animatedWidth]}>
<ImageBackground
source = {require("../assets/images/hawai.jpeg")}
style = {{width: "100%", height: "100%"}}
imageStyle = {{borderRadius: 30
}}>
[![</Animated.View][2]][2]>
)}
}
<Animated.ScrollView>
)
and this is the result:
i can scroll and it "works" and i want to make it work like the first design, the first full width and the others 20% width, but i really dont understand the interpol and the inputRange to make them appears, if someone knows and can explain to me in simple works really greateful, searched for videos and documentation but like i said still dont understand, thanks :)
I'm looking for the equivalent of Animated.sequence and Animated.parallel from react-native. So far from the docs for v2, I could only see the withSequence function that changes the value of only on value and therefore the style of only one component in series.
What I was looking for was to trigger animations in two different components, either in series or in parallel.
For parallel, it seems changing values in statements one after another worked. Correct me if I'm wrong.
// these will run in parallel
val1Shared.value = withTiming(50);
val2Shared.value = withTiming(100);
But for series, I need to have each animation inside a useTiming callback. Which leads to kind of callback hell.
val1Shared.value = withTiming(50, undefined, () => {
val2Shared.value = withTiming(100);
});
Please help with the best practices in achieving this with reanimated 2.
Thanks.
I think there is no way of doing it like in Animated.
This library requires you to think a different way. If you want you run two animations (each one for a separate view) using interpolation, you can use a single shared value and a sufficient interpolation config.
Let's assume (according to your example) that you have two views you want to animate. The first animation is from 0 to 50, the second one is from 0 to 100.
Let's also assume you're transforming an X axis (I don't know what's your case, but it's going to be very similar) and we count the animation progress from 0 to 1, and both views will animate for the same period of time.
Initialize the shared value:
const animation = useSharedValue(0);
And do sth like this (of your choice):
animated.value = withTiming(1, { duration, easing });
For the first view, your transformation will look like this:
const animatedStyles1 = useAnimatedStyle(
() => ({
transform: [
{
translateX: interpolate(animation.value, [0, 0.5, 1], [0, 50, 50]),
},
],
}),
[mode, rectSize]);
and for the second one, like this:
const animatedStyles2 = useAnimatedStyle(
() => ({
transform: [
{
translateX: interpolate(animation.value, [0, 0.5, 1], [0, 0, 100]),
},
],
}),
[mode, rectSize]);
Now the explanation.
For the first view, we only work for the half of progress from the whole animation, so we from 0 to 0.5 we'll animate from 0 to 50. then, for the reset of the progress we'll stay in the same place (this is called a dead zone), to from 0.5 to 1 value of the progress does not change (50 to 50).
Now, for the second view, we wait for the progress to be in half, so from 0 to 0.5 we don't do anything (0 to 0). Then, when the progress is in half, we animate the view from 0 to 150.
In general, that way you can orchestrate the whole thing using just a single shared value.
Here you can find more info about the interpolation. The docs is from the react-native's Animated, but the idea is the same.
Good luck!
I want to move a circle from top to bottom inside of a View. For top I simply figured out y = 0.
How can I measure the y co-ordinate for the end of a view.
I tried Dimensions, but the height and width are different from coordinates.
My app uses Animated.ValueXY({ x: xCordinate, y: yCordinate })
The xCordinate and yCordinate get passed from a function that changes coordinates.
Animated.timing(this.moveAnimation, {
duration: 1000,
toValue: {x: xCordinate, y: yCordinate},
}).start();
I managed random value for my phone but it is not same for all devices
React native provides a onLayout method that gets triggered every time your view renders. Here's a sample code.
onLayout = (e) => {
this.setState({
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height,
x: e.nativeEvent.layout.x,
y: e.nativeEvent.layout.y
})
}
render (
<View onLayout={this.onLayout}>
<Text>Hamza Waleed</Text>
</View>
)
}
I want to create my own loader using animation, my idea is using scale to handle it, so the step should be like:
show an image scale from (n) to 1 for the first time
check if conditional is accepted or not
if conditional accepted, show an image scale from 1 to (n)
I can achieve 1st condition, but when i try 2nd and 3rd condition, the animation start in a blink of eye,
anyone can help me how to fix this?
here's the code i've tried:
//animation function
playAnimation(){
this.setState({onLoad:true})
this._load.setValue(0)
this._scale.setValue(0)
Animated.timing(this._load, {
toValue: 100,
useNativeDriver: true,
duration:1000
}).start(()=>{
setTimeout(() => {
Animated.timing(this._scale, {
toValue: 100,
useNativeDriver: true,
duration:1000
}).start(()=>this.setState({onLoad:false}));
}, 2000);
});
onLoad = this._load.interpolate({
inputRange: [0, 50, 75, 100],
outputRange: [10, 1, 1.4, 1],
})
imageScale = this._scale.interpolate({
inputRange: [20, 35, 70],
outputRange: [1.2, 1, 20],
})
}
//on render
<Animated.Image
style={{
resizeMode:'contain',
position:'absolute',
zIndex:1,
width:width/2,
height:200,
transform:[{scale:this.state.onLoad?onLoad:imageScale}]
}}
source={{uri: "https://www.knittedhome.com/communities/5/004/012/872/235/images/4628207884.jpg"}}
/>
and the result:
How your code works right now is somewhere in your code, you're calling PlayAnimation().
PlayAnimation() will:
start and complete first animation
Wait 2 sec
start and complete second animation
What you're doing in your Animated.Image is setting the transforms to different values. This doesn't start or stop the animation, just tells Animated.Image what value to set transform to.
What you should do
Use one Animated.Value to keep track of the scale. Assign that value to the Animated.Image transform.
Write two Animation functions:
scaleDown, call this where you're currently calling PlayAnimation
scaleUp, use your onLoad conditional to execute this in render or somewhere appropriate.
Is there an package, or another way to have a simple, let's say blue to blueish, radial gradient background, for one of the views?
I've tried react-native-radial-gradient, but it seems like it's outdated.
Probably you can use RadialGradient from my react-native-image-filter-kit package. Note that gradients from react-native-image-filter-kit are initially designed as primitives for blending with other images and your case is not something that was taken into account in the first place.
import { Image } from 'react-native'
import {
RadialGradient,
ImageBackgroundPlaceholder
} from 'react-native-image-filter-kit'
const imageStyle = { width: 320, height: 320 }
const gradient = (
<RadialGradient
colors={['red', '#00ff00', 'blue']}
stops={[0, 0.5, 1]}
image={
<ImageBackgroundPlaceholder style={imageStyle}>
/* your content here */
</ImageBackgroundPlaceholder>
}
/>
)