I aim to show a rotating cube in a react-native app. I can show the cube with this code but whenever I uncomment the rotation, it disappears. Anything with the renderer ?
I use snack and the cube is visible only in the "Web" view, not in iOS or Android.
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import { GLView } from 'expo-gl';
import { THREE } from 'expo-three';
import ExpoTHREE from 'expo-three';
export default class App extends React.Component {
constructor(props) {
super(props);
this._onGLContextCreate = this._onGLContextCreate.bind(this);
this.animate = this.animate.bind(this);
}
_onGLContextCreate = async gl => {
this.scene = new THREE.Scene();
this.camera = new THREE.Camera(
75,
gl.drawingBufferWidth / gl.drawingBufferHeight,
0.1,
1000
);
this.scene.add(this.camera);
this.renderer = new ExpoTHREE.Renderer({ gl });
this.geometry = new THREE.BoxGeometry(1, 1, 1);
this.material = new THREE.MeshBasicMaterial({ color: 0xff0f00 });
this.obj = new THREE.Mesh(this.geometry, this.material);
this.edges = new THREE.EdgesGeometry(this.geometry);
this.line = new THREE.LineSegments(
this.edges,
new THREE.LineBasicMaterial({ color: 0xffffff })
);
this.scene.add(this.line);
this.scene.add(this.obj);
this.animate();
};
animate = () => {
requestAnimationFrame(this.animate);
//this.geometry.rotation.x += 0.01;
//this.geometry.rotation.y += 0.01;
this.renderer.render(this.scene, this.camera);
};
render() {
return (
<View style={styles.container}>
<GLView
style={{ width: 300, height: 300 }}
onContextCreate={this._onGLContextCreate}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Code was mainly adapted from https://medium.com/#zohayb1996/using-expo-and-three-js-with-react-native-4bcb353b222e
Thanks
In Three.js you don't rotate a Geometry, you rotate the Mesh. Simply update your animate function to use:
this.obj.rotation.x += 0.01;
this.obj.rotation.y += 0.01;
Related
I am trying to dim the background of this bottomSheet when it is activated or being rendered in the screen.
Also how do you make sure the bottomsheet disappears from the screen when the user touches the part of the screen that is not covered by the bottomSheet when it is active?
This is the code in app.js
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, View, ImageBackground, Text } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { TouchableOpacity } from 'react-native';
import BottomSheet from './components/BottomSheet';
import { useCallback, useRef } from 'react';
import Volcano from './Images/Volcano.jpg'
export default function App() {
const firstRef = useRef (null)
const onPress = useCallback (() => {
const isActive = firstRef?.current?.isActive1();
if (isActive) {
firstRef?.current?.scrollTo(35);
} else {
firstRef?.current?.scrollTo(-200);
}
})
return (
<GestureHandlerRootView style={{flex:1}}>
<ImageBackground source={Volcano} resizeMode='repeat' style={{
flex: 1,
width : '100%',
// flexDirection: 'column',
justifyContent: 'center',
}}>
<StatusBar style="auto" />
<TouchableOpacity style={{
height:50,
width: '10%',
backgroundColor:'green',
aspectRatio:1,
borderRadius:25,
opacity:.6,
marginLeft:360,
}} onPress={onPress}/>
<BottomSheet ref={firstRef}/>
</ImageBackground>
</GestureHandlerRootView>
);
};
This is the one in the bottomsheet.js
import { Dimensions, StyleSheet, Text, View } from 'react-native'
import React, { useCallback, useImperativeHandle } from 'react'
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { event, Extrapolate, interpolate, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
const {height: SCREEN_HEIGHT} = Dimensions.get('window');
const MAX_TRANSLATE_Y = -SCREEN_HEIGHT + 50;
const BottomSheet = React.forwardRef(({},ref) => {
const translateY = useSharedValue(35);
const active = useSharedValue()
const scrollTo = useCallback ((destination = Number) =>{
'worklet';
active.value = destination !== 35;
translateY.value = withSpring(destination, {damping:15});
}, []);
const isActive1 = useCallback (()=> {
return active.value;
},[])
useImperativeHandle(ref, () => ({scrollTo, isActive1}), [scrollTo, isActive1]);
const updatePan = useSharedValue({y:0});
const activateGesture = Gesture.Pan()
.onStart(() => {
updatePan.value = { y:translateY.value};
})
.onUpdate((e) => {
translateY.value = e.translationY + updatePan.value.y;
translateY.value = Math.max(translateY.value, MAX_TRANSLATE_Y);
})
.onEnd (() => {
if (translateY.value > -SCREEN_HEIGHT/3){scrollTo(35) ;
} else if (translateY.value < -SCREEN_HEIGHT / 1.5) {
scrollTo(MAX_TRANSLATE_Y)
}
});
const rBottomSheetStyle = useAnimatedStyle (() => {
const borderRadius = interpolate(
translateY.value,
[MAX_TRANSLATE_Y + 100, MAX_TRANSLATE_Y],
[25, 5],
Extrapolate.CLAMP
);
return {
borderRadius,
transform: [{ translateY: translateY.value }],
};
});
return (
<GestureDetector gesture={activateGesture}>
<Animated.View
style= {[styles.bottomSheetContainer, rBottomSheetStyle]}
>
<View style={styles.line} />
</Animated.View>
</GestureDetector>
)
})
const styles = StyleSheet.create({
bottomSheetContainer: {
height: SCREEN_HEIGHT,
width:'100%',
backgroundColor: 'white',
position: 'absolute',
top: SCREEN_HEIGHT,
borderRadius: 25,
},
line:{
width: 75,
backgroundColor: 'grey',
height: 4,
alignSelf: 'center',
marginVertical: 15,
borderRadius:2,
}
})
export default BottomSheet
As you can see on the image the background is not dim when the bottomsheet is activatted.
[1]: https://i.stack.imgur.com/RdEzm.jpg
Please assist
I'm trying to get screen dimensions from a custom hook but I don't know how to use the results from the imported component.
Obviously I could use {MyDims.Width} and {MyDims.Height} to display the results but for my final script I need to use the 2 objects as strings, hence the useState().
Here is the imported component: getdimensions.js
import React, {
useState,
useEffect
} from "react";
import {
Dimensions
} from "react-native";
function App(props) {
const [Width, setWidth] = useState(0)
const [Height, setHeight] = useState(0)
const getDims = () => {
setWidth(Dimensions.get("screen").width)
setHeight(Dimensions.get("screen").height)
}
useEffect(() => {
getDims()
}, []);
return {Width, Height}
}
export default App;
And the main screen: App.js
import React, {
useState,
useEffect,
} from "react";
import { Text, View, StyleSheet } from 'react-native';
import useDimensions from './components/getdimensions';
export default function App() {
const MyDims = useDimensions()
const [ShowMyDims, setShowMyDims] = useState({
width: MyDims.Width,
height: MyDims.Height
})
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
width: {ShowMyDims.width} and
height: {ShowMyDims.height}
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: 50,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
You can return it as an array to avoid it being object. To get the updates from your custom hook to your component useState, add useEffect and trigger state updates upon change in Width, Height from your custom hook.
// In your hook component
...
return [Width, Height];
// In main component
...
const [Width, Height] = useDimensions();
const [ShowMyDims, setShowMyDims] = useState({ width: Width, height: Height });
// Add a useEffect hook to listen the updates
useEffect(() => {
setShowMyDims({ width: Width, height: Height });
}, [Width, Height])
....
You have an option of directly using Width, Height from your custom hook into your component without having an intermediate useState.
I have tried to implement the component expand to full screen in react native by using Layout animation in react-native but it was not good to look. Can any one help me in getting it?
changeLayout = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ expanded: !this.state.expanded });
};
I expect to expand the component on click to full screen and again collapse it on click.
Set the initial value you want through the animation, obtain the screen width and height, and create a click function to execute.
This is an example that I made. Click this link if you want to run it yourself.
import React from 'react';
import { Animated, Text, View,Dimensions,Button } from 'react-native';
const screenwidth = Dimensions.get('screen').width
const screenheight = Dimensions.get('screen').height
class FadeInView extends React.Component {
state = {
fadeAnim: new Animated.Value(50),
fadeAnim2: new Animated.Value(50),
}
componentDidMount() {
}
animatebutton() {
Animated.timing( // Animate over time
this.state.fadeAnim, // The animated value to drive
{
toValue: screenheight,
duration: 10000, // Make it take a while
}
).start();
Animated.timing( // Animate over time
this.state.fadeAnim2, // The animated value to drive
{
toValue: screenwidth,
duration: 10000, // Make it take a while
}
).start(); // Starts the animation
}
render() {
let { fadeAnim,fadeAnim2 } = this.state;
return (
<Animated.View // Special animatable View
style={{
...this.props.style,
height: fadeAnim,
width : fadeAnim2
}}
>
{this.props.children}
</Animated.View>
);
}
}
// You can then use your `FadeInView` in place of a `View` in your components:
export default class App extends React.Component {
constructor(props){
super(props);
this.state={
}
}
animatebutton(){
this.fade.animatebutton();
}
render() {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}} >
<FadeInView style={{backgroundColor: 'powderblue'}} ref={ani => this.fade = ani}>
<Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
</FadeInView>
<Button title="go animate" onPress={() => this.animatebutton()}/>
</View>
)
}
}
OR
You can use LayoutAnimation that you want to use. Look at my example.
import React, {Component} from "react";
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableOpacity,
LayoutAnimation,
} from 'react-native';
class App extends Component {
constructor() {
super();
this.state = {
check: false,
}
}
onPresscheck() {
// Uncomment to animate the next state change.
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
// Or use a Custom Layout Animation
// LayoutAnimation.configureNext(CustomLayoutAnimation);
this.setState({ check : !this.state.check});
}
render() {
var middleStyle = this.state.check === false ? {width: 20,height:20} : {width: "100%",height:"100%"};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={() => this.onPresscheck()}>
<Text>pressbutton</Text>
</TouchableOpacity>
<View style={[middleStyle, {backgroundColor: 'seagreen'}]}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
button: {
width:"100%",
height: 60,
backgroundColor: 'blue',
alignItems: 'center',
justifyContent: 'center',
margin: 8,
},
});
export default App;
Please refer to this blog :
https://dev-yakuza.github.io/en/react-native/react-native-animatable/
Also, try using this library. Use any animation type you want and render them.
Happy coding :)
I have a react native app in which a user either has to click a photo or select a photo. When I select clicking a photo camera opens. I used a imagepicker for that
But I would like to add some widget on camera view itself like WhatsApp shows the image slideshow list view whenever we select camera. How can we draw on the camera and ability to choose the image from the small horizontal list view slideshow that is displayed on the camera view itself.
I was trying to build the same camera along with image selection from thousands of images
import React, {Component} from 'react';
import {Platform, StyleSheet, View, StatusBar, TouchableOpacity, FlatList, Image, ImageBackground} from 'react-native';
import { PermissionsAndroid, CameraRoll } from 'react-native';
//const Contner = createAppContainer(Routes);
export default class App extends Component
{
constructor(props) {
super(props);
z=[];
for (let i=0; i<500; i++)
{
z[i]=i;
}
this.z = z;
this.all_images={};
this.state={"images":{"edges":[]}};
}
async requestPhotosPermission() {
try
{
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE)
if (granted === PermissionsAndroid.RESULTS.GRANTED)
{
this.getPhotos();
} else
{
console.log("Photos permission denied")
}
} catch (err)
{
console.warn(err)
}
}
loadPhotos(){
var self=this;
const fetchParams = {
first: 1000000000,
assetType: 'Photos',
};
console.log("hellos");
CameraRoll.getPhotos(fetchParams).then((data) =>
{
console.log("length is ", data["edges"].length);
for (let j=0; j<data["edges"].length; j++)
{
this.all_images[j] = data["edges"][j]["node"]["image"]["uri"];
}
self.setState({"images":data});
}).catch((e) =>
{
console.log(e);
});
}
componentWillMount(){
this.requestPhotosPermission().then(()=>{
this.loadPhotos()
});
};
onViewableItemsChanged = ({ viewableItems, changed }) => {
// console.log("Visible items are", viewableItems[0],"to",viewableItems[viewableItems.length-1]);
// console.log("Changed in this iteration", changed);
};
render(){
console.log("finders");
let self=this;
return (
<FlatList
horizontal={true}
data={this.z}
renderItem={({item}) => {
console.log("item is ", item,self.state.images.edges.length);
// console.log(self.state.images.edges[0]);
if (self.state.images.edges.length!==0)
{
return <ImageBackground source={{uri: self.state.images.edges[item].node.image.uri}}
style={{height: 100, width: 100, margin: 4}}/>
}else
return <View style={{height:100,width:100,margin:4,borderWidth:1,backgroundColor:'#4a7642'}}/>
}}
onViewableItemsChanged={this.onViewableItemsChanged }
style={{borderWidth:3,borderColor:'red',height:'30%'}}
viewabilityConfig={{itemVisiblePercentThreshold: 50}}/>);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
backgroundColor: 'black',
},
preview: {
flex: 1,
justifyContent: 'flex-end',
alignItems: 'center',
},
capture: {
flex: 0,
backgroundColor: '#fff',
borderRadius: 5,
padding: 15,
paddingHorizontal: 20,
alignSelf: 'center',
margin: 20,
},
});
I would like this list view to overlay on the normal Camera Widget
I want to make a "rounded bottom" component, without using a ImageBackground, like this:
I tried to use a combination of <LinearGradient/>, but to simplify the code in this question, I used <View/> instead.
Here is my code:
import React from 'react'
import { Dimensions, StyleSheet, View } from 'react-native'
export default class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<View style={classes.container}>
<View style={classes.block} />
<View style={classes.roundedBlock} />
</View>
)
}
}
const classes = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
},
block: {
height: 135,
backgroundColor: 'red',
},
roundedBlock: {
height: 15,
backgroundColor: 'red',
width: Dimensions.get('window').width,
borderBottomLeftRadius: Dimensions.get('window').width / 2,
borderBottomRightRadius: Dimensions.get('window').width / 2,
}
})
This code is available for tests purpose on Expo Snack
Here is the result:
As you can see, the borderRadius is limited to 7.5px, which is half of the height of the block, instead of half of the width as demanded.
Is there a way to override this limit? If no, is there a way to achieve what I want?
You can use ART from react-native to draw whatever you want to draw. Some unofficial docs https://github.com/react-native-china/react-native-ART-doc/blob/master/doc.md. Please check the Expo Snack or code below.
import React from 'react';
import { Dimensions, StyleSheet, View, ART } from 'react-native';
const {
Surface,
Shape,
Path,
RadialGradient,
Pattern,
Transform,
LinearGradient,
} = ART;
const width = Dimensions.get('window').width;
export default class App extends React.Component {
constructor(props) {
super(props);
}
getPathRect = () => {
const x = width;
const y = 0;
const radius = 1000;
return ART.Path()
.moveTo(x, y)
.lineTo(x - width, y)
.lineTo(x - width, y + width / 2)
.lineTo(x, y + width / 2)
.close();
};
getPathArc = () => {
const x = width;
const y = 0;
const radius = 1000;
return ART.Path()
.moveTo(x, y + width / 2)
.arc(-x, 0, radius, radius)
.close();
};
gradient = () => {
return new LinearGradient(
{
'.01': 'blue', // blue in 1% position
'1': 'red', // opacity white in 100% position
},
'0',
'0',
width,
'0'
);
};
render() {
return (
<View style={classes.container}>
<Surface width={width} height={width}>
<Shape
d={this.getPathRect()}
fill={this.gradient()}
// stroke="red"
strokeWidth="1"
strokeCap="butt"
strokeJoin="bevel"
/>
<Shape
d={this.getPathArc()}
fill={this.gradient()}
// stroke="red"
strokeWidth="1"
strokeCap="butt"
strokeJoin="bevel"
/>
</Surface>
</View>
);
}
}
const classes = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
},
});