Curved bottom on View - react-native

How can i add the curved bottom to a View in react-native? See curved example
I'f tried to a add an second view like this:
headerBottom: {
width: width / 2,
height: width / 2,
backgroundColor: 'red',
position: 'absolute',
bottom: -35,
left: width / 4 - 15,
borderRadius: width / 4,
transform: [
{scaleX: 2},
{scaleY: 0.25}
]
},
I've been able to get above but rather have a less bad solution right in the same view, not as in the example in a second view.

Here is the result. I used Dimensions (const window = Dimensions.get('window');) here to make it more dynamic to different screen sizes.
const styles = StyleSheet.create({
containerStyle: {
alignSelf: 'center',
width: window.width,
overflow: 'hidden',
height: window.width / 1.7
},
sliderContainerStyle: {
borderRadius: window.width,
width: window.width * 2,
height: window.width * 2,
marginLeft: -(window.width / 2),
position: 'absolute',
bottom: 0,
overflow: 'hidden'
},
slider: {
height: window.width / 1.7,
width: window.width,
position: 'absolute',
bottom: 0,
marginLeft: window.width / 2,
backgroundColor: '#9DD6EB'
}});
render() {
return(
<View style={styles.containerStyle} >
<View style={styles.sliderContainerStyle} >
<Slider/>
</View>
</View>
);
}

I ended up with using react-native-svg.:
<Circle
cx={screenWidth / 2}
cy={`-${898 - headerHeight + 2}`}
r="898.5"
fill="#EFF2F3"
stroke="#C5CACD"
strokeWidth="2"
/>

I don't know whether this is a proper way or not. However this works for me and hope this will help someone.
<View style={styles.parent}>
<View style={styles.child}>
<Text>Hello World</Text>
</View>
</View>
Insert your code to child View
const styles = StyleSheet.create({
parent : {
height : '80%',
width : '100%',
transform : [ { scaleX : 2 } ],
borderBottomStartRadius : 200,
borderBottomEndRadius : 200,
overflow : 'hidden',
},
child : {
flex : 1,
transform : [ { scaleX : 0.5 } ],
backgroundColor : 'yellow',
alignItems : 'center',
justifyContent : 'center'
}
})

i use border bottom radius. this is work.
class Home extends Component {
render() {
return (
<View style={styles.oval} />
)
}
}
const styles= StyleSheet.create({
oval: {
height: 100,
borderBottomLeftRadius: 50,
borderBottomRightRadius: 50,
backgroundColor: 'red'
},
});

Related

React Navigation (native): Overflow visible not working on custom header

