Skip to content

Instantly share code, notes, and snippets.

@williamsiuhang
Created July 9, 2020 20:41
Show Gist options
  • Save williamsiuhang/3ea329131a6632cedc405eb798ae74b4 to your computer and use it in GitHub Desktop.
Save williamsiuhang/3ea329131a6632cedc405eb798ae74b4 to your computer and use it in GitHub Desktop.
React Native Image 3D Parallax using mobile gyroscope (Expo's DeviceMotion)
import React from 'react';
import { View, Animated, Easing } from 'react-native';
import { DeviceMotion } from 'expo-sensors';
class AnimatedAnalyticsImage extends React.Component {
state = {
motionX: 0,
motionY: 0,
motionZ: 0
}
transformValue = {
X: new Animated.Value(0),
Y: new Animated.Value(0)
}
cardTransform = {
X: '0deg',
Y: '0deg'
}
componentDidMount() {
DeviceMotion.isAvailableAsync()
.then(available => {
if (available) {
this.setState({ motionAvailable: true });
DeviceMotion.addListener(responder => {
this.setState({
motionX: responder.rotation.alpha,
motionY: responder.rotation.beta,
motionZ: responder.rotation.gamma
})
});
}
});
}
componentDidUpdate(prevProps, prevState) {
// limits parallax tilt
const rotateValue = (motion) => {
let limit = motion * 100;
if (Math.abs(limit) > 20) // limit to 20deg
limit = limit > 0 ? 20 : -20
return Math.floor(limit);
}
// update animation values
const imgRotateXPrev = rotateValue(prevState.motionY / 2); // divide by 2 to limit parallax on X axis
const imgRotateYPrev = rotateValue(prevState.motionZ);
const imgRotateX = rotateValue(this.state.motionY / 2);
const imgRotateY = rotateValue(this.state.motionZ);
this.cardTransform.X = this.transformValue.X.interpolate({ inputRange: [0, 1], outputRange: [imgRotateXPrev + 'deg', imgRotateX + 'deg'] });
this.cardTransform.Y = this.transformValue.Y.interpolate({ inputRange: [0, 1], outputRange: [imgRotateYPrev + 'deg', imgRotateY + 'deg'] });
// animation start
this.transformValue.X.setValue(0);
this.transformValue.Y.setValue(0);
Animated.timing(this.transformValue.X, { toValue: 1, duration: 100, easing: Easing.linear, useNativeDriver: true }).start();
Animated.timing(this.transformValue.Y, { toValue: 1, duration: 100, easing: Easing.linear, useNativeDriver: true }).start();
}
render() {
return (
<View style={imageStyles.container}>
<Animated.Image
source={require('assets/images/payment.png')}
style={[imageStyles.image, {
transform: [
{ rotateX: this.cardTransform.X },
{ rotateY: this.cardTransform.Y }
]
}]} />
</View>
)
}
}
const styles = {
container: {
alignItems: 'center'
}
}
const imageStyles = {
container: {
flex: 1,
marginTop: 20
},
image: {
width: '90%',
height: '100%',
alignSelf: 'center'
}
}
export default AnimatedAnalyticsImage;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment