Understanding CSS triangles and rhombus interactions in React native with absolute positions - css

I have a complex structure diagram with Rhombuses and triangles. I was able to create the structure using CSS tricks and absolute position property.
I want each rhombus and triangle to be clickable but the behavior is not as expected given how CSS triangles work with the borders.
An output image with some markers
In this image we have a rhombus and two triangles,
What I want:
On clicking at A,B,E - rhombus is clicked
On clicking C,D - first triangle (numbered 2 in picture) is clicked.
What happens instead:
On clicking A, nothing happens (I suspect the absolute and transform property here)
On clicking B, first triangle (numbered 2 in picture) is clicked. It is because of the transparent right border of first triangle.
On clicking C, second triangle (numbered 3 in picture) is clicked. It is because of the transparent top border of second triangle.
On clicking D, first triangle is clicked which is the expected behaviour.
On clicking E, rhombus is clicked which is the expected behaviour.
How can I make such complex interactive patterns with series of such shapes? Shall I consider using images/SVGs instead of CSS shapes? I also need to add multiple dynamic count of numbers/text/emojis in each shape.
Here's a simplified snippet of code:
import React from 'react';
import {
View,
TouchableOpacity,
StyleSheet,
SafeAreaView,
} from 'react-native';
const MyComplexDiagram = ({navigation, route}) => {
const clickShape = text => {
console.log('clicked: ', text);
};
return (
<SafeAreaView style={{flex: 1}}>
<View>
<TouchableOpacity onPress={() => clickShape("rhombus)}>
<View style={[styles.rhombus]} />
</TouchableOpacity>
<TouchableOpacity onPress={() => clickShape("first triangle")}>
<View style={[styles.firstTriangle]} />
</TouchableOpacity>
<TouchableOpacity onPress={() => clickShape("second triangle")}>
<View style={[styles.secondTriangle]} />
</TouchableOpacity>
</SafeAreaView>
);
};
export default MyComplexShape;
const styles = StyleSheet.create({
rhombus: {
position: 'absolute',
top: 50,
left: 100,
width: 100,
height: 100,
backgroundColor: 'green',
transform: [{rotate: '45deg'}],
},
firstTriangle: {
position: 'absolute',
top: 0,
left: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 50,
borderRightWidth: 50,
borderTopWidth: 50,
borderLeftColor: 'transparent',
borderRightColor: 'transparent',
borderTopColor: 'red',
}),
secondTriangle: {
position: 'absolute',
top: 0,
left: 0,
backgroundColor: 'transparent',
borderStyle: 'solid',
borderLeftWidth: 50 * Math.sqrt(2),
borderTopWidth: 50 * Math.sqrt(2),
borderBottomWidth: 50 * Math.sqrt(2),
borderLeftColor: 'black',
borderBottomColor: 'transparent',
borderTopColor: 'transparent',
}
});

Im not sure if the same thing happens in a browser context, but the issue with the css trick is that although the view appears to be a triangle, it is actually still a rectangle. Giving the triangles a non-transparent background color will demonstrate this. The middle triangle is intercepting the first triangle presses because its hidden rectangle entirely overlaps the first triangle. (Here's a demo showing that). You can overcome this by the other TouchableOpacities a higher zIndex than the middle one:
import React, { useMemo, useEffect, useRef, useState } from 'react';
import {
View,
TouchableOpacity,
StyleSheet,
SafeAreaView,
useWindowDimensions,
Button,
Text
} from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withSequence,
withRepeat,
cancelAnimation,
withDelay,
} from 'react-native-reanimated';
const getHypotenuse = (side1, side2) => {
if (!side2) side2 = side1;
return Math.sqrt(side1 ** 2 + side2 ** 2);
};
const startRotation = 270;
const MyComplexDiagram = ({ navigation, route }) => {
const [animationIsRunning, setAnimationIsRunning] = useState(false);
const rotation = useSharedValue(startRotation);
const backgroundColor = useSharedValue('rgba(0,0,0,0)');
const { width, height } = useWindowDimensions();
const rotationStyle = useAnimatedStyle(() => {
return {
transform: [{ rotate: `${rotation.value}deg` }],
};
});
const colorStyle = useAnimatedStyle(() => {
return {
backgroundColor: backgroundColor.value,
};
});
const { rhombusStyle, middleTriangleStyle, leftTriangleStyle } =
useMemo(() => {
const remainingWidth =
width - styles.container.padding - styles.shapeContainer.padding;
const rhombusSize = remainingWidth * 0.3;
const rhombusHypotenuse = getHypotenuse(rhombusSize);
const rhombusLeft = remainingWidth - rhombusHypotenuse;
const rhombusTop = 25;
const middleLeft = rhombusLeft - rhombusSize;
const leftLeft = middleLeft;
return {
rhombusStyle: {
width: rhombusSize,
height: rhombusSize,
left: rhombusLeft,
top: rhombusTop,
},
middleTriangleStyle: {
borderWidth: rhombusSize * 0.8,
borderLeftWidth: rhombusSize * 0.8,
left: rhombusLeft - rhombusSize * 1.1,
top: 10,
},
leftTriangleStyle: {
borderWidth: rhombusSize,
borderBottom: rhombusSize,
left: rhombusLeft - rhombusHypotenuse * 1.7,
top: 0,
},
};
}, [width]);
const clickShape = (text) => {
console.log('clicked: ', text);
};
const toggleAnimation = () => {
if (animationIsRunning) {
backgroundColor.value = withTiming('rgba(0,0,0,0)');
} else {
backgroundColor.value = withRepeat(
withSequence(
withTiming('rgba(255,0,0,1)', { duration: 2000 }),
withTiming('rgba(0,0,0,0'),
withDelay(4000,withTiming('rgba(0,0,0,0'))
),
-1
);
}
setAnimationIsRunning((prev) => !prev);
};
return (
<SafeAreaView style={styles.container}>
<Button
title={`${animationIsRunning ? 'Hide' : 'Show'} Triangle Background`}
onPress={toggleAnimation}
/>
<Text style={{zIndex:5}}>{animationIsRunning ? 'Using zIndex to force black triangle to be on top of orange':'Default behavior without zIndex changes'}</Text>
<View style={styles.shapeContainer}>
<TouchableOpacity
style={animationIsRunning && { zIndex: 5 }}
onPress={() => clickShape('leftTriangle')}>
<Animated.View
style={[styles.leftTriangle, leftTriangleStyle, colorStyle]}
/>
</TouchableOpacity>
<TouchableOpacity
style={animationIsRunning && { zIndex: 4 }}
onPress={() => clickShape('middleTriangle')}>
<Animated.View
style={[
styles.middleTriangle,
middleTriangleStyle,
rotationStyle,
colorStyle,
]}
/>
</TouchableOpacity>
<TouchableOpacity
style={animationIsRunning && { zIndex: 5 }}
onPress={() => clickShape('rhombus')}>
<Animated.View style={[styles.rhombus, rhombusStyle]} />
</TouchableOpacity>
</View>
</SafeAreaView>
);
};
export default MyComplexDiagram;
const styles = StyleSheet.create({
container: {
flex: 1,
// alignItems:'center',
justifyContent: 'center',
padding: 8,
},
shapeContainer: {
padding: 10,
},
rhombus: {
transform: [{ rotate: '45deg' }],
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
backgroundColor: 'green',
},
middleTriangle: {
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
left: 0,
width: 0,
height: 0,
borderColor: 'transparent',
borderRightColor: 'orange',
},
leftTriangle: {
position: 'absolute',
transform: [{ rotate: '225deg' }],
top: 0,
bottom: 0,
right: 0,
left: 0,
width: 0,
height: 0,
borderColor: 'transparent',
borderLeftColor: 'black',
},
});

Related

How to change the button color which consists of divs on press and back?

I have a react component which must be a custom button and contains from 2 divs and 1 . My goal here is to implement a function that changes the color from white to orange after onclick and becomes white again after I click somewhere else on the screen.
This is my first react project and I tried multiple approaches already and it still doesn´t work even if the code compiles.. Any suggestions here guys?
import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';
export default function PointMark() {
return (
<TouchableOpacity style={styles.touchPoint}>
<div style={styles.outerCircle}>
<div style={styles.innerCircle}></div>
</div>
</TouchableOpacity>
);
}
// Styles
const styles = StyleSheet.create({
touchPoint: {
width: 68,
height: 68,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 50,
border: "3px solid #ffffff",
},
outerCircle: {
width: 42,
height: 42,
borderRadius: 50,
border: "3px solid #ffffff",
position: 'relative',
},
innerCircle: {
width: 22,
height: 22,
borderRadius: 50,
backgroundColor: '#ffffff',
position: 'absolute',
top: 10,
left: 10,
}
});
How about using something like react-native-webhooks?
For your case, perhaps this:
import React from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';
import { useActive } from 'react-native-web-hooks';
export default function PointMark() {
const ref = React.useRef(null);
const isActive = useActive(ref);
return (
<TouchableOpacity
ref={ref}
style={{
...styles.touchPoint,
...(isActive && styles.touchPointActive)
}}>
<div style={styles.outerCircle}>
<div
style={{
...styles.innerCircle
...(isActive && styles.innerCircleActive)
}}
/>
</div>
</TouchableOpacity>
);
}
// Styles
const styles = StyleSheet.create({
touchPoint: {
width: 68,
height: 68,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 50,
border: "3px solid #ffffff",
},
touchPointActive: {
border: "3px solid #ff8000",
},
outerCircle: {
width: 42,
height: 42,
borderRadius: 50,
border: 'inherit',
position: 'relative',
},
innerCircle: {
width: 22,
height: 22,
borderRadius: 50,
backgroundColor: '#ffffff',
position: 'absolute',
top: 10,
left: 10,
},
innerCirlceActive: {
backgroundColor: '#ff8000',
}
});
A few things to note:
I set the border property on the outerCircle to inherit, that way you don't need to define the border again
div elements that are empty may be self-closing e.g. <div />
Here's the example they use on their NPM page:
import { useRef } from 'react';
import { StyleSheet, Linking, Text, Platform } from 'react-native';
import { useHover, useFocus, useActive } from 'react-native-web-hooks';
function Link({ children, href = '#' }) {
const ref = useRef(null);
const isHovered = useHover(ref);
const isFocused = useFocus(ref);
const isActive = useActive(ref);
return (
<Text
accessibilityRole="link"
href={href}
draggable={false}
onPress={() => Linking.openURL(href)}
tabIndex={0}
ref={ref}
style={[
styles.text,
isHovered && styles.hover,
isFocused && styles.focused,
isActive && styles.active,
]}>
{children}
</Text>
);
}
const styles = StyleSheet.create({
text: {
...Platform.select({
web: {
cursor: 'pointer',
outlineStyle: 'none',
borderBottomWidth: 1,
borderBottomColor: 'transparent',
transitionDuration: '200ms',
},
default: {},
}),
},
active: {
color: 'blue',
borderBottomColor: 'blue',
opacity: 1.0,
},
hover: {
opacity: 0.6,
},
focused: {
borderBottomColor: 'black',
},
});

React Native - Pressable is not clickable on Android

What I'm building:
I'm creating a card that will render with a question mark at top of it and when the user press it, the card will flip with animation and show some information.
Code:
From a code perspective it looks like this:
import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
import { FontAwesome } from '#expo/vector-icons';
import { GlobalStyles } from '../../constants/styles';
import { useTranslation } from 'react-i18next';
export default Card = ({ isSpy, guessingItem, onPress }) => {
const { t } = useTranslation();
let currentValue = 0;
const animatedValue = new Animated.Value(0);
animatedValue.addListener((v) => (currentValue = v.value));
const frontInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
});
const backInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg'],
});
const frontAnimatedStyles = {
transform: [{ rotateY: frontInterpolate }],
};
const backAnimatedStyles = {
transform: [{ rotateY: backInterpolate }],
};
const flipCard = () => {
console.log(currentValue);
if (currentValue >= 90) {
Animated.spring(animatedValue, {
toValue: 0,
tension: 10,
friction: 8,
useNativeDriver: false,
}).start();
setTimeout(() => onPress(), 600);
} else {
Animated.spring(animatedValue, {
toValue: 180,
tension: 10,
friction: 8,
useNativeDriver: false,
}).start();
}
};
return (
<View style={styles.constainer}>
<Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
<View style={GlobalStyles.styles.flexbox}>
<Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
<FontAwesome name="question" size={160} />
</Animated.View>
</View>
</Pressable>
<Pressable onPress={flipCard}>
<View style={GlobalStyles.styles.flexbox}>
<Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
<View style={styles.constainer}>
{!isSpy && (
<>
<FontAwesome name={guessingItem.section.icon} size={60} />
<Text style={styles.itemName}>{guessingItem.name}</Text>
</>
)}
{isSpy && (
<>
<FontAwesome name="user-secret" size={60} />
<Text style={styles.placeName}>{t('youAreSpy')}</Text>
</>
)}
</View>
</Animated.View>
</View>
</Pressable>
</View>
);
};
const styles = StyleSheet.create({
constainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
innerContainer: {
height: 300,
width: 230,
marginVertical: 50,
padding: 60,
borderWidth: 6,
borderColor: GlobalStyles.colors.primary50,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
backfaceVisibility: 'hidden',
},
innerContainerBack: {
position: 'absolute',
right: -115,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
},
itemName: {
marginTop: 20,
fontSize: 18,
alignItems: 'center',
color: GlobalStyles.colors.primary50,
},
pressed: {},
});
What is an issue:
On-screen below you can see a pressable element with red background.
If I click on any part of this red part, the card would flip. If I test it on Android, clicking on anything inside the white border would not trigger a flip. Only clicking outside of that white border but still inside the red background would trigger a flip.
The question is: Why it behaves differently when I am using basic react-native elements? How can I fix that so clicking would always work for the inside click?
I tested this component on my side and it works well. I think there is no problem with the Card component. Please check the parent component of the Card.
I only changed "right" value from -115 to 0 in the style of innerContainerBack.
innerContainerBack: {
position: 'absolute',
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
}
And you'd better use only one Pressable Component and remove the View component in the Pressable. So my ultimate solution is
import { View, StyleSheet, Text, Pressable, Animated } from 'react-native';
import { FontAwesome } from '#expo/vector-icons';
import { GlobalStyles } from '../../constants/styles';
import { useTranslation } from 'react-i18next';
export default Card = ({ isSpy, guessingItem, onPress }) => {
const { t } = useTranslation();
let currentValue = 0;
const animatedValue = new Animated.Value(0);
animatedValue.addListener((v) => (currentValue = v.value));
const frontInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['0deg', '180deg'],
});
const backInterpolate = animatedValue.interpolate({
inputRange: [0, 180],
outputRange: ['180deg', '360deg'],
});
const frontAnimatedStyles = {
transform: [{ rotateY: frontInterpolate }],
};
const backAnimatedStyles = {
transform: [{ rotateY: backInterpolate }],
};
const flipCard = () => {
console.log(currentValue);
if (currentValue >= 90) {
Animated.spring(animatedValue, {
toValue: 0,
tension: 10,
friction: 8,
useNativeDriver: false,
}).start();
setTimeout(() => onPress(), 600);
} else {
Animated.spring(animatedValue, {
toValue: 180,
tension: 10,
friction: 8,
useNativeDriver: false,
}).start();
}
};
return (
<View style={styles.constainer}>
<Pressable onPress={flipCard} style={{ backgroundColor: 'red' }}>
<Animated.View style={[styles.innerContainer, frontAnimatedStyles]}>
<FontAwesome name="question" size={160} />
</Animated.View>
<Animated.View style={[backAnimatedStyles, styles.innerContainer, styles.innerContainerBack]}>
<View style={styles.constainer}>
{!isSpy && (
<>
<FontAwesome name={guessingItem.section.icon} size={60} />
<Text style={styles.itemName}>{guessingItem.name}</Text>
</>
)}
{isSpy && (
<>
<FontAwesome name="user-secret" size={60} />
<Text style={styles.placeName}>{t('youAreSpy')}</Text>
</>
)}
</View>
</Animated.View>
</Pressable>
</View>
);
};
const styles = StyleSheet.create({
constainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
innerContainer: {
height: 300,
width: 230,
marginVertical: 50,
padding: 60,
borderWidth: 6,
borderColor: GlobalStyles.colors.primary50,
borderRadius: 20,
justifyContent: 'center',
alignItems: 'center',
backfaceVisibility: 'hidden',
},
innerContainerBack: {
position: 'absolute',
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
},
itemName: {
marginTop: 20,
fontSize: 18,
alignItems: 'center',
color: GlobalStyles.colors.primary50,
},
pressed: {},
});