I'm implementing a custom header for #react-navigation/native-stack. Using React Navigation v6.
One of the elements within the custom header has a native shadow on iOS added (via the style prop). The shadow is a tad bigger than the header and unfortunately, I can't get it to display beyond the boundaries of the header. Of course, I've tried using overflow: visible on basically every component in the tree, but no success. The shadow is clipped off:
Here's my custom header:
function CustomHeader(props: NativeStackHeaderProps) {
const { options, route, navigation } = props;
const insets = useSafeAreaInsets();
const headerHeight = Helpers.getHeaderHeight(insets);
return (
<View style={{
height: headerHeight,
paddingTop: insets.top,
width: '100%',
backgroundColor: Colors.white,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 20,
paddingRight: 20,
overflow: 'visible',
}}
>
<View style={{
flex: 1, display: 'flex', alignItems: 'flex-start',
}}
>
{ options.headerLeft ? options.headerLeft({ canGoBack: false }) : (
<TouchableOpacity
onPress={() => route.name === 'Home'
? null
: navigation.reset({ index: 0, routes: [{ name: 'Home' }] })}
>
<Image
style={{ width: Sizing.logo, height: Sizing.logo }}
source={Logo}
/>
</TouchableOpacity>
)}
</View>
<Text style={{
textAlign: 'center', color: Colors.purple,
}}
>
{(options.title || route.name).toUpperCase()}
</Text>
<View style={{
flex: 1, display: 'flex', alignItems: 'flex-end', overflow: 'visible',
}}
>
{ options.headerRight ? options.headerRight({ canGoBack: false }) : null}
</View>
</View>
);
}
The button on the right with the shadow is passed in through the headerRight option and contains this shadow style:
nativeShadow: {
shadowColor: Colors.gray,
shadowOffset: { width: 0, height: 8 },
shadowRadius: Colors.shadows.gray.distance,
shadowOpacity: 0.5,
},
Any idea what I could try next? I don't want to increase the headers' height since this would break the layout elsewhere.
I had a similar problem with the menu. I think i was taking another way and maybe you should try it.
Be sure that all the parent elements has at least the same height than the navmenu's height.
import React, { Component } from 'react';
import { TouchableOpacity, StyleSheet, Image, View, Text } from 'react-native';
const styles = StyleSheet.create({
menuParent: {
marginLeft: 30,
justifyContent: "flex-start",
alignItems: "flex-end",
flex: 1,
height: 250
},
btnContainer: {
width: 40,
height: 40,
elevation: 50,
zIndex: 50,
marginTop: 5
},
image: {
width: 40,
height: 40,
},
menuContainer: {
position: "absolute",
top: 5,
left: -5,
right: -5,
padding: 20,
borderRadius: 10,
elevation: 10,
zIndex: 10,
flex: 1,
height: 230,
backgroundColor: "#fff",
}
});
export default class DefaultHeaderMenu extends Component {
state = {
menuVisible: false
};
constructor(props) {
super(props);
}
render() {
return (
<View style={ styles.menuParent }>
{this.state.menuVisible ? <View style={ styles.menuContainer }></View> : null }
<TouchableOpacity
style={ styles.btnContainer }
onPress={ () => this.setState({ menuVisible: !this.state.menuVisible }) }
>
<Image
source={ require('#/assets/img/menu.png') }
style={ styles.image }
/>
</TouchableOpacity>
</View>
);
}
}
This is the component i used and the passed it to nav as headerRight element.
In my case, the problem was that height: 250 was missing in menuParent.
I hope it helps you and good luck
I had a problem like this once and fixed it by using 'zIndex'. Try setting it as the* max index in your project. Hope it helps

Touchable Opacity messes up width in row container

I'm trying to make these two Card components appear next to each other in a row as shown
here which seems to work when I wrap the component in a View, but appears like this with a bunch of unnecessary space in between when I try it with a TouchableOpacity.
Here is my code for the Card component (currently with TouchableOpacity on and the View wrapper commented out):
export const NavCard = ({
title,
height = 200,
onPress = null,
background = null,
}) => {
return (
<TouchableOpacity
onPress={onPress}
style={[
{ height: height },
background ? styles.cardImage : styles.noImage,
]}
>
{/* <View
style={[
{ height: height },
background ? styles.cardImage : styles.noImage,
]}
> */}
<Image
source={background}
resizeMode="cover"
style={{
height: height,
width: "100%",
borderRadius: 15,
position: "absolute",
top: 0,
right: 0,
}}
/>
<View style={{ padding: 15 }}>
<Text style={styles.title}>{title}</Text>
<Image
style={styles.arrow}
source={require("../assets/arrow-right.png")}
/>
</View>
{/* </View> */}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
cardImage: {
flexGrow: 1,
margin: "2%",
borderRadius: 15,
},
noImage: {
flexGrow: 1,
margin: "2%",
borderRadius: 15,
backgroundColor: "#333436",
},
title: {
fontSize: 24,
color: "#E4E5EA",
fontWeight: "bold",
shadowColor: "#000000",
shadowOffset: { width: 2, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
},
arrow: {
width: 15,
height: 15,
resizeMode: "contain",
position: "absolute",
top: 22,
right: 20,
},
});
Here is the code for the screen:
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<View style={styles.rowContainer}>
<NavCard
title="Map"
height={180}
onPress={() => navigation.navigate("Map")}
background={require("../assets/pvdx1.png")}
></NavCard>
<NavCard
title="CAD"
height={180}
background={require("../assets/pvdx1.png")}
onPress={() => navigation.navigate("CADScreen")}
></NavCard>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
paddingTop: 10,
padding: 4,
flex: 1,
},
rowContainer: {
flexDirection: "row",
justifyContent: "space-between",
},
body: {
paddingTop: 10,
fontSize: 16,
color: "#E4E5EA",
},
});
export default HomeScreen
Does anyone know why it's messing up the width of both components if the styles of the View and TouchableOpacity are exactly the same? I'm using React Native.
Edit: Have updated to use Image instead of ImageBackground (code now reflects this), but the result is the same.
I figured out the issue: I was importing TouchableOpacity from react-native-gesture-handler instead of react-native. Changed it and it works just fine.
Probably the problem is in your <ImageBackground /> , Because I replaced that with native <Image /> and it's working. here is how my Image looks, you can compare with your code:
<Image
style={{
height: height,
borderRadius: 15,
position: "absolute",
top: 0,
right: 0,
width: "100%"
}}
resizeMode="cover"
source={{ uri: "https://via.placeholder.com/250x150" }}
/>
Here you can check the working code
for me i had to also set the width on the TouchableOpacity (and the view within)
<TouchableOpacity
style={{flex: 1, width: '100%'}}

How to rotate and translate the Text 90 degree correctly in React Native?

I am trying to implement this layout
Here is my view code snippet
<View style={[{
flexDirection: 'row',
backgroundColor: 'white',
borderRadius: 8
}]}>
<Text style={{
color: 'white',
backgroundColor: 'red',
paddingStart: 12,
paddingEnd: 12,
transform: [
{ rotate: '-90deg' },
]
}}>{`Sample Text`}</Text>
<View >
<Text>Right Content will be here</Text>
</View>
</View>
Output
The problem of my current implementation are
The rotated view is not positioned at positioned
The container view's height is not affected with the rotated view's width or height
How can I fix it in a way it could adapt the font size accessibility settings?
You can use a transformation.
https://facebook.github.io/react-native/docs/transforms.html#proptypes
myStyle: {
transform: [{ rotate: '90deg'}]
}
Well with few research I've achieved my goal. Where I am manipulating the x, y position of text container based on the height and width of the rotated text
Solution
function MyComponent() {
const [height, setHeight] = React.useState(0)
const [width, setWidth] = React.useState(0)
return (
<View style={{ marginTop: 300 }}>
<View style={[{
flexDirection: 'row',
backgroundColor: 'white',
marginStart: 16,
marginEnd: 16,
borderRadius: 8,
overflow: 'hidden',
minHeight: width,
}]}>
<View style={{
backgroundColor: 'yellow',
transform: [
{ translateX: -(width / 2 - height / 2) * 2 }
]
}}>
<Text
style={{
color: 'white',
backgroundColor: 'red',
paddingStart: 12,
paddingEnd: 12,
transform: [
{ rotate: '-90deg' },
{ translateY: (width / 2 - height / 2) },
{ translateX: -(width / 2 - height / 2) }
]
}}
onLayout={(e) => {
setHeight(e.nativeEvent.layout.height)
setWidth(e.nativeEvent.layout.width)
}}
>{`Sample Text`}</Text>
</View>
<View style={{
backgroundColor: 'yellow',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
transform: [
{ translateX: -(width / 2 - height / 2) * 2 }
]
}}>
<Text>Right Content will be here</Text>
</View>
</View>
</ View>
)
}
Output

Unable to create curved view in react-native