How to set height to an element by his background image height

Hi I have this component in my app, Right now the height of this ButonBase is hard coded.
I get an image from backend and I set that image as a backgroundImage for the ImageImagine component.
I need to make height of that ButonBase to bee dinamic so the background image in imageImagine can mentain original aspect ratio.
import React, { useState } from 'react';
import {
Box,
ButtonBase as MuiButtonBase,
IconButton,
Typography,
styled,
} from '#mui/material';
import Label from './Label';
import Favorite from './Favorite';
import MintingContextButton from './MintingContextButton';
import StatusLabel from 'src/components/StatusLabel';
import PublicLabel from 'src/components/PublicLabel';
import { IMAGINE_STATUS } from 'src/constants';
import { authDomain } from 'src/config';
const ButtonBase = styled(MuiButtonBase)(({ theme }) => ({
position: 'relative',
height: 342,
width: '100%',
borderRadius: 32,
[theme.breakpoints.down('xs')]: {
width: '100% !important', // Overrides inline-style
height: 326,
},
'&:hover': {
'& .imageTitle': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 1,
},
'& .imageBackdrop': {
opacity: 0.7,
},
'& .MuiIconButton-favorite': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 12,
},
'& .MuiButton-minting': {
opacity: 1,
transition: theme.transitions.create('opacity'),
zIndex: 12,
},
},
}));
const ContextContainer = styled('span')(() => ({
position: 'absolute',
width: '100%', // should on if real mint button
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
padding: 16,
zIndex: 1, // Remove if real mint button
}));
const StyledStatusLabel = styled(StatusLabel)(() => ({
position: 'absolute',
left: 16,
top: 16,
zIndex: 1,
}));
const MintIconButton = styled(IconButton)(() => ({
position: 'absolute',
left: 16,
top: 16,
zIndex: 1,
background: 'rgba(0, 0, 0, 0.7)',
'&:hover': {
background: 'rgba(128, 58, 254, 0.9)',
},
}));
const ImageTitle = styled('span')(({ theme }) => ({
opacity: 0,
textAlign: 'left',
// [theme.breakpoints.down('sm')]: {
// opacity: 1,
// zIndex: 1
// },
}));
const ImageBackdrop = styled('span')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: theme.palette.common.black,
opacity: 0,
transition: theme.transitions.create('opacity'),
borderRadius: 32,
// [theme.breakpoints.down('sm')]: {
// opacity: 0.7,
// },
}));
const ImageImagine = styled('span')(() => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%',
borderRadius: 32,
}));
const GalleryItem = ({
imagine,
imagine: {
id,
title,
status,
isMyFavorite,
public: publicStatus,
context: imgContext,
thumb_url: imgUrl,
count_favorites: countFavorites,
},
onOpen,
onClose,
...props
}) => {
const [isHoverFavorite, setHoverFavorite] = useState(false);
return (
<>
<ButtonBase
className="MuiButtonBase-gallery"
disableRipple
focusRipple
onClick={(event) => onOpen(event, id)}
>
<ContextContainer>
<Box display="flex" justifyContent="space-between">
<Box
display="flex"
visibility={status === IMAGINE_STATUS.COMPLETE && 'hidden'}
>
<StyledStatusLabel status={status} />
</Box>
{status === IMAGINE_STATUS.COMPLETE && (
<React.Fragment>
<Favorite
imagineId={id}
isMyFavorite={isMyFavorite}
isHoverFavorite={isHoverFavorite}
isHidden
onHoverFavorite={setHoverFavorite}
count={countFavorites}
/>
{/* <MintingContextButton imagine={imagine} /> */}
</React.Fragment>
)}
</Box>
<ImageTitle className="imageTitle">
<Typography color="textPrimary" variant="subtitle1" noWrap>
{title}
</Typography>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
>
{imgContext && <Label>{imgContext}</Label>}
<PublicLabel publicStatus={publicStatus} />
</Box>
</ImageTitle>
</ContextContainer>
<ImageImagine
style={{
backgroundImage: `url(${
imgUrl ?? `${authDomain}/images/glitchcat.gif`
})`,
}}
/>
<ImageBackdrop className="imageBackdrop" />
</ButtonBase>
</>
);
};
export default GalleryItem;
Had you tried to set the height of the ButtonBase into height: auto and the min-height into min-height: fit-content?