<View style={{
width: window.width,
height: window.width / 7,
backgroundColor: colors.white}}>
<View style={{
backgroundColor: colors.primary,
borderBottomLeftRadius: 80,
borderBottomRightRadius: 80,
height: window.width / 7,
width: window.width,
flex: 1,
}}>
<View style={{
backgroundColor: '#000',
height: window.width / 7,
width: window.width / 3,
borderRadius: 100,
alignSelf: 'center',
position: 'absolute',
bottom: 0,
overflow: 'hidden',
}}>
</View>
</View>
Check the below solution
import * as React from "react";
import { View, StyleSheet } from "react-native";
export default function Curve() {
return (
<View style={{ margin: 50 }}>
<View style={styles.squreStyle} />
<View style={styles.arcStyle} />
</View>
);
}
const styles = StyleSheet.create({
squreStyle: {
width: "100%",
height: 75,
borderRadius: 12,
backgroundColor: "black",
},
arcStyle: {
width: "20%",
height: 70,
position: "absolute",
bottom: -25,
left: "40%",
borderRadius: 35,
backgroundColor: "black",
transform: [{ scaleX: 5 }, { scaleY: 1 }],
},
});
Sample image of the working code
Edit - Modified according to the requirements of Scroll & Write content inside header.
import * as React from "react";
import { View, StyleSheet, Text, ScrollView } from "react-native";
export default function Curve() {
return (
<ScrollView>
<View style={{marginVertical: 50}}>
<View style={styles.squreStyle}>
<Text style={{ color: "white", textAlign: "center" }}>Sample Header</Text>
</View>
<View style={styles.arcStyle} />
</View>
<View style={{ height: 500, backgroundColor: "green" }} />
<View style={{ height: 500, backgroundColor: "yellow" }} />
</ScrollView>
);
}
const styles = StyleSheet.create({
squreStyle: {
width: "100%",
height: 75,
borderRadius: 12,
backgroundColor: "black",
zIndex: 1,
},
arcStyle: {
width: "20%",
height: 70,
position: "absolute",
bottom: -25,
left: "40%",
borderRadius: 35,
backgroundColor: "black",
transform: [{ scaleX: 5 }, { scaleY: 1 }],
},
});
Hope this helps you. Feel free for doubts.
The thing is react native doesnt let us add border radius to sides unless of same height and width so that it can be a circle. What i did is exactly created a circle and pushed it upwards with top so that it seems like this. You can control the curvature by fixing the height and width and make it dynamic so that in every screen it looks the same.
Please check code below and also the expo snack:
expo-snack
Code:
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.newD}></View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
newD:{
height:500,
width:500,
backgroundColor:'red',
marginLeft:-70,
borderRadius:500,
top:-280
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
});
Hope it helps. feel free for doubts
You can use react-native-canvas for draw it
import {Dimensions} from 'react-native';
import Canvas from 'react-native-canvas';
const {width} = Dimensions.get('window');
...
drawArc = (canvas) => {
const height = 150;
const ctx = canvas.getContext('2d');
context.moveTo(0,height/2);
context.quadraticCurveTo(width/2, height, width, height/2);
};
...
<Canvas
ref={canvas => drawArc(canvas) />
...
Hope this helps to you.

Responsive React-native-web layout

I'm trying to create a dashboard layout from a react-native project. The idea is to keep most of the code similar for Android, iOS and Web only the layout or navigation style will change. But I find making this type of layout in web is easy but to make it responsive without re-rendering is difficult.
I have achieved this by manually calculating the windows heigh and width by following the code
Dimensions.get('window').width
Dimensions.get('window').height
and by eventListener keep updating the state so that it re-renders the whole page again and again.
Dimensions.addEventListener("change", this.updateScreen);
Is there a way I can simply use some % value to fill up the screen. Right now if I use % it squeezed to a child View size. I even tried flex:1 with alignSelf:stretch, alignItem:stretch, width:'100%' etc but no luck.
For a while, let's talk about center row (image attached) it contain 3 columns. I want left and right block (Menu & Call to Action) to be 300px each. Now if I'm on 1000px width monitor my Content block should be (1000 - (300+300)) 400px. if monitor is 1200px then Content block will be (1200 - (300+300)) 600px.
I hope this doesn't come too late.
<View style={{ flex: 1 }}>
<View style={{ height: 100, backgroundColor: 'red' }} />
<View style={{ flex: 1, backgroundColor: 'gray', flexDirection: 'row' }}>
<View style={{ width: 100, backgroundColor: 'green' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
<View style={{ width: 100, backgroundColor: 'green' }} />
</View>
<View style={{ height: 100, backgroundColor: 'red' }} />
</View>
This is the result from the above code.
You don't need to do percentage calculations at all; just structure it in 2 layers of flex layout.
For the components that should not stretch, state their width. For the rest, specify the flex value.
If you insist on using 1 layer to handle it all, then we shall discuss again.
Cheers.
I made this exactly for the same reasons:
https://www.npmjs.com/package/react-native-animated-layout
Demos:
Expo snack: https://snack.expo.dev/#mehmetkaplan/react-native-animated-layout
Web demo: https://mehmetkaplan.github.io/react-native-animated-layout/
It takes layouts as inputs and using the Width / Height ratio of the screen, decides which layout to use.
And within each layout, you define top, right, bottom, left of each screen using the coordinates between 0 and 1. (Because browser resizing changes the coordinates of each View and you want views to stay exact relative places.)
import React, { useState, useEffect } from 'react';
import { Text, View } from 'react-native';
import ReactNativeAnimatedLayout from 'react-native-animated-layout';
export default function App() {
const [rerender, setRerender] = useState(0);
const redView = <View style={{ backgroundColor: 'red', height: '100%', width: '100%', }}><Text>{"I am the red view"}</Text></View>;
const greenView = <View style={{ backgroundColor: 'green', height: '100%', width: '100%', overflow: 'hidden'}}>
<Text>{"I am the green view and my contents overflow. Because of the overflow prop, the over-flown content is hidden."}</Text><Text>{"0"}</Text><Text>{"1"}</Text><Text>{"2"}</Text><Text>{"3"}</Text><Text>{"4"}</Text><Text>{"5"}</Text><Text>{"6"}</Text><Text>{"7"}</Text><Text>{"8"}</Text><Text>{"9"}</Text><Text>{"10"}</Text><Text>{"11"}</Text><Text>{"12"}</Text><Text>{"13"}</Text><Text>{"14"}</Text><Text>{"15"}</Text><Text>{"16"}</Text><Text>{"17"}</Text><Text>{"18"}</Text><Text>{"19"}</Text><Text>{"20"}</Text><Text>{"21"}</Text><Text>{"22"}</Text><Text>{"23"}</Text><Text>{"24"}</Text><Text>{"25"}</Text><Text>{"26"}</Text><Text>{"27"}</Text><Text>{"28"}</Text><Text>{"29"}</Text>
</View>;
const blueView = <View style={{ backgroundColor: 'blue', height: '100%', width: '100%', }}><Text>{"I am the blue view"}</Text></View>;
const layouts = [
{
validAfterWHRatio: 0,
validBeforeWHRatio: 0.9,
views: [
{ top: 0, bottom: 0.5, left: 0, right: 1, children: redView },
{ top: 0.75, bottom: 1, left: 0, right: 1, children: blueView },
{ top: 0.5, bottom: 0.75, left: 0, right: 1, children: greenView },
]
},
{
validAfterWHRatio: 1 / 0.62,
validBeforeWHRatio: 999, // infinity
views: [
{ top: 0, bottom: 1, left: 0, right: 0.5, children: redView },
{ top: 0.5, bottom: 1, left: 0.5, right: 1, children: blueView },
{ top: 0, bottom: 0.5, left: 0.5, right: 1, children: greenView },
]
},
{
defaultFlag: true,
views: [
{ top: 0.16, bottom: 0.84, left: 0.16, right: 0.5, children: redView },
{ top: 0.50, bottom: 0.84, left: 0.5, right: 0.84, children: blueView },
{ top: 0.16, bottom: 0.50, left: 0.5, right: 0.84, children: greenView },
]
},
];
useEffect(() => {
let nextRerender = rerender + 1;
setRerender(nextRerender);
}, []); // when we want to re-render
return <ReactNativeAnimatedLayout
layouts={layouts}
rerender={rerender}
/>;
}
Note: This approach may be useful especially for PWAs. If you need to use scroll bars, they should be inside one of the views, probably "the" content view.