react native AutoComplete dropdown in ios isn't scrollable

I want to use autoComplete input in react-native, the best option i found was
react-native-autocomplete-input, my problem is that i can't scroll my drop down list in IOS but in android i can.
I tried this solution and it's doesn't help :(
https://github.com/mrlaessig/react-native-autocomplete-input/issues/57#issuecomment-332723713
I checked a lot of suggestions and nothing doesn't help, i will be glad if someone that used it will help, thank you!
return (
<View style={styles.container}>
{showAddChar && (
<Text style={styles.addChar} onPress={this.onChoosenItem}>
+
</Text>
)}
<Autocomplete
data={data}
defaultValue={query}
onChangeText={(text: string) =>
this.setState({ query: text, hideResult: false })
}
keyExtractor={index => index['id'].toString()}
inputContainerStyle={styles.inputContainerStyle}
listContainerStyle={{}}
listStyle={styles.listStyle}
style={{ fontSize: 18 }}
hideResults={hideResult}
placeholder={placeholder}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.item}
onPress={() => this.onChoosenItem(item)}
>
<Text>{item['name']}</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
left: 0,
position: 'absolute',
right: 0,
top: 0
},
addChar: {
zIndex: 1,
position: 'absolute',
fontSize: 30,
right: 0,
top: 0
},
inputContainerStyle: {
borderColor: 'rgb(70, 48, 235)',
borderBottomWidth: 3,
borderTopWidth: 0,
borderRightWidth: 0,
borderLeftWidth: 0
},
// listContainerStyle: {height:filteredMembers.length * 70},
listStyle: {
borderColor: 'grey',
margin: 0,
overflow: 'scroll',
maxHeight: 200
},
item: {
fontSize: 16,
borderBottomColor: 'grey',
borderBottomWidth: 2,
padding: 12
}
});
enter image description here

Can't align button to the bottom left of the screen - React Native

I would like to position my button to the bottom left of the screen. I have tried using bottom: 0 and left: 0, but that did not do anything. I researched a bit and discovered that I need to set the position: 'absolute'. However, when I do this, my button disappears completely.
How can I position by the button to the bottom left of the screen?
Here is my code:
import React, { Component } from 'react';
import { Button,Alert, TouchableOpacity,Image } from 'react-native'
import {
AppRegistry,
StyleSheet,
Text,
View,
} from 'react-native';
class Project extends Component {
render() {
return (
<View style={{backgroundColor: '#375D81', flex: 1}}>
<View style = {styles.container}>
<TouchableOpacity style = {styles.buttonText} onPress={() => { Alert.alert('You tapped the button!')}}>
<Text>
Button
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
main: {
backgroundColor: 'blue'
},
container: {
alignItems: 'center',
},
buttonText: {
borderWidth: 1,
padding: 25,
borderColor: 'black',
backgroundColor: '#C4D7ED',
borderRadius: 15,
bottom: 0,
left: 0,
width: 100,
height: 100,
position: 'absolute'
}
});
AppRegistry.registerComponent('Project', () => Project);
You've forgotten to flex your container. Add: flex: 1 in there and you're all good:
container: {
alignItems: 'center',
flex: 1
},
You just need to set a width and height to your button then:
justifyContent: 'flex-end'
That's it
App.js
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import styles from './styles';
class Example extends React.Component {
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button}>
<Text style={styles.text}>Button</Text>
</TouchableOpacity>
</View>
);
}
}
export default Example;
styles.js
export default {
container: {
flex: 1,
justifyContent: 'flex-end',
},
button: {
justifyContent: 'center',
textAlign: 'center',
width: 70,
height: 40,
backgroundColor: 'green',
borderRadius: 10,
margin: 20,
},
text: {
color: 'white',
textAlign: 'center',
margin: 10,
},
};
You will get something like this:
See Expo Snack: snack.expo.io/#abranhe/button-left
React-native elements are "rendered" from the top left when defining their position absolutely. This means when you are defining bottom: 0 and left: 0, this is rendering the item where you want horizontally, but vertically it will be off the screen. switch to
position 'absolute',
bottom: 100,
left: 0,
This should be what you are looking for

Resources